How to get a random enum in TypeScript?

Viewed 20075

How to get a random item from an enumeration?

enum Colors {
  Red, Green, Blue
}

function getRandomColor(): Color {
  // return a random Color (Red, Green, Blue) here
}
14 Answers

After much inspiration from the other solutions, and the keyof keyword, here is a generic method that returns a typesafe random enum.

function randomEnum<T>(anEnum: T): T[keyof T] {
  const enumValues = Object.keys(anEnum)
    .map(n => Number.parseInt(n))
    .filter(n => !Number.isNaN(n)) as unknown as T[keyof T][]
  const randomIndex = Math.floor(Math.random() * enumValues.length)
  const randomEnumValue = enumValues[randomIndex]
  return randomEnumValue;
}

Use it like this:

interface MyEnum {X, Y, Z}
const myRandomValue = randomEnum(MyEnum) 

myRandomValue will be of type MyEnum.

Most of the above answers are returning the enum keys, but don't we really care about the enum values?

If you are using lodash, this is actually as simple as:

_.sample(Object.values(myEnum)) as MyEnum

The casting is unfortunately necessary as this returns a type of any. :(

If you're not using lodash, or you want this as its own function, we can still get a type-safe random enum by modifying @Steven Spungin's answer to look like:

function randomEnum<T>(anEnum: T): T[keyof T] {
  const enumValues = (Object.values(anEnum) as unknown) as T[keyof T][];
  const randomIndex = Math.floor(Math.random() * enumValues.length);
  return enumValues[randomIndex];
}

How about this, using Object.values from es2017 (not supported by IE and support in other browsers is more recent):

function randEnumValue<T>(enumObj: T): T[keyof T] {
  const enumValues = Object.values(enumObj);
  const index = Math.floor(Math.random() * enumValues.length);
  
  return enumValues[index];
}

If you need to support string or heterogeneous enums, I might suggest a function like this:

const randomEnumKey = enumeration => {
  const keys = Object.keys(enumeration)
    .filter(k => !(Math.abs(Number.parseInt(k)) + 1));
  const enumKey = keys[Math.floor(Math.random() * keys.length)];
  
  return enumKey;
};

To get a random value:

const randomEnumValue = (enumeration) => enumeration[randomEnumKey(enumeration)];

Or simply:

MyEnum[randomEnumKey(MyEnum)]

Example:

https://stackblitz.com/edit/typescript-random-enum

So no answer did work for me, I end up doing like this:

having the following enum

export enum Jokenpo {
  PAPER = 'PAPER',
  ROCK = 'ROCK',
  SCISSOR = 'SCISSOR',
}

to random select

const index= Math.floor(Math.random() * Object.keys(Jokenpo).length);
const value= Object.values(Jokenpo)[index];

return Jokenpo[value];

I'm just getting a random value from the enums and using it to convert to the real thing.

In addition to @sarink answer.

import _ from 'lodash';

export function getRandomValueFromEnum<E>(enumeration: { [s: string]: E } | ArrayLike<E>): E {
  return _.sample(Object.values(enumeration)) as E;
}

It is not perfect, but could try something like this..

function getRandomEnum(input: object): any {
  const inputTransform: { [key: string]: string } = input as { [key: string]: string };
  const keysToArray = Object.keys(inputTransform);
  const valuesToArray = keysToArray.map(key => inputTransform[key]);

  if (!isNaN(parseInt(keysToArray[0], 10))) {
    const len = keysToArray.length;

    while (keysToArray.length !== len / 2) {
      keysToArray.shift();
      valuesToArray.shift();
    }
  }

  const randIndex = Math.floor(keysToArray.length * Math.random());

  return valuesToArray[randIndex];
}

Tested with...

enum Names {
  foo,
  bar,
  baz,
  // foo = "fooZZZ",
  // bar = "barZZZ",
  // baz = "bazZZZ",
}

for (let i = 0; i < 10; i += 1) {
  const result = getRandomEnum(Names);
  if (result === Names.foo) console.log(`${result} is foo`);
  else if (result === Names.bar) console.log(`${result} is bar`);
  else if (result === Names.baz) console.log(`${result} is baz`);
}

Outputs...

0 is foo
0 is foo
2 is baz
0 is foo
2 is baz
2 is baz
1 is bar
1 is bar
2 is baz
1 is bar

or...

bazZZZ is baz
bazZZZ is baz
bazZZZ is baz
barZZZ is bar
bazZZZ is baz
barZZZ is bar
barZZZ is bar
fooZZZ is foo
barZZZ is bar
fooZZZ is foo
enum Colors {
  Red, Green, Blue
}

const keys = Object.keys(Colors)
const real_keys = keys.slice(keys.length / 2,keys.length)
const random =  real_keys[Math.floor(Math.random()*real_keys.length)]
console.log(random)

Tested on TypeScript 3.5.2 using ESNext. It's quite simple.

Bonus using namespace

enum Colors {
  Red,
  Green,
  Blue,
}
namespace Colors {
  // You need to minus the function inside your namespace
  // That's why I had - 1
  // Because instead of 0,1,2,red,green,blue = 6
  // It will be 0,1,2,red,green,blue,Random = 7
  export function Random(): any {
    const length = ((Object.keys(Colors).length - 1) / 2)
    return Colors[Math.floor(Math.random() * length / 2)]
  }
}

console.log(Colors.Random())

Here is a solution that worked for me in ES5:

function getRandomEnum<T extends object>(anEnum: T): T[keyof T] {
  const enumValues = Object.keys(anEnum)
    .filter(key => typeof anEnum[key as keyof typeof anEnum] === 'number')
    .map(key => key);
 
  const randomIndex = Math.floor(Math.random() * enumValues.length)
  
  return anEnum[randomIndex as keyof T];
}

Ease solution that works with all types of enums

function randomEnum<T extends Record<string, number | string>>(anEnum: T): T[keyof T] {
  const enumValues = getEnumValues(anEnum);
  const randomIndex = Math.floor(Math.random() * enumValues.length);
        
  return enumValues[randomIndex];
}

Used lodash, but function can be rewritten without it

function getEnumValues<T extends Record<string, number | string>>(anEnum: T): Array<T[keyof T]> {
  const enumClone = _.clone(anEnum);

  _.forEach(enumClone, (_value: number | string, key: string) => {
      if (!isNaN(Number(key))) {
        delete enumClone[key];
      }
    });

  return _.values(enumClone) as Array<T[keyof T]>;
}

enum Status { active, pending }
enum Type { active = 'active', pending = 'pending' } 
enum Mixed { active = 'active', pending = 1 }

randomEnum(Status) // (Return random between 0,1) 
randomEnum(Type) // (Return 'active' or 'pending' string) 
randomEnum(Mixed) // (Return 'active' or 1)

this is working for me.

enum Colors {
  Red = 'Red', 
  Green = 'Green', 
  Blue = 'Blue',
};

function getRandomInt(min: number, max: number) {
  return Math.floor(Math.random() * max) + min;
}

const colorKeys = Object.keys(Colors) as (keyof typeof Colors)[];

const randomColor = Colors[
  colorKeys[
    getRandomInt(0, colorKeys.length)
  ]
];
Related