TypeScript-Aufzählung zum Objektarray

100

Ich habe eine Aufzählung wie folgt definiert:

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

Ich möchte jedoch, dass es wie folgt als Objektarray / -liste aus unserer API dargestellt wird:

[{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'}]

Gibt es eine einfache und native Möglichkeit, dies zu tun, oder muss ich eine Funktion erstellen, die die Aufzählung sowohl in ein int als auch in einen String umwandelt, und die Objekte in ein Array einbauen?

AnimaSola
quelle
Aufzählungen sind reale Objekte, die zur Laufzeit vorhanden sind. Sie können das Mapping also wie GoalProgressMeasurements[GoalProgressMeasurements.Completed_Tasks]folgt umkehren: um den Namen der Aufzählung zu erhalten. Ich weiß nicht, ob das hilft.
Diullei
Können Sie "von unserer API" eine bessere Beschreibung geben, vielleicht ein Beispiel für die Verwendung
Gilamran

Antworten:

47

Ein kniffliges Problem ist, dass TypeScript die Aufzählung im ausgegebenen Objekt "doppelt" abbildet, sodass sowohl über den Schlüssel als auch über den Wert darauf zugegriffen werden kann.

enum MyEnum {
    Part1 = 0,
    Part2 = 1
}

wird ausgegeben als

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

Sie sollten das Objekt also zuerst filtern, bevor Sie es zuordnen. Die Lösung von @Diullei hat also die richtige Antwort. Hier ist meine Implementierung:

// 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]);
}

Verwenden Sie es so:

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

console.log(ToArray(GoalProgressMeasurements));
user8363
quelle
1
mmm wenn enum MyEnum { Part1 = 0, Part2 = 1 }wird { Part1: 0, Part2: 1, 0: 'Part1', 1: 'Part2' } dann, warum, wenn Sie console.log(Object.values(MyEnum))nur 0,1 drucken?
Juan José Ramírez
@ JuanJoséRamírez wo siehst du das? Für mich Object.values(MyEnum)bewertet zu["Part1", "Part2", 0, 1]
user8363
Ich habe gerade console.log(Object.values(MyEnum))in meiner Komponente gedruckt . Ich benutze eckig, nicht sicher, ob das verwandt ist. Ich bin nicht so erfahren in TypeScript
Juan José Ramírez
Könnte sich das Verhalten durch verschiedene TS-Versionen ändern?
Juan José Ramírez
3
Ich habe überprüft die Dokumentation typescriptlang.org/docs/handbook/release-notes/... und es scheint , String Aufzählungen ein anderes Verhalten haben. Sie erhalten überhaupt keine umgekehrte Zuordnung. In meinem Code habe ich eine Zeichenfolgenaufzählung verwendet, nicht die Zeichenfolge in diesem Beispiel.
Juan José Ramírez
40

Wenn Sie ES6 verwenden

Sie erhalten ein Wertearray der angegebenen Aufzählung .

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

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

Sie erhalten colorValueArraywie diese [ 'WHITE', 'BLACK', 'BLUE', 0, 1, 3 ]. Alle Schlüssel befinden sich in der ersten Hälfte des Arrays und alle Werte in der zweiten Hälfte.

Jai Prak
quelle
9
Beachten Sie, dass dies zu Duplikaten führt. Der Zeichenfolgenwert und der numerische Wert für jedes Element, dh ein Typ, (string | YourEnumType)[]der nicht in jedem Fall gewünscht wird.
Christian Ivicevic
Ist garantiert, dass die erste Hälfte die Schlüssel und die zweite Hälfte die Werte sind? irgendeine Referenz?
Neekey
36

Aufzählungen sind reale Objekte, die zur Laufzeit vorhanden sind. Sie können das Mapping also wie folgt umkehren:

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

Basierend darauf können Sie den folgenden Code verwenden:

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);

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

Diullei
quelle
3
Sie müssen die Standardeinstellungen = 2erst schreiben, wenn = 5- Alles danach = 1automatisch +1 ist.
Sebilasse
8
Vielleicht brauchen Sie das nicht, aber es ist ausdrucksvoller. Was es meiner Meinung nach besser macht.
SubliemeSiem
5
Nur eine Anmerkung, dies funktioniert nicht für String-Wert-Enums
Bashar Ali Labadi
21

