TypeScript 枚举转换为对象数组

我有一个这样定义的枚举:

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

然而,我希望它被表示为一个对象数组/列表从我们的API如下:

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

是否有原生的简单方法来做到这一点还是说我必须构建一个函数,将枚举转换为 int 和字符串,并将对象构建为数组?

344534 次浏览

枚举是运行时存在的真实对象。所以你可以这样反向映射:

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

基于此,您可以使用以下代码:

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

参考:https://www.typescriptlang.org/docs/handbook/enums.html

简单的解决方案。您可以使用以下函数将Enum转换为对象数组。

 buildGoalProgressMeasurementsArray(): Object[] {


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

如果你需要去掉下划线,我们可以像下面这样使用regex:

buildGoalProgressMeasurementsArray(): Object[] {


return Object.keys(GoalProgressMeasurements)
.map(key => ({ id: GoalProgressMeasurements[key], name: key.replace(/_/g, ' ') }))
}

一个棘手的地方是TypeScript会在触发对象中'double'映射enum,所以它可以通过键和值访问。

enum MyEnum {
Part1 = 0,
Part2 = 1
}

将以

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

所以你应该在映射之前先过滤对象。所以@Diullei的解决方案是正确的。这是我的实现:

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

像这样使用它:

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


console.log(ToArray(GoalProgressMeasurements));

你可以这样做:

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

你可以这样使用它:

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;

您可以根据需要使用对象的附加属性扩展GoalProgressMeasurement。我将这种方法用于每个应该是包含多个值的对象的枚举。

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

如果你使用的是ES8

只有在这种情况下,它才会工作得很好。它会给你给定枚举的值数组。

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


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

你将得到像这样的colorValueArray。所有的键都在数组的前半部分,所有的值都在数组的后半部分。

甚至这种枚举也可以正常工作

enum Operation {
READ,
WRITE,
EXECUTE
}

但是这个解决方案将不适用于异构的枚举

enum BooleanLikeHeterogeneousEnum {
No = 0,
Yes = "YES",
}

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

我不喜欢上面的答案,因为它们都不能正确地处理字符串/数字的混合,可以在TypeScript enum中作为值。

下面的函数遵循TypeScript枚举的语义,给出键到值的正确映射。从那里,获取一个对象数组或者仅仅是键或者仅仅是值是很简单的。

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

使用示例:

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

我还要指出OP是逆向思考枚举的。“key"在枚举中,严格来说是在左边,而值在右边。TypeScript允许你尽可能多地重复RHS上的值。

首先,我们得到这个枚举的键数组。然后,使用map()函数将数据转换为所需的格式。Id从键获取,name从enum通过相同的键获取。

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

我使用

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

一条简单的线就可以做到这一点。

它只需要3个简单的步骤 -加载组合键&放大器;值使用Object.entries。< br > -过滤掉非数字(因为typescript会为反向查找生成值)。< br > -然后我们将它映射到我们喜欢的数组对象

这将返回一个枚举值数组:

 Object.values(myEnum);

由于具有字符串值的枚举不同于具有数字值的枚举,因此最好从@user8363解决方案中过滤非数字。

这里是你如何从enum字符串中获取值,数字混合:

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

我很惊讶,在一个TypeScript线程中,没有人给出支持类型的有效TypeScript函数。下面是@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]
);
}

有一个简单的解决方案,所以当你运行Object.keys(Enum)时,它会给你一个值和键的数组,在第一个切片值中,在第二个切片键中,所以为什么我们不只是返回第二个切片,下面的代码适合我。

enum Enum {
ONE,
TWO,
THREE,
FOUR,
FIVE,
SIX,
SEVEN
}
const keys = Object.keys(Enum);
console.log(keys.slice(keys.length / 2));

我不认为顺序可以保证,否则将很容易切片Object.entries结果的后半部分并从那里映射。

上述答案的唯一(非常小的)问题是

  • 字符串和数字之间有很多不必要的类型转换。
  • 当一次迭代同样干净有效时,条目将迭代两次。
type StandardEnum = { [id: string]: number | string; [nu: number]: string;}


function enumToList<T extends StandardEnum> (enm: T) : { id: number; description: string }[] {
return Object.entries(enm).reduce((accum, kv) => {
if (typeof kv[1] === 'number') {
accum.push({ id: kv[1], description: kv[0] })
}
return accum
}, []) // if enum is huge, perhaps pre-allocate with new Array(entries.length / 2), however then push won't work, so tracking an index would also be required
}

感谢polkovnikov.ph,我终于能够找到一个解决方案,将工作的大多数用例。

问题的有效解

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, ' '),
}));
}

例子:


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"}
]
*/

此外,我还使用了一个有用的util函数来将枚举对象映射到它拥有的可用值数组:

该映射器

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

用例工作

  • 数字枚举
enum Colors1 {
WHITE = 0,
BLACK = 1
}
console.log(Object.values(Colors1)); // ['WHITE', 'BLACK', 0, 1]
console.log(enumToArray(Colors1));   // [0, 1]
  • 字符串枚举
enum Colors2 {
WHITE = "white",
BLACK = "black"
}
console.log(Object.values(Colors2)); // ['white', 'black']
console.log(enumToArray(Colors2));   // ['white', 'black']
  • 异构枚举
enum Colors4 {
WHITE = "white",
BLACK = 0
}
console.log(Object.values(Colors4)); // ["BLACK", "white", 0]
console.log(enumToArray(Colors4));   // ["white", 0]
  • 与具有导出函数的命名空间合并的Enum

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']
function enumKeys(_enum) {
const entries = Object.entries(_enum).filter(e => !isNaN(Number(e[0])));
if (!entries.length) {
// enum has string values so we can use Object.keys
return Object.keys(_enum);
}
return entries.map(e => e[1]);
}

在数组中获取enum值。

export enum DocumentationTypeEnum {
GDPR = 'GDPR',
HELP = 'HELP',
OTHER = 'OTHER',
FOOTER = 'FOOTER'
}
const keys = Object.keys(DocumentationTypeEnum);
    

console.log(keys); // Output :  ["GDPR", "HELP", "OTHER", "FOOTER"]

TS:

作品ONLY with short (<10个元素)enum

