javascript - Enum do TypeScript para array de objetos
Eu tenho um enum definido desta maneira:
export enum GoalProgressMeasurements {
Percentage = 1,
Numeric_Target = 2,
Completed_Tasks = 3,
Average_Milestone_Progress = 4,
Not_Measured = 5
}
No entanto, eu gostaria que fosse representado como um array/lista de objetos da nossa API, como abaixo:
[{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'}]
Existe uma maneira fácil e nativa de fazer isso ou eu tenho que construir uma função que converta o enum tanto para int quanto para string, e montar os objetos em um array?
Resposta #1
Se você estiver usando ES8
Para este caso apenas, funcionará perfeitamente. Ele lhe dará um array de valores do enum fornecido.
enum Colors {
WHITE = 0,
BLACK = 1,
BLUE = 3
}
const colorValueArray = Object.values(Colors); //[ 'WHITE', 'BLACK', 'BLUE', 0, 1, 3 ]
Você receberá colorValueArray assim [ 'WHITE', 'BLACK', 'BLUE', 0, 1, 3 ]. Todas as chaves estarão na primeira metade do array e todos os valores na segunda metade.
Mesmo esse tipo de enum funcionará bem
enum Operation {
READ,
WRITE,
EXECUTE
}
Mas esta solução não funcionará para enums heterogêneos como este
enum BooleanLikeHeterogeneousEnum {
No = 0,
Yes = "YES",
}
Resposta #2
Uma parte complicada é que o TypeScript irá 'mapear duplamente' o enum no objeto emitido, para que ele possa ser acessado tanto pela chave quanto pelo valor.
enum MyEnum {
Part1 = 0,
Part2 = 1
}
será emitido como
{
Part1: 0,
Part2: 1,
0: 'Part1',
1: 'Part2'
}
Então você deve filtrar o objeto antes de mapear. Então a solução do @Diullei tem a resposta certa. Aqui está a minha implementação:
// 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]);
}
Use-o assim:
export enum GoalProgressMeasurements {
Percentage,
Numeric_Target,
Completed_Tasks,
Average_Milestone_Progress,
Not_Measured
}
console.log(ToArray(GoalProgressMeasurements));
Resposta #3
Simplesmente isso retornará um array de valores de enum:
Object.values(myEnum);
Resposta #4
Enums são objetos reais que existem em tempo de execução. Portanto, você é capaz de reverter o mapeamento fazendo algo assim:
let value = GoalProgressMeasurements.Not_Measured;
console.log(GoalProgressMeasurements[value]);
// => Not_Measured
Com base nisso, você pode usar o seguinte 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);
Referência: https://www.typescriptlang.org/docs/handbook/enums.html
Resposta #5
Solução Fácil. Você pode usar a seguinte função para converter seu Enum em um array de objetos.
buildGoalProgressMeasurementsArray(): Object[] {
return Object.keys(GoalProgressMeasurements)
.map(key => ({ id: GoalProgressMeasurements[key], name: key }))
}
Se você precisasse remover esse sublinhado, poderíamos usar regex da seguinte forma:
buildGoalProgressMeasurementsArray(): Object[] {
return Object.keys(GoalProgressMeasurements)
.map(key => ({ id: GoalProgressMeasurements[key], name: key.replace(/_/g, ' ') }))
}
Resposta #6
Eu uso
Object.entries(GoalProgressMeasurement).filter(e => !isNaN(e[0]as any)).map(e => ({ name: e[1], id: e[0] }));
Uma linha simples que faz o trabalho.
Isso faz o trabalho em 3 passos simples
- Carrega a combinação de chaves e valores usando Object.entries.
- Filtra os que não são números (já que o TypeScript gera os valores para pesquisa reversa).
- Então nós mapeamos para o objeto de array que desejamos.
Resposta #7
Graças a polkovnikov.ph, finalmente consegui encontrar uma solução que funcionaria para a maioria dos casos de uso.
Solução válida para a pergunta
type Descripted<T> = {
[K in keyof T]: {
readonly id: T[K];
readonly description: string;
}
}[keyof T]
/**
* Helper to produce an array of enum descriptors.
* @param enumeration Enumeration object.
* @param separatorRegex Regex that would catch the separator in your enum key.
*/
export function enumToDescriptedArray<T>(enumeration: T, separatorRegex: RegExp = /_/g): Descripted<T>[] {
return (Object.keys(enumeration) as Array<keyof T>)
.filter(key => isNaN(Number(key)))
.filter(key => typeof enumeration[key] === "number" || typeof enumeration[key] === "string")
.map(key => ({
id: enumeration[key],
description: String(key).replace(separatorRegex, ' '),
}));
}
Exemplo :
export enum GoalProgressMeasurements {
Percentage = 1,
Numeric_Target = 2,
Completed_Tasks = 3,
Average_Milestone_Progress = 4,
Not_Measured = 5
}
console.log(enumToDescriptedArray(GoalProgressMeasurements))
// Produces:
/*
[
{id: 1, description: "Percentage"},
{id: 2, description: "Numeric Target"},
{id: 3, description: "Completed Tasks"},
{id: 4, description: "Average Milestone Progress"},
{id: 5, description: "Not Measured"}
]
*/
Além disso, há uma função utilitária útil que eu uso para mapear o objeto de enumeração para um array dos valores disponíveis que ele possui:
O mapeador
type NonFunctional<T> = T extends Function ? never : T;
/**
* Helper to produce an array of enum values.
* @param enumeration Enumeration object.
*/
export function enumToArray<T>(enumeration: T): NonFunctional<T[keyof T]>[] {
return Object.keys(enumeration)
.filter(key => isNaN(Number(key)))
.map(key => enumeration[key])
.filter(val => typeof val === "number" || typeof val === "string");
}
Casos de uso em funcionamento
- Enumeração numérica
enum Colors1 {
WHITE = 0,
BLACK = 1
}
console.log(Object.values(Colors1)); // ['WHITE', 'BLACK', 0, 1]
console.log(enumToArray(Colors1)); // [0, 1]
- Enumeração de string
enum Colors2 {
WHITE = "white",
BLACK = "black"
}
console.log(Object.values(Colors2)); // ['white', 'black']
console.log(enumToArray(Colors2)); // ['white', 'black']
- Enum heterogêneo
enum Colors4 {
WHITE = "white",
BLACK = 0
}
console.log(Object.values(Colors4)); // ["BLACK", "white", 0]
console.log(enumToArray(Colors4)); // ["white", 0]
- Enum mesclado com um namespace com funções exportadas
enum Colors3 {
WHITE = "white",
BLACK = "black"
}
namespace Colors3 {
export function fun() {}
}
console.log(Object.values(Colors3)); // ['white', 'black', Function]
console.log(enumToArray(Colors3)); // ['white', 'black']
Resposta #8
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]);
}
}
Resposta #9
Exemplo para obter valor de enum dentro de um array :
export enum DocumentationTypeEnum {
GDPR = 'GDPR',
HELP = 'HELP',
OTHER = 'OTHER',
FOOTER = 'FOOTER'
}
const keys = Object.keys(DocumentationTypeEnum);
console.log(keys); // Output : ["GDPR", "HELP", "OTHER", "FOOTER"]
Resposta #10
Eu não gostei de nenhuma das respostas acima porque nenhuma delas lida corretamente com a mistura de strings/números que podem ser valores em enums do TypeScript.
A função a seguir segue a semântica dos enums do TypeScript para fornecer um Map adequado de chaves para valores. A partir daí, obter um array de objetos ou apenas as chaves ou apenas os valores é 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;
}
Exemplo de uso:
enum Dog {
Rover = 1,
Lassie = "Collie",
Fido = 3,
Cody = "Mutt",
}
let map = enumToMap(Dog); //Map of keys to values
let 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
Também vou apontar que o autor original está pensando em enums ao contrário. A "chave" no enum está tecnicamente no lado esquerdo e o valor está no lado direito. TypeScript permite que você repita os valores no lado direito tanto quanto quiser.