Einfache Lösung. Mit der folgenden Funktion können Sie Ihre Aufzählung in ein Array von Objekten konvertieren.

 buildGoalProgressMeasurementsArray(): Object[] {

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

Wenn Sie diesen Unterstrich entfernen müssen, können Sie Regex wie folgt verwenden:

buildGoalProgressMeasurementsArray(): Object[] {

    return Object.keys(GoalProgressMeasurements)
              .map(key => ({ id: GoalProgressMeasurements[key], name: key.replace(/_/g, ' ') }))
 }
Manoj Shrestha
quelle
7
Sie sollten die Schlüssel der Typennummer filtern Object.keys(GoalProgressMeasurements) .filter(key => typeof GoalProgressMeasurements[key] === 'number') .map(key => ({ id: GoalProgressMeasurements[key], name: key }))
Salvador Rubio Martinez
12

ich benutze

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

Eine einfache 1 Zeile, die den Job erledigt.

Es erledigt die Arbeit in 3 einfachen Schritten
- Lädt die Kombination von Schlüsseln und Werten mit Object.entries.
- Filtert die Nicht-Zahlen heraus (da Typoskript die Werte für die umgekehrte Suche generiert).
- Dann ordnen wir es dem Array-Objekt zu, das wir mögen.

CMS
quelle
Nicht kompatibel mit IE mit Front-End (sollte nicht unterstützen, dh ist eine gute Antwort ... aber ich denke, Kunden). Die Hoffnung, dass Babel es
schafft
Dies funktioniert nicht für String-Aufzählungen.
Tony Smith vor
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
quelle
1
Vielen Dank für diese Helfer. Sehr hilfreich.
user906573
8

Dadurch erhalten Sie eine Reihe von Aufzählungswerten:

 Object.values(myEnum);
Gaurav Panwar
quelle
Warum ist das nicht die beste Antwort? Trotzdem danke!
Paulo Queiroz
@PauloQueiroz Es wird automatisch an die Spitze gesetzt, wenn die Upvote steigt. Vielen Dank!
Gaurav Panwar
Da es nicht das richtige Ergebnis liefert
walox
4

Zuerst erhalten wir eine Reihe von Schlüsseln für diese Aufzählung. Anschließend konvertieren wir mit der Funktion map () die Daten in das gewünschte Format. id wird vom Schlüssel erhalten, name wird von enum durch denselben Schlüssel erhalten.

const converted = Object.keys(GoalProgressMeasurements).map(key => {
        return {
            id: GoalProgressMeasurements[key],
            name: key,
        };
    });
VaCool
quelle
2
Willkommen bei stackoverflow. Wenn Sie Fragen beantworten, ist es eine gute Idee, zu erklären, was Ihr Code-Snippet tut. Weitere Informationen finden Sie hier: So beantworten Sie
Djensen
Bitte fügen Sie Ihrer Antwort einige Erklärungen oder Details hinzu. Während es die Frage beantworten könnte, hilft es OP oder zukünftigen Community-Mitgliedern nicht, das Problem oder die vorgeschlagene Lösung zu verstehen, wenn sie nur einen Code als Antwort hinzufügen.
Maxim
2

Ich mochte keine der obigen Antworten, weil keine von ihnen die Mischung von Zeichenfolgen / Zahlen, die Werte in TypeScript-Aufzählungen sein können, korrekt handhabt.

Die folgende Funktion folgt der Semantik von TypeScript-Enums, um eine korrekte Zuordnung von Schlüsseln zu Werten zu erhalten. Von dort aus ist es trivial, ein Array von Objekten oder nur die Schlüssel oder nur die Werte zu erhalten.

/**
 * 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;
}

Anwendungsbeispiel:

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

Ich werde auch darauf hinweisen, dass das OP rückwärts an Aufzählungen denkt. Der "Schlüssel" in der Aufzählung befindet sich technisch auf der linken Seite und der Wert befindet sich auf der rechten Seite. Mit TypeScript können Sie die Werte auf der rechten Seite so oft wiederholen, wie Sie möchten.

MgSam
quelle
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
quelle
Bitte setzen Sie Ihre Antwort immer in einen Kontext, anstatt nur Code einzufügen. Sehen Sie hier für weitere Details.
gehbiszumeis
0

Sie können dies folgendermaßen tun:

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"),
}

Und Sie können es so verwenden:

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;

Sie können die GoalProgressMeasurement nach Bedarf um zusätzliche Eigenschaften des Objekts erweitern. Ich verwende diesen Ansatz für jede Aufzählung, die ein Objekt sein sollte, das mehr als einen Wert enthält.

Boban Stojanovski
quelle
0

Da sich Aufzählungen mit Strings-Werten von denen mit Zahlenwerten unterscheiden, ist es besser, Nicht-Zahlen aus der @ user8363-Lösung zu filtern.

Hier ist, wie Sie Werte aus enum entweder Zeichenfolgen, Anzahl von gemischt erhalten können:

    //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
quelle
0

Ich bin überrascht, dass in einem TypeScript-Thread niemand eine gültige TypeScript-Funktion mit unterstützter Eingabe angegeben hat. Hier ist eine Variation der @ user8363-Lösung:

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
quelle
0

Es gibt eine einfache Lösung. Wenn Sie also diese ausführen Object.keys(Enum), erhalten Sie ein Array von Werten und Schlüsseln in den Werten des ersten Slice und im zweiten Schlüssel. Warum wir also nicht nur das zweite Slice zurückgeben, funktioniert dieser Code unten für mich .

enum Enum {
   ONE,
   TWO,
   THREE,
   FOUR,
   FIVE,
   SIX,
   SEVEN
}
const keys = Object.keys(Enum); 
console.log(keys.slice(keys.length / 2));
دانیال مدیری
quelle
Neugierig, warum dies abgelehnt wurde? Ich hatte die gleiche Idee. Wäre die Sorge, dass sich TypeScript ändert und nicht mehr auf die gleiche Weise Aufzählungen erstellt?
Tony Smith vor