可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have an enum defined this way:
export enum GoalProgressMeasurements {
Percentage = 1,
Numeric_Target = 2,
Completed_Tasks = 3,
Average_Milestone_Progress = 4,
Not_Measured = 5
}
However, I'd like it to be represented as an object array/list from our API like below:
[{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'}]
Is there are easy and native way to do this or do I have to build a function that casts the enum to both an int and a string, and build the objects into an array?
回答1:
Enums are real objects that exist at runtime. So you are able to reverse the mapping doing something like this:
let value = GoalProgressMeasurements.Not_Measured;
console.log(GoalProgressMeasurements[value]);
// => Not_Measured
Based on that you can use the following code:
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);
Reference: https://www.typescriptlang.org/docs/handbook/enums.html
回答2:
A tricky bit is that TypeScript will 'double' map the enum in the emitted object, so it can be accessed both by key and value.
enum MyEnum {
Part1 = 0,
Part2 = 1
}
will be emitted as
{
Part1: 0,
Part2: 1,
0: 'Part1',
1: 'Part2'
}
So you should filter the object first before mapping. So @Diullei 's solution has the right answer. Here is my implementation:
// 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 it like this:
export enum GoalProgressMeasurements {
Percentage,
Numeric_Target,
Completed_Tasks,
Average_Milestone_Progress,
Not_Measured
}
console.log(ToArray(GoalProgressMeasurements));
回答3:
Easy Solution. You can use the following function to convert your Enum to an array of objects.
buildGoalProgressMeasurementsArray(): Object[] {
return Object.keys(GoalProgressMeasurements)
.map(key => ({ id: GoalProgressMeasurements[key], name: key }))
}
If you needed to strip that underscore off, we could use regex as follows:
buildGoalProgressMeasurementsArray(): Object[] {
return Object.keys(GoalProgressMeasurements)
.map(key => ({ id: GoalProgressMeasurements[key], name: key.replace(/_/g, ' ') }))
}
回答4:
If you are using ES6
It will give you value array of the given enum.
enum colors = {
"WHITE" : 0,
"BLACK" : 1
}
const colorValueArray = Object.values(colors);
回答5:
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]);
}
}
回答6:
I use
Object.entries(GoalProgressMeasurement).filter(e => !isNaN(e[0]as any)).map(e => ({ name: e[1], id: e[0] }));
A simple 1 line that does the job.
It does the job in 3 simple steps
- Loads the combination of keys & values using Object.entries
.
- Filters out the non numbers (since typescript generates the values for reverse lookup).
- Then we map it to the array object we like.
回答7:
You can do that in this way:
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"),
}
And you can use it like this:
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;
You can extend the GoalProgressMeasurement with additional properties of the object as you need. I'm using this approach for every enumeration that should be an object containing more then a value.
回答8:
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);
回答9:
I didn't like any of the above answers because none of them correctly handle the mixture of strings/numbers that can be values in TypeScript enums.
The following function follows the semantics of TypeScript enums to give a proper Map of keys to values. From there, getting an array of objects or just the keys or just the values is 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;
}
Example Usage:
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
I'll also point out that the OP is thinking of enums backwards. The "key" in the enum is technically on the left hand side and the value is on the right hand side. TypeScript allows you to repeat the values on the RHS as much as you'd like.
回答10:
First we get an array of keys for this enum. Then, using the map () function, we convert the data to the desired format. id is obtained from the key, name is obtained from enum by the same key.
const converted = Object.keys(GoalProgressMeasurements).map(key => {
return {
id: GoalProgressMeasurements[key],
name: key,
};
});