簡體   English   中英

TypeScript 枚舉到 object 數組

[英]TypeScript enum to object array

我有一個這樣定義的枚舉:

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

但是,我希望將它表示為我們的 API 中的 object 數組/列表,如下所示:

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

是否有簡單的本機方法來執行此操作,或者我是否必須構建一個 function 將枚舉同時轉換為 int 和字符串,並將對象構建到數組中?

一個棘手的問題是 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));

如果您使用的是 ES8

對於這種情況,它只會工作得很好。 它將為您提供給定enum 的值數組。

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

枚舉是在運行時存在的真實對象。 因此,您可以執行以下操作來反轉映射:

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

簡單地說,這將返回一個枚舉值數組:

 Object.values(myEnum);

簡單的解決方案。 您可以使用以下函數將 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, ' ') }))
 }

我用

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

一個簡單的 1 行就可以完成這項工作。

它通過 3 個簡單的步驟完成工作
- 使用Object.entries加載鍵和值的組合。
- 過濾掉非數字(因為打字稿生成反向查找的值)。
- 然后我們將它映射到我們喜歡的數組對象。

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

感謝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 Colors3 {
    WHITE = "white",
    BLACK = "black"
}
namespace Colors3 {
    export function fun() {}
}
console.log(Object.values(Colors3)); // ['white', 'black', Function]
console.log(enumToArray(Colors3));   // ['white', 'black']

我不喜歡上述任何一個答案,因為它們都沒有正確處理可以是 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

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 正在向后考慮枚舉。 枚舉中的“鍵”在技術上位於左側,而值位於右側。 TypeScript 允許您根據需要重復 RHS 上的值。

首先,我們獲得此枚舉的鍵數組。 然后,使用 map() 函數,我們將數據轉換為所需的格式。 id 是從key 中獲取的,name 是通過相同的key 從enum 中獲取的。

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

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

在需要枚舉條目列表的情況下,我不鼓勵使用 TS 枚舉。

在運行時枚舉被實現為 object,但它僅在這種情況下按預期工作:

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"] 

因此,根據枚舉定義,您編寫的任何 function 都將在枚舉上循環工作/失敗。 這是...不好。

我的結論:Enum 並非設計為用作 object。如果您需要訪問鍵和值 collections,請使用const而不是enum

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

Typescript 將控制 object 密鑰的存在,您將能夠以安全一致的方式執行Object.keys等操作。

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
}

有一個簡單的解決方案,所以當你運行Object.keys(Enum) ,它會給你一個值和鍵的數組,在第一個切片值和第二個鍵中,那么為什么我們不只返回第二個切片,下面的這段代碼對我有用。

enum Enum {
   ONE,
   TWO,
   THREE,
   FOUR,
   FIVE,
   SIX,
   SEVEN
}
const keys = Object.keys(Enum); 
console.log(keys.slice(keys.length / 2));
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]);
}

TS:

僅適用於短(<10 個元素)枚舉

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

無恥地從這個博客竊取

我從幾個月就知道打字稿,下面的解決方案對我有用。 希望它也可以幫助某人-

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

你可以這樣做:

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。 對於應該是包含多個值的對象的每個枚舉,我都使用這種方法。

由於具有字符串值的枚舉不同於具有數字值的枚舉,因此最好從 @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]); }

我在 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.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
}

在數組中獲取枚舉值的示例:

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

console.log(keys); // Output :  ["GDPR", "HELP", "OTHER", "FOOTER"]
export const isNumber = (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;
    [nu: number]: string;
}

export const getEnumValues = <T extends TEnum>(enumerable: T) =>
  Object.keys(enumerable)
    .filter((x) => !isNumber(x))
    .map((key) => enumerable[key]) as Array<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}))

讓枚舉變量為:

 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 = [ 
{ id:1 , value: A },
{ id:2 , value: B },
];
 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 ;) 

我已經解決了它,這樣。 假設你有一個像下面這樣的枚舉

export enum UnitEnum {
  GRAM = 'gm',
  KILOGRAM = 'kg',
  LITRE = 'lt',
  CENTIMETER = 'cm',
  INCH = 'in',
  METER = 'mt',
  KILOMETER = 'km',
}

而且,你有一個像這樣的 class,

export interface Unit {
  Name: string;
  Symbol: string;
}

然后你可以創建一個如下所示的 function 到 map 異構枚舉到某種類型的 object,

export function getDefaultUnits() {
  const myUnits = Object.entries(UnitEnum).map(x => {
    return { Name: x[0], Symbol: x[1] } as Unit
  })

  console.log(myUnits);

  return myUnits;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM