从JavaScript数组中获取随机值

1233

考虑以下内容:

var myArray = ['January', 'February', 'March'];    

如何使用JavaScript从该数组中随机选择一个值?

28个回答

2273

这是一个简单的一行代码:

const randomElement = array[Math.floor(Math.random() * array.length)];

例如:

const months = ["January", "February", "March", "April", "May", "June", "July"];

const random = Math.floor(Math.random() * months.length);
console.log(random, months[random]);


10
@SapphireSun 这是正确的。请注意 Math.floor(Math.random(...)) 调用,它会向下取整。 - ashes999
56
啊,我学到了新知识。我之前讨论的是值恰好等于1的情况,但根据W3Schools上的信息,Math.random生成的数是从0(包括)到1(不包括)之间的。我的错误。 - SapphireSun
14
我可能错了,但我记得 var rand = myArray[Math.random() * myArray.length>>0] 稍微快一点。 - user3546284
5
我更喜欢这个变量:var rand = myArray[Math.random() * myArray.length | 0] - np42
请注意,只要您传递一个空数组,此函数将返回 undefined。在这种情况下抛出异常可能会有所帮助。 - Froxx

128

如果您的项目中已经包含了underscorelodash,您可以使用_.sample

// will return one item randomly from the array
_.sample(['January', 'February', 'March']);

如果您需要随机获取多个项目,可以将其作为第二个参数传递给下划线:
// will return two items randomly from the array using underscore
_.sample(['January', 'February', 'March'], 2);

或者使用 lodash 中的 _.sampleSize 方法:
// will return two items randomly from the array using lodash
_.sampleSize(['January', 'February', 'March'], 2);

3
在使用 TypeScript 时,请注意,对于字符串数组,返回类型将会是 "string | undefined" 而不是 "string"。 - Stephan Schielke

61

你可以考虑在数组原型上定义一个函数,以便创建一个方法 [].sample(),该方法返回一个随机元素。

首先,在你的代码中插入以下代码片段来定义原型函数:

Array.prototype.sample = function(){
  return this[Math.floor(Math.random()*this.length)];
}

然后,要从数组中随机抽取一个元素,只需调用.sample()

[1,2,3,4].sample() //=> a random element

我将这些代码片段发布到公共领域,在 CC0 1.0 许可证 的条款下。


1
这是做什么的? - Ken Sharp
4
它允许你在任何数组上调用 .sample() 方法来获取一个随机项。 - Ben Aubin
31
应避免扩展本地对象类型。我已删除我的答案,因为它被投票很多但会促进不良实践。关于这个问题的更多讨论请参见例如 https://dev59.com/T2Yr5IYBdhLWcg3wAFg0 和 https://eslint.org/docs/rules/no-extend-native。 - Markus Amalthea Magnuson
4
这是一个很好的观点。然而,在原型上定义自定义方法不一定是个问题,特别是如果这样做得少并且不在库代码中进行。原型提供了一种替代方案,它相当漂亮(在我个人看来),并公开了有时被忽视但有趣的语言部分,至少在适度使用时如此。对于几乎所有应用程序代码,这不会引起问题,因此这只是个口味问题。 - Ben Aubin
请查看我的答案,了解如何扩展实例而不是原型。 - Manngo

49

~~Math.Floor()快得多,因此在使用 UI 元素生成输出时进行性能优化时,~~获胜。更多信息

var rand = myArray[~~(Math.random() * myArray.length)];

但是,如果您知道该数组将拥有数百万个元素,那么您可能需要重新考虑位运算符和Math.Floor()之间的选择,因为位运算符在处理大数时会表现出奇怪的行为。请参见下面的示例以了解输出。

var number = Math.floor(14444323231.2); // => 14444323231
var number = 14444323231.2 | 0; // => 1559421343

1
链接已失效,但是这篇文章很有趣,我以后会比使用 Math.floor 更多。 - alistair
8
使用“按位取反”运算符虽然更快,但不够易读,因此您需要选择对您更重要的内容。 - Maciej Urbański
双波浪线 - 很有趣...我学到了新东西。 - Gel
对于那些想要理解它的含义的人来说,~是按位取反运算符,它可以颠倒二进制数中的10。与所有按位运算符一样,它首先将数字转换为32位整数,这正是你真正想要的。使用~~可以将其还原为原始的32位整数。 - Manngo
关于 Math.floor(),所有函数都有一个开销,其中包括存储和恢复原始状态。通常,优化编译器会寻找机会将代码原地复制以避免这种开销,但是对于像 JavaScript 这样的动态语言来说,预测更加困难。 - Manngo

42

The shortest version:

var myArray = ['January', 'February', 'March']; 
var rand = myArray[(Math.random() * myArray.length) | 0]
console.log(rand)


