TypeScript枚举到对象数组


110

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

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和字符串的函数,并将对象构建为数组?


枚举是在运行时存在的真实对象。因此,您可以执行以下操作来逆向映射:GoalProgressMeasurements[GoalProgressMeasurements.Completed_Tasks]获取枚举名称。我不知道这是否有帮助。
Diullei

您能否给“来自我们的API”一个更好的描述,或者给出一个用法示例
gilamran

Answers:


52

棘手的一点是TypeScript将“双重”映射发射对象中的枚举,因此可以通过键和值对其进行访问。

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

1
如果enum MyEnum { Part1 = 0, Part2 = 1 }变成 mmm,{ Part1: 0, Part2: 1, 0: 'Part1', 1: 'Part2' } 那么为什么console.log(Object.values(MyEnum))只打印0,1?
JuanJoséRamírez20年

@JuanJoséRamírez在哪里看到的?对我来说Object.values(MyEnum)评估为["Part1", "Part2", 0, 1]
user8363'4

我只是console.log(Object.values(MyEnum))在组件中打印。我正在使用角度,不确定是否相关。我在TypeScript中没有那么丰富的经验
JuanJoséRamírez20年

行为可以通过不同的TS版本改变吗?
JuanJoséRamírez20年

3
我一直在检查docstypescriptlang.org/docs/handbook/release-notes/…,似乎字符串枚举有不同的行为。它们根本不会生成反向映射。在我的代码中,我使用的是字符串枚举,而不是本例中的字符串。
JuanJoséRamírez20年

46

如果您使用的是ES8

在这种情况下,它将工作得很好。它将为您提供给定枚举的值数组。

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

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

你会得到colorValueArray这样的[ 'WHITE', 'BLACK', 'BLUE', 0, 1, 3 ]。所有键将在数组的前半部分,而所有值将在后半部分。

即使这种枚举也可以正常工作

enum Operation {
    READ,
    WRITE,
    EXECUTE
}

但这种解决方案将不工作异构枚举这样

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

9
请记住,这将产生重复项。每个元素的字符串值和数值,(string | YourEnumType)[]这不是每种情况下都可能需要的类型。
Christian Ivicevic

是否保证前半部分将是键,后半部分将是值?有参考吗?
Neekey

1
Object.values()不属于ES6。它是ES2017的一部分。
atiyar

注意没有ES8之类的东西。正如@atiyar所说的,它叫做ES2017。
异端猴

37

枚举是在运行时存在的真实对象。因此,您可以执行以下操作来反转映射:

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


3
您无需编写默认值,= 2直到= 5-= 1自动为+1。
sebilasse

9
也许您不需要,但是它更具表现力。更好的恕我直言。
SubliemeSiem

5
只是注意,这不适用于字符串值枚举
Bashar Ali Labadi

21

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

 buildGoalProgressMeasurementsArray(): Object[] {

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

如果您需要去除下划线,可以使用正则表达式,如下所示:

buildGoalProgressMeasurementsArray(): Object[] {

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

8
你应该过滤的类型号键 Object.keys(GoalProgressMeasurements) .filter(key => typeof GoalProgressMeasurements[key] === 'number') .map(key => ({ id: GoalProgressMeasurements[key], name: key }))
萨尔瓦多卢比奥马丁内斯

13

我用

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

简单的1行即可完成工作。

它只需3个简单步骤即可完成工作
-使用加载键和值的组合Object.entries
-过滤掉非数字(因为打字稿会生成用于反向查找的值)。
-然后我们将其映射到我们喜欢的数组对象。


与前端的IE不兼容(不应该支持ie,这是一个很好的答案...但是我想是客户)。希望babel能够实现它,但是由于我尚未验证它,所以坚持使用其他方法
TamusJRoyce

字符串枚举很容易,只需执行Object.values(GoalProgressMeasurement)
CMS


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

1
谢谢您的帮助。很有用。
user906573

4

首先,我们获得该枚举的键数组。然后,使用map()函数,将数据转换为所需的格式。id是从键获得的,name是从枚举中通过同一键获得的。

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

2
欢迎使用stackoverflow。回答问题时,最好解释一下代码段的作用。有关更多信息,请参见此处:如何回答
Djensen


请考虑在回答中添加一些解释或详细信息。虽然它可能会回答问题,但仅添加一段代码作为答案,并不意味着可以帮助OP或未来的社区成员理解问题或建议的解决方案。
Maxim

3

我不喜欢上述任何答案,因为它们都不能正确处理可能是TypeScript枚举中的值的字符串/数字的混合。

以下函数遵循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

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

我还要指出,OP在考虑枚举。枚举中的“键”在技术上位于左侧,而值在右侧。使用TypeScript,您可以根据需要在RHS上重复很多值。


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


请始终将答案放在上下文中,而不仅仅是粘贴代码。有关更多详细信息,请参见此处
gehbiszumeis 19/12/12

1

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

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

好奇为什么这被否决了?我有同样的想法。会担心TypeScript会改变并且不再以相同的方式生成枚举吗?
托尼·史密斯

1

这是我使用正确键入的简单功能

/**
 * Helper to produce an array of enum values.
 * @param enumeration Enumeration object.
 */
export function enumToArray<T, G extends keyof T = keyof T>(enumeration: T): T[G][] {
  // tslint:disable: comment-format

  // enum Colors {
  //   WHITE = 0,
  //   BLACK = 1,
  // }
  // Object.values(Colors) will produce ['WHITE', 'BLACK', 0, 1]

  // So, simply slice the second half
  const enumValues = Object.values(enumeration);
  return enumValues.slice(enumValues.length / 2, enumValues.length) as T[G][];
}

用法示例:

enum Colors {
  Red = 1,
  Blue = 2,
}
enumToArray(Colors)

好一个。谢谢!!!
Mauricio Contreras

0

您可以通过以下方式进行操作:

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。我将这种方法用于每个枚举,该枚举应该是一个包含更多值的对象。


0

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

这是从字符串或混合数中获取枚举值的方法:

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


0

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

0

我认为订单不能得到保证,否则很容易将 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
}

0
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]);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.