【发布时间】:2023-03-06 22:23:01
【问题描述】:
如何从枚举中获取随机项?
enum Colors {
Red, Green, Blue
}
function getRandomColor(): Color {
// return a random Color (Red, Green, Blue) here
}
【问题讨论】:
标签: typescript enums
如何从枚举中获取随机项?
enum Colors {
Red, Green, Blue
}
function getRandomColor(): Color {
// return a random Color (Red, Green, Blue) here
}
【问题讨论】:
标签: typescript enums
在从其他解决方案获得大量灵感和keyof 关键字之后,这里是一个返回类型安全随机枚举的通用方法 .
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;
}
像这样使用它:
interface MyEnum {X, Y, Z}
const myRandomValue = randomEnum(MyEnum)
myRandomValue 将是 MyEnum 类型。
【讨论】:
Object.keys(anEnum).map(n => Number.parseInt(n))代替Object.values(anEnum).map(n => +n)吗?枚举的键都是字符串,而它的值是数字和字符串的混合(数字是实际的枚举值,字符串是它的键)。例如,给定 enum Bool { FALSE, TRUE },Object.keys(Bool) 将返回 ["0", "1", "FALSE", "TRUE"](所有字符串),但 Object.values(Bool) 将返回 ["FALSE", "TRUE", 0, 1](数字和字符串)。
上面的大部分答案都是返回枚举键,但我们真的不关心枚举值吗?
如果你使用的是 lodash,这其实很简单:
_.sample(Object.values(myEnum)) as MyEnum
不幸的是,强制转换是必要的,因为这会返回any 的类型。 :(
如果您不使用 lodash,或者您希望它作为自己的函数,我们仍然可以通过修改 @Steven Spungin 的答案来获得类型安全的随机枚举:
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];
}
【讨论】:
myEnum 是什么?
Object.values(myEnum) 发生了一些奇怪的事情——它为上面的 Colors 枚举返回了一个混合字符串和数字的数组,例如 ['Red', 'Green', 'Blue', 0, 1, 2]。 Object.keys 的作用相同,只是在这种情况下数字也是字符串:['Red', 'Green', 'Blue', '0', '1', '2']
如果您需要支持字符串或异构枚举,我可能会建议这样的函数:
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;
};
获取随机值:
const randomEnumValue = enumeration => enumeration[randomEnumKey(enumeration)];
或者简单地说:
MyEnum[randomEnumKey(MyEnum)]
例子:
【讨论】:
const randomEnumKey = <T>(enumeration: T): keyof T => { ... stackblitz.com/edit/typescript-random-enum-nfk4ab?file=index.ts
这是我能想到的最好的方法,但它看起来像一个 hack,并且取决于 TypeScript 中 enum 的实现,我不确定它是否会保持不变。
给定一个枚举,例如
enum Color { Red, Green, Blue }
如果我们console.log 它,我们会得到以下输出。
{
'0': 'Red',
'1': 'Green',
'2': 'Blue',
Red: 0,
Green: 1,
Blue: 2,
}
这意味着我们可以遍历该对象的键并仅获取数值,如下所示:
const enumValues = Object.keys(Color)
.map(n => Number.parseInt(n))
.filter(n => !Number.isNaN(n))
在我们的例子中,enumValues 现在是 [0, 1, 2]。我们现在只需要随机选择其中之一。有一个很好的函数实现,它返回一个random integer between two values,这正是我们随机选择索引所需要的。
const randomIndex = getRandomInt(0, enumValues.length)
现在我们只选择随机枚举值:
const randomEnumValue = enumValues[randomIndex]
【讨论】:
TS2322: Type 'number' is not assignable to type 'Color' 在你的最后一行
您可以找到以下代码来完成工作。
enum Colors {
Red, blue, pink, yellow, Orange
}
function getRandomColor(): string
{
var len = (Object.keys(Colors).length/2)-1; // returns the length
// calculate the random number
var item = (Math.floor(Math.random() * len) + 0);
return Colors[item];
}
【讨论】:
enum Colors { Red: 100, Blue }这样的枚举,这将失败
const item = Math.floor(Math.random() * Object.keys(Colors).length); const i2 = Object.keys(Colors)[item]; return Colors[i2];
这个怎么样,使用 es2017 中的Object.values(IE 不支持,其他浏览器支持较新):
function randEnumValue<T>(enumObj: T): T[keyof T] {
const enumValues = Object.values(enumObj);
const i = Math.floor(Math.random() * enumValues.length);
return enumValues[i];
}```
【讨论】:
除了@sarink 的回答。
import _ from 'lodash';
export function getRandomValueFromEnum<E>(enumeration: { [s: string]: E } | ArrayLike<E>): E {
return _.sample(Object.values(enumeration)) as E;
}
【讨论】:
它并不完美,但可以尝试这样的东西..
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];
}
经过测试...
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`);
}
输出...
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
或者...
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
【讨论】:
编辑:固定类型问题。
稍微整理一下@ShailendraSingh的答案,如果枚举在问题中定义,没有任何自定义索引,那么这是一个简单的方法:
enum Color {
Red, Green, Blue
}
function getRandomColor(): Color {
var key = Math.floor(Math.random() * Object.keys(Color).length/2);
return Color[key];
}
请注意,此处的返回类型Color(根据问题中的要求)实际上是枚举索引而不是颜色名称。
【讨论】:
TS2322: Type 'number' is not assignable to type 'Color'
enum Colors {
Red, Green, Blue
}
let keys = Object.keys(Colors)
let real_keys = keys.slice(keys.length / 2,keys.length)
let random = real_keys[Math.floor(Math.random()*real_keys.length)]
console.log(random)
使用 esnext 在 typescript 3.5.2 上测试。很简单。
使用命名空间的奖励
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 {
let length = ((Object.keys(Colors).length - 1) / 2)
return Colors[Math.floor(Math.random() * length / 2)]
}
}
console.log(Colors.Random())
【讨论】:
这里有一个在 es5 中对我有用的解决方案:
function getRandomEnum<T extends object>(anEnum: T): T[keyof T] {
let enumValues = Object.keys(anEnum)
.filter(key => typeof anEnum[key as keyof typeof anEnum] === 'number')
.map(key => key);
let randomIndex = Math.floor(Math.random() * enumValues.length)
return anEnum[randomIndex as keyof T];
}
【讨论】:
适用于所有类型枚举的简单解决方案
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];
}
使用lodash,但没有它可以重写函数
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)
【讨论】:
所以没有答案对我有用,我最终这样做了:
具有以下枚举
export enum Jokenpo {
PAPER = 'PAPER',
ROCK = 'ROCK',
SCISSOR = 'SCISSOR',
}
随机选择
const random = Math.floor(Math.random() * Object.keys(Jokenpo).length);
const selected = Object.values(Jokenpo)[random];
return Jokenpo[selected];
我只是从枚举中获取一个随机值并使用它来转换为真实的值。
【讨论】: