[javascript] TypeScript enum to object array

I have an enum defined this way:

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

However, I'd like it to be represented as an object array/list from our API like below:

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

Is there are easy and native way to do this or do I have to build a function that casts the enum to both an int and a string, and build the objects into an array?

This question is related to javascript arrays typescript enums casting

The answer is


Easy Solution. You can use the following function to convert your Enum to an array of objects.

 buildGoalProgressMeasurementsArray(): Object[] {

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

If you needed to strip that underscore off, we could use regex as follows:

buildGoalProgressMeasurementsArray(): Object[] {

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

A tricky bit is that TypeScript will 'double' map the enum in the emitted object, so it can be accessed both by key and value.

enum MyEnum {
    Part1 = 0,
    Part2 = 1
}

will be emitted as

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

So you should filter the object first before mapping. So @Diullei 's solution has the right answer. Here is my implementation:

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

Use it like this:

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

console.log(ToArray(GoalProgressMeasurements));

Enums are real objects that exist at runtime. So you are able to reverse the mapping doing something like this:

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

Based on that you can use the following code:

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

Reference: https://www.typescriptlang.org/docs/handbook/enums.html


Since enums with Strings values differ from the ones that have number values it is better to filter nonNumbers from @user8363 solution.

Here is how you can get values from enum either strings, numbers of mixed:

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


If you are using ES8

For this case only it will work perfectly fine. It will give you value array of the given enum.

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

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

You will get colorValueArray like this [ 'WHITE', 'BLACK', 'BLUE', 0, 1, 3 ]. All the keys will be in first half of the array and all the values in second half.

Even this kind of enum will work fine

enum Operation {
    READ,
    WRITE,
    EXECUTE
}

But this solution will not work for Heterogeneous enums like this

enum BooleanLikeHeterogeneousEnum {
  No = 0,
  Yes = "YES",
}

There is a simple solution, So when you run Object.keys(Enum) that gonna give you a Array of Values and Keys, in first slice Values and in the second one keys, so why we don't just return the second slice, this code below works for me.

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

I use

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

A simple 1 line that does the job.

It does the job in 3 simple steps
- Loads the combination of keys & values using Object.entries.
- Filters out the non numbers (since typescript generates the values for reverse lookup).
- Then we map it to the array object we like.


Simply this will return an array of enum values:

 Object.values(myEnum);

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

Here's the simple function with correct typing I use

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

Usage example:

enum Colors {
  Red = 1,
  Blue = 2,
}
enumToArray(Colors)

You can do that in this way:

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

And you can use it like this:

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;

You can extend the GoalProgressMeasurement with additional properties of the object as you need. I'm using this approach for every enumeration that should be an object containing more then a value.


_x000D_
_x000D_
enum GoalProgressMeasurements {_x000D_
    Percentage = 1,_x000D_
    Numeric_Target = 2,_x000D_
    Completed_Tasks = 3,_x000D_
    Average_Milestone_Progress = 4,_x000D_
    Not_Measured = 5_x000D_
}_x000D_
    _x000D_
const array = []_x000D_
    _x000D_
for (const [key, value] of Object.entries(GoalProgressMeasurements)) {_x000D_
    if (!Number.isNaN(Number(key))) {_x000D_
        continue;_x000D_
    }_x000D_
_x000D_
    array.push({ id: value, name: key.replace('_', '') });_x000D_
}_x000D_
_x000D_
console.log(array);
_x000D_
_x000D_
_x000D_


I didn't like any of the above answers because none of them correctly handle the mixture of strings/numbers that can be values in TypeScript enums.

The following function follows the semantics of TypeScript enums to give a proper Map of keys to values. From there, getting an array of objects or just the keys or just the values is trivial.

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

Example Usage:

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

I'll also point out that the OP is thinking of enums backwards. The "key" in the enum is technically on the left hand side and the value is on the right hand side. TypeScript allows you to repeat the values on the RHS as much as you'd like.


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

First we get an array of keys for this enum. Then, using the map () function, we convert the data to the desired format. id is obtained from the key, name is obtained from enum by the same key.

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

I don't think the order can be guaranteed, otherwise it would be easy enough to slice the second half of Object.entries result and map from there.

The only (very minor) issues with the answers above is that

  • there is a lot of unnecessary type conversion between string and number.
  • the entries are iterated twice when a single iteration is just as clean and effective.
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
}

I'm surprised in a TypeScript thread no one gave valid TypeScript function with typing supported. Here's variation of @user8363 solution:

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

Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to arrays

PHP array value passes to next row Use NSInteger as array index How do I show a message in the foreach loop? Objects are not valid as a React child. If you meant to render a collection of children, use an array instead Iterating over arrays in Python 3 Best way to "push" into C# array Sort Array of object by object field in Angular 6 Checking for duplicate strings in JavaScript array what does numpy ndarray shape do? How to round a numpy array?

Examples related to typescript

TS1086: An accessor cannot be declared in ambient context Element implicitly has an 'any' type because expression of type 'string' can't be used to index Angular @ViewChild() error: Expected 2 arguments, but got 1 Typescript: No index signature with a parameter of type 'string' was found on type '{ "A": string; } Understanding esModuleInterop in tsconfig file How can I solve the error 'TS2532: Object is possibly 'undefined'? Typescript: Type 'string | undefined' is not assignable to type 'string' Typescript: Type X is missing the following properties from type Y length, pop, push, concat, and 26 more. [2740] Can't perform a React state update on an unmounted component TypeScript and React - children type?

Examples related to enums

Enums in Javascript with ES6 Check if value exists in enum in TypeScript Why Python 3.6.1 throws AttributeError: module 'enum' has no attribute 'IntFlag'? TypeScript enum to object array How can I loop through enum values for display in radio buttons? How to get all values from python enum class? Get enum values as List of String in Java 8 enum to string in modern C++11 / C++14 / C++17 and future C++20 Implementing Singleton with an Enum (in Java) Swift: Convert enum value to String?

Examples related to casting

Subtracting 1 day from a timestamp date Cast object to interface in TypeScript TypeScript enum to object array Casting a number to a string in TypeScript Hive cast string to date dd-MM-yyyy Casting int to bool in C/C++ Swift double to string No function matches the given name and argument types C convert floating point to int PostgreSQL : cast string to date DD/MM/YYYY