객체 배열에 대한 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

답변:


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 }에 회전 { Part1: 0, Part2: 1, 0: 'Part1', 1: 'Part2' } 한 후, 왜 때 console.log(Object.values(MyEnum))그냥 단지 0,1 인쇄?
— Juan José Ramírez

@ JuanJoséRamírez 어디서 볼 수 있습니까? 나를 위해 Object.values(MyEnum)평가["Part1", "Part2", 0, 1]
— user8363

방금 console.log(Object.values(MyEnum))구성 요소를 인쇄 했습니다. 나는 각도를 사용하고 있는데 그것이 관련되어 있는지 확실하지 않습니다. 나는 타이프에 경험이 아니에요
— 후안 호세 라미레즈에게

다른 TS 버전을 통해 동작이 변경 될 수 있습니까?
— Juan José Ramírez

3
나는 docs typescriptlang.org/docs/handbook/release-notes/…를 확인했는데 문자열 열거 형이 다른 동작을하는 것 같습니다. 역 매핑이 전혀 생성되지 않습니다. 내 코드에서는이 예제의 문자열이 아닌 문자열 열거 형을 사용했습니다.
— Juan José Ramírez

46

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

9
이렇게하면 중복이 생성됩니다. 각 요소의 문자열 값과 숫자 값, 즉 (string | YourEnumType)[]각 경우에 원하는 것이 아닌 유형 입니다.
— Christian Ivicevic

전반부가 키이고 후반부가 값이 될 것이라는 것이 보장됩니까? 어떤 참조?
— Neekey

1
Object.values()ES6의 일부가 아닙니다. ES2017의 일부입니다.
— atiyar

ES8과 같은 것은 없습니다. @atiyar가 말했듯이 ES2017이라고합니다.
— Heretic Monkey

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
-이후의 모든 항목 은 자동으로 +1이 = 2될 때까지 기본값을 쓸 필요가 없습니다 . = 5= 1
— sebilasse

9
그럴 필요는 없지만 더 표현력이 풍부합니다. 더 나은 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, ' ') }))
 }

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

작업을 수행하는 간단한 한 줄.

간단한 3 단계로 작업을 수행합니다
.-를 사용하여 키와 값의 조합을로드합니다 Object.entries.
-숫자가 아닌 항목을 필터링합니다 (typescript가 역방향 조회에 대한 값을 생성하므로).
-그런 다음 원하는 배열 객체에 매핑합니다.


IE와 프런트 엔드와 호환되지 않습니다 (즉, 지원하지 않아야하는 것은 훌륭한 답변이지만 고객이 생각합니다). 바벨을 증발하는 희망,하지만 난 그것을 확인하지 않은 때문에 다른 접근 방식을 고수
— TamusJRoyce을

string enum은 간단합니다.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 2010 년

4

위의 답변 중 어느 것도 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의 값을 원하는만큼 반복 할 수 있습니다.


4

먼저이 열거 형에 대한 키 배열을 얻습니다. 그런 다음 map () 함수를 사용하여 데이터를 원하는 형식으로 변환합니다. id는 키에서 가져오고 이름은 동일한 키로 enum에서 가져옵니다.

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

2
stackoverflow에 오신 것을 환영합니다. 질문에 답할 때 코드 조각의 기능을 설명하는 것이 좋습니다. 추가 정보를 보려면 여기를 참조하십시오 답변하는 방법
— Djensen


답변에 대한 설명이나 세부 사항을 추가해보십시오. 질문에 답할 수는 있지만 답으로 코드를 추가하는 것만으로도 OP 또는 미래의 커뮤니티 구성원이 문제 또는 제안 된 솔루션을 이해하는 데 도움이되지는 않습니다.
— Maxim

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

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가 변경되어 더 이상 enum을 동일한 방식으로 만들지 않을까 걱정할까요?
— Tony Smith

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]);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.