TypeScript enum ke array objek

100

Saya memiliki enum yang didefinisikan dengan cara ini:

export enum GoalProgressMeasurements {
    Percentage = 1,
    Numeric_Target = 2,
    Completed_Tasks = 3,
    Average_Milestone_Progress = 4,
    Not_Measured = 5
}

Namun, saya ingin itu direpresentasikan sebagai larik / daftar objek dari API kami seperti di bawah ini:

[{id: 1, name: 'Percentage'}, 
 {id: 2, name: 'Numeric Target'},
 {id: 3, name: 'Completed Tasks'},
 {id: 4, name: 'Average Milestone Progress'},
 {id: 5, name: 'Not Measured'}]

Apakah ada cara mudah dan asli untuk melakukan ini atau apakah saya harus membangun fungsi yang mentransmisikan enum ke int dan string, dan membangun objek ke dalam array?

AnimaSola
sumber
Enum adalah objek nyata yang ada saat runtime. Jadi Anda dapat membalikkan pemetaan dengan melakukan sesuatu seperti ini: GoalProgressMeasurements[GoalProgressMeasurements.Completed_Tasks]untuk mendapatkan nama enum. Saya tidak tahu apakah itu membantu.
Diullei
Dapatkah Anda memberikan deskripsi yang lebih baik untuk "dari API kami", mungkin memberikan contoh penggunaan
gilamran

Jawaban:

47

Sedikit rumit adalah TypeScript akan 'menggandakan' peta enum dalam objek yang dipancarkan, sehingga dapat diakses baik dengan kunci dan nilai.

enum MyEnum {
    Part1 = 0,
    Part2 = 1
}

akan dipancarkan sebagai

{
   Part1: 0,
   Part2: 1,
   0: 'Part1',
   1: 'Part2'
}

Jadi Anda harus memfilter objek terlebih dahulu sebelum memetakan. Jadi solusi @Diullei punya jawaban yang tepat. Inilah implementasi saya:

// Helper
const StringIsNumber = value => isNaN(Number(value)) === false;

// Turn enum into array
function ToArray(enumme) {
    return Object.keys(enumme)
        .filter(StringIsNumber)
        .map(key => enumme[key]);
}

Gunakan seperti ini:

export enum GoalProgressMeasurements {
    Percentage,
    Numeric_Target,
    Completed_Tasks,
    Average_Milestone_Progress,
    Not_Measured
}

console.log(ToArray(GoalProgressMeasurements));
pengguna8363
sumber
1
mmm kalo enum MyEnum { Part1 = 0, Part2 = 1 }berubah jadi { Part1: 0, Part2: 1, 0: 'Part1', 1: 'Part2' } lalu, kenapa kalo console.log(Object.values(MyEnum))cuma print 0,1 saja?
Juan José Ramírez
@ JuanJoséRamírez di mana Anda melihat itu? Bagi saya Object.values(MyEnum)mengevaluasi ke["Part1", "Part2", 0, 1]
user8363
Saya baru saja mencetak console.log(Object.values(MyEnum))di komponen saya. Saya menggunakan sudut, tidak yakin apakah itu terkait. Saya tidak begitu berpengalaman dalam TypeScript
Juan José Ramírez
dapatkah perilaku berubah melalui versi TS yang berbeda?
Juan José Ramírez
3
Saya telah memeriksa docs typescriptlang.org/docs/handbook/release-notes/… dan tampaknya enum string memiliki perilaku yang berbeda. Mereka tidak mendapatkan pemetaan terbalik sama sekali. Dalam kode saya, saya menggunakan string enum, bukan string dalam contoh ini.
Juan José Ramírez
40

Jika Anda menggunakan ES6

Ini akan memberi Anda nilai array dari enum yang diberikan .

enum Colors {
  WHITE = 0,
  BLACK = 1,
  BLUE = 3
}

const colorValueArray = Object.values(Colors); //[ 'WHITE', 'BLACK', 'BLUE', 0, 1, 3 ]

Anda akan colorValueArrayseperti ini [ 'WHITE', 'BLACK', 'BLUE', 0, 1, 3 ]. Semua kunci akan berada di paruh pertama larik dan semua nilai di paruh kedua.

