Перерахування TypeScript до масиву об’єктів


100

У мене перелік визначений таким чином:

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]отримати ім’я переліку. Не знаю, чи це допомагає.
Діулей

Чи можете ви дати кращий опис "з нашого API", можливо, навести приклад використання
gilamran

Відповіді:


47

Хитрий біт полягає в тому, що 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 }перетворюється на { Part1: 0, Part2: 1, 0: 'Part1', 1: 'Part2' } тоді, чому, коли ви console.log(Object.values(MyEnum))просто друкуєте лише 0,1?
Хуан Хосе Рамірес

@ JuanJoséRamírez де ти це бачиш? Для мене Object.values(MyEnum)оцінює["Part1", "Part2", 0, 1]
user8363

Я щойно надрукував console.log(Object.values(MyEnum))у своєму компоненті. Я використовую angular, не впевнений, що це пов’язано. Я не такий досвідчений у TypeScript
Хуан Хосе Рамірес

чи може поведінка змінитися через різні версії TS?
Хуан Хосе Рамірес

3
Я перевіряв docs typescriptlang.org/docs/handbook/release-notes/… і, схоже, перелічення рядків мають іншу поведінку. Вони взагалі не отримують зворотне відображення. У моєму коді я використовував перерахування рядка, а не рядок у цьому прикладі.
Хуан Хосе Рамірес

40

Якщо ви використовуєте ES6

Це дасть вам масив значень даного переліку .

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 ]. Усі ключі будуть у першій половині масиву, а всі значення - у другій половині.


9
Майте на увазі, що це дасть дублікати. Значення рядка та числове значення для кожного елемента, тобто типу, (string | YourEnumType)[]який не є тим, що вам може знадобитися в кожному випадку.
Крістіан Івічевич,

чи гарантовано, що перша половина буде ключами, а друга половина - значеннями? будь-яке посилання?
Нікі

36

Перелічення - це реальні об'єкти, які існують під час виконання. Таким чином, ви можете змінити відображення, роблячи щось подібне:

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

8
Можливо, вам і не потрібно, але це більш виразно. Що робить його кращим IMHO.
SubliemeSiem

5
лише примітка, що це не працює для перерахувань рядкових значень
Башар Алі

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

7
вам слід відфільтрувати ключі типу номера Object.keys(GoalProgressMeasurements) .filter(key => typeof GoalProgressMeasurements[key] === 'number') .map(key => ({ id: GoalProgressMeasurements[key], name: key }))
Сальвадор Рубіо Мартінес

12

я використовую

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

Простий 1 рядок, який виконує цю роботу.

Це робить роботу в 3 простих кроки
- завантажує комбінацію ключів і значень за допомогою Object.entries.
- Фільтрує не числа (оскільки машинопис генерує значення для зворотного пошуку).
- Потім ми прив'язуємо його до об'єкта масиву, який нам подобається.


Не сумісний з IE з інтерфейсом (не повинен підтримувати, тобто є чудовою відповіддю ... але я думаю, клієнти). Сподіваючись, що
Бабель виявляє

Це не працює для перерахувань рядків.
Тоні Сміт

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
Дякую за цих помічників. Дуже корисний.
user906573

8

Це дасть вам масив значень переліку:

 Object.values(myEnum);

Чому це не головна відповідь? У будь-якому разі, дякую!
Пауло Кейроз

@PauloQueiroz Він автоматично піде вгору, якщо збільшиться "за". Дякую!
Gaurav Panwar

Тому що це Байдуже т дати правильний результат, перевірка stackoverflow.com/a/57266281/3548345
walox

4

Спочатку ми отримуємо масив ключів для цього переліку. Потім, використовуючи функцію map (), ми перетворюємо дані у потрібний формат. id отримується з ключа, ім'я отримується з переліку тим самим ключем.

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

2
Ласкаво просимо до stackoverflow. Відповідаючи на запитання, корисно пояснити, що робить ваш фрагмент коду. Для отримання додаткової інформації дивіться тут: Як відповісти
Дженсен


Будь ласка, подумайте про те, щоб додати відповідь на пояснення чи деталі. Незважаючи на те, що він може відповісти на запитання, просто додавши шматок коду як відповідь, жодне слово не допомагає ОП чи майбутнім членам спільноти зрозуміти проблему чи запропоноване рішення.
Максим

2

Мені не сподобалась жодна з наведених вище відповідей, оскільки жодна з них не обробляє правильно комбінацію рядків / чисел, які можуть бути значеннями в переліках 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

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

Оскільки перелічення зі значеннями рядків відрізняються від тих, що мають значення чисел, краще відфільтрувати nonNumbers з рішення @ 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.keys(Enum)що дасть вам масив значень і ключів, у значеннях першого зрізу та в другому ключі, так чому ми не просто повертаємо другий зріз, цей код нижче працює для мене .

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

Цікаво, чому це було проти? У мене була така сама ідея. Хвилюється чи те, що TypeScript зміниться і більше не буде перераховувати однаково?
Тоні Сміт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.