Enumeración de TypeScript a matriz de objetos

100

Tengo una enumeración definida de esta manera:

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

Sin embargo, me gustaría que se representara como una matriz / lista de objetos de nuestra API como se muestra a continuación:

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

¿Hay una forma fácil y nativa de hacer esto o tengo que construir una función que arroje la enumeración tanto a un int como a una cadena, y construya los objetos en una matriz?

AnimaSola
fuente
Las enumeraciones son objetos reales que existen en tiempo de ejecución. Entonces puede revertir el mapeo haciendo algo como esto: GoalProgressMeasurements[GoalProgressMeasurements.Completed_Tasks]para obtener el nombre de enumeración. No sé si eso ayuda.
Diullei
¿Puede dar una mejor descripción de "de nuestra API", tal vez dar un ejemplo de uso
gilamran

Respuestas:

47

Un poco complicado es que TypeScript 'duplicará' la asignación de la enumeración en el objeto emitido, por lo que se puede acceder a él tanto por clave como por valor.

enum MyEnum {
    Part1 = 0,
    Part2 = 1
}

será emitido como

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

Por lo tanto, primero debe filtrar el objeto antes de realizar el mapeo. Entonces, la solución de @Diullei tiene la respuesta correcta. Aquí está mi implementación:

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

Úselo así:

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

console.log(ToArray(GoalProgressMeasurements));
user8363
fuente
1
mmm si se enum MyEnum { Part1 = 0, Part2 = 1 }convierte en { Part1: 0, Part2: 1, 0: 'Part1', 1: 'Part2' } entonces, ¿por qué cuando console.log(Object.values(MyEnum))solo imprime 0,1?
Juan José Ramírez
@ JuanJoséRamírez ¿dónde ves eso? Para mí se Object.values(MyEnum)evalúa como["Part1", "Part2", 0, 1]
user8363
Acabo de imprimir console.log(Object.values(MyEnum))en mi componente. Estoy usando angular, no estoy seguro de si eso está relacionado. No tengo tanta experiencia en TypeScript
Juan José Ramírez
¿Podría cambiar el comportamiento a través de diferentes versiones de TS?
Juan José Ramírez
3
He estado revisando los documentos typescriptlang.org/docs/handbook/release-notes/… y parece que las enumeraciones de cadenas tienen un comportamiento diferente. No obtienen un mapeo inverso generado en absoluto. En mi código, estaba usando una enumeración de cadena, no la cadena en este ejemplo.
Juan José Ramírez
40

Si está utilizando ES6

Le dará una matriz de valores de la enumeración dada .

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

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

Te pondrás colorValueArrayasí [ 'WHITE', 'BLACK', 'BLUE', 0, 1, 3 ]. Todas las claves estarán en la primera mitad de la matriz y todos los valores en la segunda mitad.

Jai Prak
fuente
8
Tenga en cuenta que esto producirá duplicados. El valor de la cadena y el valor numérico de cada elemento, es decir, un tipo (string | YourEnumType)[]que no es el que podría desear en cada caso.
Christian Ivicevic
¿Se garantiza que la primera mitad serán las claves y la segunda mitad serán los valores? alguna referencia?
Neekey
36

Las enumeraciones son objetos reales que existen en tiempo de ejecución. Entonces puede revertir el mapeo haciendo algo como esto:

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

En base a eso, puede usar el siguiente código:

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

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

Diullei
fuente
3
no es necesario que escriba los valores predeterminados = 2hasta que = 5- Cualquier valor posterior = 1es +1 automáticamente.
sebilasse
8
Quizás no sea necesario, pero es más expresivo. Lo que lo hace mejor en mi humilde opinión.
SubliemeSiem
4
solo una nota, esto no funciona para enumeraciones de valor de cadena
Bashar Ali Labadi
21

Solución fácil. Puede usar la siguiente función para convertir su Enum en una matriz de objetos.

 buildGoalProgressMeasurementsArray(): Object[] {

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

Si necesita eliminar ese subrayado, podríamos usar expresiones regulares de la siguiente manera:

buildGoalProgressMeasurementsArray(): Object[] {

    return Object.keys(GoalProgressMeasurements)
              .map(key => ({ id: GoalProgressMeasurements[key], name: key.replace(/_/g, ' ') }))
 }
Manoj Shrestha
fuente
7
debe filtrar las claves del número de tipo Object.keys(GoalProgressMeasurements) .filter(key => typeof GoalProgressMeasurements[key] === 'number') .map(key => ({ id: GoalProgressMeasurements[key], name: key }))
Salvador Rubio Martinez
12

yo suelo

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

Una línea simple que hace el trabajo.

Hace el trabajo en 3 sencillos pasos
: carga la combinación de claves y valores usando Object.entries.
- Filtra los no números (ya que el mecanografiado genera los valores para la búsqueda inversa).
- Luego lo asignamos al objeto de matriz que nos gusta.

CMS
fuente
No es compatible con IE con front-end (no debería ser compatible, es decir, es una gran respuesta ... pero supongo que los clientes). Espero que babel lo suceda, pero sigo otros enfoques, ya que no lo he verificado
TamusJRoyce
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
fuente
1
Gracias por estos ayudantes. Muy útil.
user906573
8

Esto le dará una matriz de valores de enumeración:

 Object.values(myEnum);
Gaurav Panwar
fuente
¿Por qué esta no es la respuesta principal? ¡Gracias de todos modos!
Paulo Queiroz
@PauloQueiroz Subirá automáticamente si aumenta el voto a favor. ¡Gracias!
Gaurav Panwar
Debido a que no da el resultado correcto, consulte stackoverflow.com/a/57266281/3548345
walox
4

Primero obtenemos una matriz de claves para esta enumeración. Luego, usando la función map (), convertimos los datos al formato deseado. id se obtiene de la clave, nombre se obtiene de enum por la misma clave.

const converted = Object.keys(GoalProgressMeasurements).map(key => {
        return {
            id: GoalProgressMeasurements[key],
            name: key,
        };
    });
VaCool
fuente
2
Bienvenido a stackoverflow. Al responder preguntas, es una buena idea explicar qué hace el fragmento de código. Para obtener más información, consulte aquí: Cómo responder
Djensen
Considere agregar alguna explicación o detalles a su respuesta. Si bien podría responder a la pregunta, simplemente agregar un fragmento de código como respuesta, no ayuda a OP ni a los futuros miembros de la comunidad a comprender el problema o la solución propuesta.
Maxim
2

No me gustó ninguna de las respuestas anteriores porque ninguna de ellas maneja correctamente la combinación de cadenas / números que pueden ser valores en las enumeraciones de TypeScript.

La siguiente función sigue la semántica de las enumeraciones de TypeScript para proporcionar un mapa adecuado de claves para los valores. A partir de ahí, obtener una matriz de objetos o solo las claves o solo los valores es trivial.

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

Ejemplo de uso:

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

También señalaré que el OP está pensando en enumeraciones al revés. La "clave" en la enumeración está técnicamente en el lado izquierdo y el valor está en el lado derecho. TypeScript le permite repetir los valores en el RHS tanto como desee.

MgSam
fuente
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
fuente
Por favor, ponga su respuesta siempre en contexto en lugar de simplemente pegar el código. Consulte aquí para obtener más detalles.
gehbiszumeis
0

Puedes hacerlo de esta manera:

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

Y puedes usarlo así:

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;

Puede ampliar GoalProgressMeasurement con propiedades adicionales del objeto según lo necesite. Estoy usando este enfoque para cada enumeración que debería ser un objeto que contenga más de un valor.

Boban Stojanovski
fuente
0

Dado que las enumeraciones con valores de cadenas difieren de las que tienen valores numéricos, es mejor filtrar los no números de la solución @ user8363.

Así es como puede obtener valores de enum, ya sea cadenas, números de mezcla:

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

Me sorprende que en un hilo de TypeScript nadie haya dado una función válida de TypeScript con soporte de escritura. Aquí está la variación de la solución @ 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
fuente
-1

Hay una solución simple, así que cuando ejecute Object.keys(Enum)eso le dará una matriz de valores y claves, en los valores del primer segmento y en el segundo, las claves, entonces, ¿por qué no devolvemos el segundo segmento? Este código a continuación funciona para mí. .

enum Enum {
   ONE,
   TWO,
   THREE,
   FOUR,
   FIVE,
   SIX,
   SEVEN
}
const keys = Object.keys(Enum); 
console.log(keys.slice(keys.length / 2));
دانیال مدیری
fuente