TypeScript enum tới mảng đối tượng


100

Tôi có một enum được định nghĩa theo cách này:

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

Tuy nhiên, tôi muốn nó được biểu diễn dưới dạng một mảng / danh sách đối tượng từ API của chúng tôi như bên dưới:

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

Có cách nào dễ dàng và nguyên bản để thực hiện điều này không hay tôi phải xây dựng một hàm truyền enum thành cả int và string, đồng thời xây dựng các đối tượng thành một mảng?


Enums là các đối tượng thực tồn tại trong thời gian chạy. Vì vậy, bạn có thể đảo ngược ánh xạ làm một cái gì đó như sau: GoalProgressMeasurements[GoalProgressMeasurements.Completed_Tasks]để lấy tên enum. Tôi không biết nếu điều đó giúp đỡ.
Diullei

Bạn có thể cho một mô tả tốt hơn để "từ API của chúng tôi", có thể đưa ra một ví dụ về việc sử dụng
gilamran

Câu trả lời:


47

Một chút khó khăn là TypeScript sẽ 'nhân đôi' ánh xạ enum trong đối tượng được phát ra, vì vậy nó có thể được truy cập bằng cả khóa và giá trị.

enum MyEnum {
    Part1 = 0,
    Part2 = 1
}

sẽ được phát ra dưới dạng

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

Vì vậy, bạn nên lọc đối tượng trước khi ánh xạ. Vì vậy, giải pháp của @Diullei có câu trả lời đúng. Đây là cách triển khai của tôi:

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

Sử dụng nó như thế này:

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

console.log(ToArray(GoalProgressMeasurements));

1
mmm nếu enum MyEnum { Part1 = 0, Part2 = 1 }biến thành { Part1: 0, Part2: 1, 0: 'Part1', 1: 'Part2' } then, tại sao khi bạn console.log(Object.values(MyEnum))nó chỉ in 0,1?
Juan José Ramírez

@ JuanJoséRamírez bạn thấy điều đó ở đâu? Đối với tôi, Object.values(MyEnum)đánh giá là["Part1", "Part2", 0, 1]
user8363

Tôi chỉ in console.log(Object.values(MyEnum))trong thành phần của tôi. Tôi đang sử dụng góc cạnh, không chắc liệu điều đó có liên quan hay không. Tôi không có kinh nghiệm về TypeScript
Juan José Ramírez

hành vi có thể thay đổi qua các phiên bản TS khác nhau không?
Juan José Ramírez

3
Tôi đã kiểm tra các tài liệu stylescriptlang.org/docs/handbook/release-notes/… và có vẻ như string enums có một hành vi khác. Họ không nhận được một ánh xạ ngược được tạo ra. Trong mã của tôi, tôi đang sử dụng một chuỗi enum, không phải chuỗi trong ví dụ này.
Juan José Ramírez

40

Nếu bạn đang sử dụng ES6

Nó sẽ cung cấp cho bạn mảng giá trị của enum đã cho .

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

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

Bạn sẽ nhận được colorValueArraynhư thế này [ 'WHITE', 'BLACK', 'BLUE', 0, 1, 3 ]. Tất cả các khóa sẽ nằm trong nửa đầu của mảng và tất cả các giá trị trong nửa sau.


9
Hãy nhớ rằng điều này sẽ tạo ra các bản sao. Giá trị chuỗi và giá trị số cho mỗi phần tử, đó là kiểu (string | YourEnumType)[]không phải là thứ bạn có thể muốn trong mỗi trường hợp.
Christian Ivicevic

có đảm bảo rằng nửa đầu sẽ là các khóa và nửa sau sẽ là các giá trị không? bất kỳ tài liệu tham khảo?
Neekey

36

Enums là các đối tượng thực tồn tại trong thời gian chạy. Vì vậy, bạn có thể đảo ngược ánh xạ khi làm như sau:

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

Dựa vào đó, bạn có thể sử dụng mã sau:

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

Tham khảo: https://www.typescriptlang.org/docs/handbook/enums.html


3
bạn không cần phải viết các giá trị mặc định = 2cho đến khi = 5- Mọi thứ sau đó = 1đều được +1 tự động.
sebilasse

8
Có thể bạn không cần, nhưng nó mang tính biểu cảm hơn. Điều đó làm cho IMHO tốt hơn.
SubliemeSiem

5
chỉ là một ghi chú này không làm việc cho enums chuỗi giá trị
Bashar Ali Labadi

21