Jai Prak
sumber
9
Ingatlah bahwa ini akan menghasilkan duplikat. Nilai string dan nilai numerik untuk setiap elemen, itu adalah tipe (string | YourEnumType)[]yang mungkin tidak Anda inginkan dalam setiap kasus.
Christian Ivicevic
apakah dijamin bahwa paruh pertama akan menjadi kunci dan paruh kedua akan menjadi nilainya? ada referensi?
Neekey
36

Enum adalah objek nyata yang ada saat runtime. Jadi Anda dapat membalikkan pemetaan dengan melakukan sesuatu seperti ini:

let value = GoalProgressMeasurements.Not_Measured;
console.log(GoalProgressMeasurements[value]);
// => Not_Measured

Berdasarkan itu Anda dapat menggunakan kode berikut:

export enum GoalProgressMeasurements {
    Percentage = 1,
    Numeric_Target = 2,
    Completed_Tasks = 3,
    Average_Milestone_Progress = 4,
    Not_Measured = 5
}

let map: {id: number; name: string}[] = [];

for(var n in GoalProgressMeasurements) {
    if (typeof GoalProgressMeasurements[n] === 'number') {
        map.push({id: <any>GoalProgressMeasurements[n], name: n});
    }
}

console.log(map);

Referensi: https://www.typescriptlang.org/docs/handbook/enums.html

Diullei
sumber
3
Anda tidak perlu menulis default = 2sampai = 5- Apa pun setelahnya = 1+1 secara otomatis.
sebilasse
8
Mungkin tidak perlu, tetapi lebih ekspresif. Yang membuatnya IMHO lebih baik.
SubliemeSiem
5
hanya catatan ini tidak berfungsi untuk enum nilai string
Bashar Ali Labadi
21

Solusi Mudah. Anda dapat menggunakan fungsi berikut untuk mengubah Enum Anda menjadi sebuah array objek.

 buildGoalProgressMeasurementsArray(): Object[] {

    return Object.keys(GoalProgressMeasurements)
              .map(key => ({ id: GoalProgressMeasurements[key], name: key }))
 }

Jika Anda perlu menghapus garis bawah itu, kita bisa menggunakan regex sebagai berikut:

buildGoalProgressMeasurementsArray(): Object[] {

    return Object.keys(GoalProgressMeasurements)
              .map(key => ({ id: GoalProgressMeasurements[key], name: key.replace(/_/g, ' ') }))
 }
Manoj Shrestha
sumber
7
Anda harus memfilter kunci jenis nomor Object.keys(GoalProgressMeasurements) .filter(key => typeof GoalProgressMeasurements[key] === 'number') .map(key => ({ id: GoalProgressMeasurements[key], name: key }))
Salvador Rubio Martinez
12

saya menggunakan

Object.entries(GoalProgressMeasurement).filter(e => !isNaN(e[0]as any)).map(e => ({ name: e[1], id: e[0] }));

Garis 1 sederhana yang melakukan pekerjaan itu.

Itu melakukan pekerjaan dalam 3 langkah sederhana
- Memuat kombinasi kunci & nilai menggunakan Object.entries.
- Memfilter nomor non (karena skrip ketikan menghasilkan nilai untuk pencarian terbalik).
- Kemudian kita memetakannya ke objek array yang kita suka.

CMS
sumber
Tidak kompatibel dengan IE dengan front-end (seharusnya tidak mendukung yaitu adalah jawaban yang bagus ... tapi saya rasa pelanggan). Berharap babel yang melakukannya, tetapi tetap berpegang pada pendekatan lain karena saya belum memverifikasinya
TamusJRoyce
Ini tidak berfungsi untuk enum string.
Tony Smith
9
class EnumHelpers {

    static getNamesAndValues<T extends number>(e: any) {
        return EnumHelpers.getNames(e).map(n => ({ name: n, value: e[n] as T }));
    }

    static getNames(e: any) {
        return EnumHelpers.getObjValues(e).filter(v => typeof v === 'string') as string[];
    }

    static getValues<T extends number>(e: any) {
        return EnumHelpers.getObjValues(e).filter(v => typeof v === 'number') as T[];
    }