const keys = Object.keys(Enum).filter((el: string) => el.length > 1)
console.log(keys)
  1. Object.keys()将返回一个包含['0','1','2','enumElement1', 'enumElement2', enumElement3']的数组
  2. Filter()获取每个元素并检查其长度(因为字符串),并从结果数组中排除所有数字

还有另一种使用ES8 Object.entries的方法

export enum Weeks {
MONDAY = 1,
TUESDAY= 2,
WEDNESDAY = 3,
THURSDAY = 4,
FRIDAY = 5,
SATURDAY=6,
SUNDAY=7,
}




function convertEnumToArray(){
const arrayObjects = []
// Retrieve key and values using Object.entries() method.
for (const [propertyKey, propertyValue] of Object.entries(Weeks)) {


// Ignore keys that are not numbers
if (!Number.isNaN(Number(propertyKey))) {
continue;
}


// Add keys and values to array
arrayObjects.push({ id: propertyValue, name: propertyKey });
}


console.log(arrayObjects);
}

将产生以下内容:

[
{ id: 1, name: 'MONDAY' },
{ id: 2, name: 'TUESDAY' },
{ id: 3, name: 'WEDNESDAY' },
{ id: 4, name: 'THURSDAY' },
{ id: 5, name: 'FRIDAY' },
{ id: 6, name: 'SATURDAY' },
{ id: 7, name: 'SUNDAY' }
]

无耻地从这个博客

我从几个月前就了解了typescript,下面的解决方案对我来说很有效。希望它也能帮助到一些人

export enum ScheduleType {
Basic = <any>'B',
Consolidated = <any>'C',
}


scheduleTypes = Object.keys(ScheduleType)
.filter((k, i) => i % 2)
.map((key: any) => {
return {
systemValue: key,
displayValue: ScheduleType[key],
};
});

它给出了以下结果- [{displayValue: "Basic", systemValue: "B"}, {displayValue: "Consolidated", systemValue: "C"}]

. {displayValue: "Consolidated", systemValue: "

此方法基于语句: enum的键不能是数字

export const isNumeric = (num?: Value | null): num is number => {
if (num === undefined || num === null) {
return false;
}
  

const number = +num;


if (number - number !== 0) {
// Discard Infinity and NaN
return false;
}


if (number === num) {
return true;
}


if (typeof num === 'string') {
return !(number === 0 && num.trim() === '');
}
return false;
};


enum En  {
ewq1 = 1,
we2 = 'ss',
sad = 'sad',
}


type TEnum = {
[id: string]: number | string;
}


export const getEnumValues = <T extends TEnum>(enumerable: T) =>
Object.keys(enumerable)
.filter((x) => !isNumeric(x))
.map((key) => enumerable[key] as T[keyof T])


console.log(getEnumValues(En)) // [1, "ss", "sad"]

另一种方法是

export const GoalNames = {
[GoalProgressMeasurements.Percentage] = 'Percentage',
[GoalProgressMeasurements.Numeric_Target] = 'Numeric Target',
[GoalProgressMeasurements.Completed_Tasks] = 'Completed Tasks',
[GoalProgressMeasurements.Average_Milestone_Progress] = 'Average Milestone Progress',
[GoalProgressMeasurements.Not_Measured] = 'Not Measured'
}

你可以调用:

const name = GoalNames[goalEnumVal];

我是这样解的

        const listKeys = Object.keys(TripStatus); //TripStatus is enum type
const numOfItem = listKeys.length/2;
for(let i=0; i<numOfItem; i++){
this.listStatus.push({
id: listKeys[i],
name: listKeys[numOfItem+i]
})
}

只有一句话:

Object.entries(GoalProgressMeasurements).map(([key, value]) => ({id: key, value: value}))

我不建议在需要枚举条目列表的情况下使用TS enum。

在运行时,Enum是作为对象实现的,但它只在这种情况下工作:

enum X {
Z = 'z',
F = 'f'
};


console.log(Object.values(X))
console.log(Object.keys(X))
>>>
[LOG]: ["z", "f"]
[LOG]: ["Z", "F"]

在这种情况下,它与陷阱一起工作(TS让你通过它的数值访问值):

enum X {
Z,
F
};


console.log(Object.values(X))
console.log(Object.keys(X))
>>>
[LOG]: ["Z", "F", 0, 1]
[LOG]: ["0", "1", "Z", "F"]

因此,根据Enum定义,任何你写在Enum上循环的函数都将工作/失败。这是…不好的。

我的结论: Enum不是设计用来作为对象的。如果需要访问键和值集合,请使用const而不是enum:

const Enumed = {
X: 1,
Y: 2
}

Typescript将控制对象键的存在,你将能够以安全和一致的方式执行Object.keys等。

设enum变量为:

 enum EnumName {
A = 1,
B = 2
};

那么列表是:

const list = Object.keys(Enum)
.filter((value => isNaN(Number(value)) === false))
.map(key => ({ id: key, value: Enum[key] }));

list的值为

list = [
{ id:1 , value: A },
{ id:2 , value: B },
];
export function enumKeys(E: any): string[] {
return Object.keys(E).filter(k => isNaN(Number(k)));
}


export function enumValues(E: any): string[] | number[] {
return enumKeys(E).map(k => E[k as any]);
}

两者都适用:

enum TestA {
RED = "red",
BLUE = "blue"
}


enum TestB {
ONE = 1,
TWO = 2
}
 this worked for me :


export enum FeedBackType {
FEEDBACK1= 'FEEDBACK1',
FEEDBACK2= 'FEEDBACK2',
FEEDBACK3= 'FEEDBACK3',
}


-----------------------------------------------------------------
export function getTypeFeedBackList() {
let feedbackList: FeedBackType[] = [];
Object.keys(FeedBackType).map((key) => {
let strEnum = key as unknown as FeedBackType;
feedbackList.push(strEnum);
});
return feedbackList;
}
----------------------------------------------------------------
declare this :


public feedbackList: FeedBackType[] = [];


and after call your function in  :


ngOnInit(): void {
this.feedbackList = getTypeFeedBackList();
console.log(this.feedbackList);
}


Happy coding ;)