Giải pháp dễ dàng. Bạn có thể sử dụng hàm sau để chuyển đổi Enum của bạn thành một mảng đối tượng.

 buildGoalProgressMeasurementsArray(): Object[] {

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

Nếu bạn cần loại bỏ dấu gạch dưới đó, chúng tôi có thể sử dụng regex như sau:

buildGoalProgressMeasurementsArray(): Object[] {

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

7
bạn nên lọc các phím số loại Object.keys(GoalProgressMeasurements) .filter(key => typeof GoalProgressMeasurements[key] === 'number') .map(key => ({ id: GoalProgressMeasurements[key], name: key }))
Salvador Rubio Martinez

12

tôi sử dụng

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

1 dòng đơn giản thực hiện công việc.

Nó thực hiện công việc trong 3 bước đơn giản
- Tải kết hợp các khóa và giá trị bằng cách sử dụng Object.entries.
- Lọc ra các số không phải là số (vì bảng chữ tạo ra các giá trị để tra cứu ngược lại).
- Sau đó, chúng tôi ánh xạ nó vào đối tượng mảng mà chúng tôi thích.


Không tương thích với IE với front-end (không nên hỗ trợ tức là một câu trả lời tuyệt vời ... nhưng tôi đoán là khách hàng). Babel hy vọng sẽ biến nó thành công, nhưng vẫn theo
đuổi

Điều này không hoạt động đối với enums chuỗi.
Tony Smith

9
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
Cảm ơn bạn vì những người giúp đỡ này. Rất hữu dụng.
user906573


4

Đầu tiên, chúng ta nhận được một mảng các khóa cho enum này. Sau đó, sử dụng hàm map (), chúng tôi chuyển đổi dữ liệu sang định dạng mong muốn. id được lấy từ khóa, tên được lấy từ enum bằng cùng một khóa.

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

2
Chào mừng bạn đến với stackoverflow. Khi trả lời các câu hỏi, bạn nên giải thích đoạn mã của mình hoạt động như thế nào. Để biết thêm thông tin, hãy xem tại đây: Cách trả lời
Djensen


Vui lòng xem xét thêm một số giải thích hoặc chi tiết cho câu trả lời của bạn. Mặc dù nó có thể trả lời câu hỏi, nhưng chỉ cần thêm một đoạn mã làm câu trả lời, thực tế không giúp OP hoặc các thành viên cộng đồng tương lai hiểu được vấn đề hoặc giải pháp được đề xuất.
Maxim

2

Tôi không thích bất kỳ câu trả lời nào ở trên vì không câu trả lời nào trong số chúng xử lý chính xác hỗn hợp các chuỗi / số có thể là giá trị trong enums TypeScript.

Hàm sau tuân theo ngữ nghĩa của enums TypeScript để cung cấp một Bản đồ các khóa cho các giá trị thích hợp. Từ đó, việc nhận một mảng các đối tượng hoặc chỉ các khóa hoặc chỉ các giá trị là điều tầm thường.

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

Cách sử dụng ví dụ:

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

Tôi cũng sẽ chỉ ra rằng OP đang nghĩ về enums ngược. Về mặt kỹ thuật, "khóa" trong enum nằm ở phía bên trái và giá trị ở phía bên phải. TypeScript cho phép bạn lặp lại các giá trị trên RHS nhiều như bạn muốn.


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


Vui lòng đặt câu trả lời của bạn luôn trong ngữ cảnh thay vì chỉ dán mã. Xem tại đây để biết thêm chi tiết.
gehbiszumeis

0

Bạn có thể làm điều đó theo cách này:

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

Và bạn có thể sử dụng nó như thế này:

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;

Bạn có thể mở rộng GoalProgressMeasurement với các thuộc tính bổ sung của đối tượng khi bạn cần. Tôi đang sử dụng cách tiếp cận này cho mọi kiểu liệt kê phải là một đối tượng chứa nhiều giá trị hơn.


0

Vì các giá trị enums với Strings khác với các enums có giá trị số, tốt hơn nên lọc các nonNumbers khỏi giải pháp @ user8363.

Đây là cách bạn có thể lấy giá trị từ enum một trong hai chuỗi, số hỗn hợp:

    //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

Tôi ngạc nhiên trong một chuỗi TypeScript không ai cung cấp chức năng TypeScript hợp lệ với hỗ trợ gõ. Đây là biến thể của giải pháp @ 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

Có một giải pháp đơn giản, Vì vậy, khi bạn chạy Object.keys(Enum)nó sẽ cung cấp cho bạn Mảng Giá trị và Khóa, trong Giá trị lát đầu tiên và trong khóa thứ hai, vậy tại sao chúng tôi không trả lại lát thứ hai, đoạn mã này bên dưới phù hợp với tôi .

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

Tò mò tại sao điều này bị phản đối? Tôi đã có cùng một ý tưởng. Liệu bạn có lo lắng rằng TypeScript sẽ thay đổi và không còn tạo ra enums theo cách như cũ không?
Tony Smith
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.