    static getSelectList<T extends number, U>(e: any, stringConverter: (arg: U) => string) {
        const selectList = new Map<T, string>();
        this.getValues(e).forEach(val => selectList.set(val as T, stringConverter(val as unknown as U)));
        return selectList;
    }

    static getSelectListAsArray<T extends number, U>(e: any, stringConverter: (arg: U) => string) {
        return Array.from(this.getSelectList(e, stringConverter), value => ({ value: value[0] as T, presentation: value[1] }));
    }

    private static getObjValues(e: any): (number | string)[] {
        return Object.keys(e).map(k => e[k]);
    }
}
Liam Kernighan
sumber
1
Terima kasih untuk para pembantu ini. Sangat berguna.
pengguna906573
8

Ini akan memberi Anda array nilai enum:

 Object.values(myEnum);
Gaurav Panwar
sumber
Mengapa ini bukan jawaban teratas? Terima kasih!
Paulo Queiroz
@PauloQueiroz Ini akan naik secara otomatis jika upvote meningkat. Terima kasih!
Gaurav Panwar
Karena tidak memberikan hasil yang benar, periksa stackoverflow.com/a/57266281/3548345
walox
4

Pertama kita mendapatkan array kunci untuk enum ini. Kemudian, menggunakan fungsi map (), kami mengonversi data ke format yang diinginkan. id diperoleh dari kunci, nama diperoleh dari enum dengan kunci yang sama.

const converted = Object.keys(GoalProgressMeasurements).map(key => {
        return {
            id: GoalProgressMeasurements[key],
            name: key,
        };
    });
VaCool
sumber
2
Selamat datang di stackoverflow. Saat menjawab pertanyaan, sebaiknya jelaskan apa yang dilakukan cuplikan kode Anda. Untuk info lebih lanjut, lihat di sini: Bagaimana Menjawab
Djensen
Harap pertimbangkan untuk menambahkan beberapa penjelasan atau detail ke jawaban Anda. Meskipun mungkin menjawab pertanyaan, hanya menambahkan sepotong kode sebagai jawaban, tidak membantu OP atau anggota komunitas di masa mendatang memahami masalah atau solusi yang diusulkan.
Maxim
2

Saya tidak menyukai salah satu jawaban di atas karena tidak satupun dari mereka menangani campuran string / angka dengan benar yang dapat menjadi nilai dalam enum TypeScript.

Fungsi berikut mengikuti semantik enum TypeScript untuk memberikan Peta kunci yang tepat ke nilai. Dari sana, mendapatkan larik objek atau hanya kunci atau hanya nilainya itu sepele.

/**
 * Converts the given enum to a map of the keys to the values.
 * @param enumeration The enum to convert to a map.
 */
function enumToMap(enumeration: any): Map<string, string | number> {
  const map = new Map<string, string | number>();
  for (let key in enumeration) {
      //TypeScript does not allow enum keys to be numeric
      if (!isNaN(Number(key))) continue;

      const val = enumeration[key] as string | number;

      //TypeScript does not allow enum value to be null or undefined
      if (val !== undefined && val !== null)
          map.set(key, val);
  }

  return map;
}

Contoh Penggunaan:

enum Dog {
    Rover = 1,
    Lassie = "Collie",
    Fido = 3,
    Cody = "Mutt",
}

let map = enumToMap(Dog); //Map of keys to values

lets objs = Array.from(map.entries()).map(m => ({id: m[1], name: m[0]})); //Objects as asked for in OP
let entries = Array.from(map.entries()); //Array of each entry
let keys = Array.from(map.keys()); //An array of keys
let values = Array.from(map.values()); //An array of values

Saya juga akan menunjukkan bahwa OP memikirkan enum mundur. "Kunci" dalam enum secara teknis berada di sisi kiri dan nilainya ada di sisi kanan. TypeScript memungkinkan Anda untuk mengulang nilai pada RHS sebanyak yang Anda inginkan.

MgSam
sumber
1

enum GoalProgressMeasurements {
    Percentage = 1,
    Numeric_Target = 2,
    Completed_Tasks = 3,
    Average_Milestone_Progress = 4,
    Not_Measured = 5
}
    
const array = []
    
for (const [key, value] of Object.entries(GoalProgressMeasurements)) {
    if (!Number.isNaN(Number(key))) {
        continue;
    }

    array.push({ id: value, name: key.replace('_', '') });
}

console.log(array);

nico
sumber
Harap letakkan jawaban Anda selalu dalam konteks, bukan hanya menempelkan kode. Lihat di sini untuk lebih jelasnya.
gehbiszumeis
0

Anda dapat melakukannya dengan cara ini:

export enum GoalProgressMeasurements {
    Percentage = 1,
    Numeric_Target = 2,
    Completed_Tasks = 3,
    Average_Milestone_Progress = 4,
    Not_Measured = 5
}

export class GoalProgressMeasurement {
    constructor(public goalProgressMeasurement: GoalProgressMeasurements, public name: string) {
    }
}

export var goalProgressMeasurements: { [key: number]: GoalProgressMeasurement } = {
    1: new GoalProgressMeasurement(GoalProgressMeasurements.Percentage, "Percentage"),
    2: new GoalProgressMeasurement(GoalProgressMeasurements.Numeric_Target, "Numeric Target"),
    3: new GoalProgressMeasurement(GoalProgressMeasurements.Completed_Tasks, "Completed Tasks"),
    4: new GoalProgressMeasurement(GoalProgressMeasurements.Average_Milestone_Progress, "Average Milestone Progress"),
    5: new GoalProgressMeasurement(GoalProgressMeasurements.Not_Measured, "Not Measured"),
}

Dan Anda bisa menggunakannya seperti ini:

var gpm: GoalProgressMeasurement = goalProgressMeasurements[GoalProgressMeasurements.Percentage];
var gpmName: string = gpm.name;

var myProgressId: number = 1; // the value can come out of drop down selected value or from back-end , so you can imagine the way of using
var gpm2: GoalProgressMeasurement = goalProgressMeasurements[myProgressId];
var gpmName: string = gpm.name;

Anda dapat memperluas GoalProgressMeasurement dengan properti tambahan dari objek yang Anda butuhkan. Saya menggunakan pendekatan ini untuk setiap enumerasi yang seharusnya menjadi objek yang mengandung lebih dari nilai.

Boban Stojanovski
sumber
0

Karena enum dengan nilai String berbeda dari yang memiliki nilai angka, lebih baik memfilter nonNumber dari solusi @ user8363.

Berikut adalah bagaimana Anda bisa mendapatkan nilai dari enum baik string, number of mixed:

    //Helper
    export const StringIsNotNumber = value => isNaN(Number(value)) === true;
    
    // Turn enum into array
    export function enumToArray(enumme) {
      return Object.keys(enumme)
       .filter(StringIsNotNumber)
       .map(key => enumme[key]);
    }

Islem Penywis
sumber
0

Saya terkejut dalam utas TypeScript tidak ada yang memberikan fungsi TypeScript yang valid dengan pengetikan yang didukung. Berikut variasi solusi @ user8363:

const isStringNumber = (value: string) => isNaN(Number(value)) === false;

function enumToArray<T extends {}>(givenEnum: T) {
  return (Object.keys(givenEnum).filter(isStringNumber) as (keyof T)[]).map(
    (key) => givenEnum[key]
  );
}
Daniel Kmak
sumber
0

Ada solusi sederhana, Jadi ketika Anda menjalankannya Object.keys(Enum)akan memberi Anda Array of Values ​​and Keys, di bagian pertama Nilai dan di kunci kedua, jadi mengapa kita tidak hanya mengembalikan bagian kedua, kode di bawah ini berfungsi untuk saya .

enum Enum {
   ONE,
   TWO,
   THREE,
   FOUR,
   FIVE,
   SIX,
   SEVEN
}
const keys = Object.keys(Enum); 
console.log(keys.slice(keys.length / 2));
دانیال مدیری
sumber
Penasaran mengapa ini tidak disukai? Saya memiliki ide yang sama. Akankah dikhawatirkan TypeScript akan berubah dan tidak lagi membuat enum dengan cara yang sama?
Tony Smith