6
| 0 的作用是将数值转换为32位整数。 - Ken Sharp
6
它会将浮点数转换为整数,与Math.floor相同。 - foxiris
5
在 JavaScript 中,| 0 本身是一种位运算,不会执行任何操作,但是浮点数在进行任何位运算之前都会被转换为整数。因此,这有点类似于 +'' 不会真正执行任何操作,但可以用来将值转换为字符串。请注意,翻译时应保留原文意思,并使其更加通俗易懂。 - dshepherd
3
这不同于 Math.floor 但在这里是正确的做法。它是一个运算符,所以如果只考虑速度,它比 Math.floor 更快,因为在运行代码的任何时候,某些代码可能会执行 Math.floor = someOtherFunction,但对于 '|' 它们无法执行相同操作。另一方面,关于 Math.floor| 的区别,请尝试 Math.floor(-1.5)-1.5 | 0。顺便说一下,您不需要使用括号,因为 | 的优先级非常低。 - gman

23

假设你想选择一个与上次不同的随机项(虽然不是真正的随机,但仍然是常见需求)...

/**
 * Return a random element from an array that is
 * different than `last` (as long as the array has > 1 items). 
 * Return null if the array is empty.
*/
function getRandomDifferent(arr, last = undefined) {
  if (arr.length === 0) {
    return null;
  } else if (arr.length === 1) {
    return arr[0];
  } else {
    let num = 0;
    do {
      num = Math.floor(Math.random() * arr.length);
    } while (arr[num] === last);
    return arr[num];
  }
}

实现方法如下:

const arr = [1,2,3];
const r1 = getRandomDifferent(arr);
const r2 = getRandomDifferent(arr, r1); // r2 is different than r1.

13

如果您有固定的值(比如一个月份名称列表)并且想要一个一行解决方案

var result = ['January', 'February', 'March'][Math.floor(Math.random() * 3)]

数组的第二部分是一个访问操作,如在JavaScript中[5,6,8,7][1,2]为什么等于8中所述。


11
这种代码是一种糟糕且有害的做法,绝不能在生产中使用。它的可读性很低,而且有一个硬编码的数组长度。改变数组输入的人可能会忘记编辑最后硬编码的长度。 - Seagull
1
@Seagull OP从未要求特定的环境。此外,该评论毫无意义,因为它可以适用于本问题中几乎所有的答案 ;) - I.G. Pascual
但是大多数人通过谷歌搜索到这个问题,并且他们可能会在其他场景中使用该解决方案,而不仅仅是原始的 OP。 - Seagull
@Seagull 哈哈,人们可以自由决定使用哪种方法,我不是《Clean Code》指南的常见问题解答! - I.G. Pascual
2
我喜欢它的可读性,我自己也得出了相同的解决方案。 - Rauli Rajande
1
我完全同意,这样硬编码数组长度的方式非常糟糕,会导致维护问题。 - Will59

13
许多提供的解决方案都会向特定数组添加一个方法,从而限制其仅适用于该数组。这个解决方案是可重用的代码,适用于任何数组,并且可以使其类型安全。

TypeScript

export function randChoice<T>(arr: Array<T>): T {
  return arr[Math.floor(Math.random() * arr.length)]
}

JavaScript

function randChoice(arr) {
  return arr[Math.floor(Math.random() * arr.length)]
}

13
如果你想要像Pascual的解决方案一样把它写在一行上,另一个解决方案是使用ES6的find函数来编写它(基于这个事实,随机选择其中一个项的概率为1/n):

var item = ['A', 'B', 'C', 'D'].find((_, i, ar) => Math.random() < 1 / (ar.length - i));
console.log(item);

如果仅用于测试目的且没有充分理由只保存在单独变量中,则可以采用这种方法。否则,其他答案(floor(random()*length和使用单独函数)是您的选择。


8
Faker.js有许多实用函数可用于生成随机测试数据。在测试套件的上下文中,这是一个不错的选择:

Faker.js

const faker = require('faker');
faker.helpers.arrayElement(['January', 'February', 'March']);

正如评论者所提到的,通常不应该在生产代码中使用这个库。


17
对于这样一个简单的问题,添加整个库的依赖是不必要的,并且会增加代码的臃肿。如果需要,您可以建议使用Faker中选择随机数组元素的实际方法。 - Pixxl
2
通常像“简单问题”这样的问题都可以通过提供简单解决方案的库来解决,这些问题已经被数百人面对过。这些库通常是强大且经过良好调试的,并处理了我们不想重新实现的各种注意事项。这通常是我建议使用库的情况。 - smonff
那么你应该只需从库中复制那个方法并将其放入一个工具文件中。 - Rick Bross
关于库在被发送到Web浏览器时应该根据页面大小进行成本/效益评估的建议是明智的,我完全同意将Faker.js发送到浏览器是荒谬的。然而,问题没有提及使用哪个JS运行时。对于基于NodeJS的运行时,更重的依赖关系是完全合理的,这也是我在Cucumber JS测试套件中使用Faker.js的情况。 - Nathan
1
我正在处理已经使用faker.js的测试,所以这对我来说是一个有用的答案。 - Chris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接