Перечисление 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, так и в строку, и строит объекты в массив?

AnimaSola
источник
Перечисления - это реальные объекты, существующие во время выполнения. Таким образом, вы можете отменить отображение, сделав что-то вроде этого: GoalProgressMeasurements[GoalProgressMeasurements.Completed_Tasks]получить имя перечисления. Не знаю, поможет ли это.
Diullei
Не могли бы вы дать лучшее описание «из нашего 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));
user8363
источник
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 ]. Все ключи будут в первой половине массива, а все значения - во второй половине.

Джай Прак
источник
8
Имейте в виду, что это приведет к дублированию. Строковое значение и числовое значение для каждого элемента, то есть тип, (string | YourEnumType)[]который вам может не понадобиться в каждом случае.
Christian Ivicevic
Гарантируется ли, что первая половина будет ключами, а вторая половина - ценностями? любая ссылка?
Neekey
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

Diullei
источник
3
вам не нужно записывать значения по умолчанию = 2до тех пор, пока = 5- Все, что будет после, = 1автоматически станет +1.
sebilasse
8
Может и не нужно, но так выразительнее. Что делает его лучше ИМХО.
SubliemeSiem
4
просто примечание, что это не работает для перечислений строковых значений
Башар Али Лабади
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
вы должны отфильтровать ключи типа number 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.
- Отфильтровывает не числа (поскольку машинописный текст генерирует значения для обратного просмотра).
- Затем мы сопоставляем его с объектом массива, который нам нравится.

CMS
источник
Не совместим с IE с интерфейсом (не должен поддерживать, т.е. отличный ответ ... но я думаю, клиенты).
Надеюсь, что Babel
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 Он автоматически перейдет в топ, если количество голосов увеличится. Благодарность!
Гаурав Панвар,
Поскольку это не дает правильного результата, проверьте stackoverflow.com/a/57266281/3548345
walox
4

Сначала мы получаем массив ключей для этого перечисления. Затем с помощью функции map () преобразуем данные в желаемый формат. id получается из ключа, имя получается из enum по тому же ключу.

const converted = Object.keys(GoalProgressMeasurements).map(key => {
        return {
            id: GoalProgressMeasurements[key],
            name: key,
        };
    });
VaCool
источник
2
Добро пожаловать в stackoverflow. Отвечая на вопросы, неплохо объяснить, что делает ваш фрагмент кода. Для получения дополнительной информации см. Здесь: Как ответить
Дженсен,
Пожалуйста, подумайте о добавлении некоторых пояснений или деталей к своему ответу. Хотя он может ответить на вопрос, просто добавив фрагмент кода в качестве ответа, он не поможет OP или будущим членам сообщества понять проблему или предлагаемое решение.
Максим
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 сколько угодно раз.

MgSam
источник
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

Поскольку перечисления со значениями String отличаются от перечислений с числовыми значениями, лучше отфильтровать 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]);
    }

Islem Penywis
источник
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]
  );
}
Даниэль Кмак
источник
-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));
دانیال مدیری
источник