从JavaScript数组中获取随机值

Sar*_*rah 714 javascript

考虑:

var myArray = ['January', 'February', 'March'];    
Run Code Online (Sandbox Code Playgroud)

如何使用JavaScript从此数组中选择随机值?

Jac*_*kin 1340

var rand = myArray[Math.floor(Math.random() * myArray.length)];
Run Code Online (Sandbox Code Playgroud)

  • 啊,我学到了新东西.我正在讨论它完全等于1的情况,但显然(根据W3Schools),Math.random在0和0之间.我的错. (31认同)
  • 我可能错了,但我记得`var rand = myArray [Math.random()*myArray.length >> 0]`稍快一些 (13认同)
  • @SapphireSun这是对的.注意`Math.floor(Math.random(...))`调用,向下舍入. (8认同)
  • 以防万一,如果你已经在使用lodash,你可以使用_.sample(数组) (5认同)
  • 我更喜欢这种变体:`var rand = myArray [Math.random()* myArray.length | 0]` (2认同)

Mar*_*son 85

我发现将原型函数添加到Array类更简单:

Array.prototype.randomElement = function () {
    return this[Math.floor(Math.random() * this.length)]
}
Run Code Online (Sandbox Code Playgroud)

现在我只需键入以下内容即可获得随机数组元素:

var myRandomElement = myArray.randomElement()
Run Code Online (Sandbox Code Playgroud)

请注意,这将为所有数组添加一个属性,因此如果您使用循环使用for..in,则应使用.hasOwnProperty():

for (var prop in myArray) {
    if (myArray.hasOwnProperty(prop)) {
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

(这对你来说可能是也可能不是麻烦.)

  • 向你不拥有的对象添加/删除方法被认为是一种非常糟糕的做法.以prototype.js为例. (52认同)
  • @Richards不,不是,除非你正在建立一个图书馆.这是一个意见. (27认同)
  • 如果你这样做,你将在你的所有数组上有一个额外的属性,弄乱你的`for(i in array)`循环...... (9认同)
  • 我也做了同样的事情,尽管有时候在使用Array.prototype时要小心,特别是在使用第三方模块时. (6认同)
  • 您可以[使属性不可枚举](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#Enumerable_attribute).但是,我仍然认为修补原型是不好的做法.Function-bind(::)是另一种选择:https://github.com/zenparsing/es-function-bind.@ MarkusAmaltheaMagnuson的解决方案(在迭代时特别排除了属性)在我看来太难以记住,并且可能会引入微妙的错误. (4认同)
  • @BenjaminGruenbaum编程中的很多东西都是意见,这被广泛认为是一种不好的做法。其他开发人员会怀疑“ randomElement”的来源。这是模棱两可的。这是一种不好的做法,也正是为什么他们是它的拖延规则,`no-extend-native` (3认同)

Bre*_*Nee 64

如果您已经在项目中包含下划线lodash,则可以使用_.sample.

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

如果您需要随机获得多个项目,可以将其作为下划线中的第二个参数传递:

// will return two items randomly from the array using underscore
_.sample(['January', 'February', 'March'], 2);
Run Code Online (Sandbox Code Playgroud)

或者使用_.sampleSizelodash中的方法:

// will return two items randomly from the array using lodash
_.sampleSize(['January', 'February', 'March'], 2);
Run Code Online (Sandbox Code Playgroud)

  • 使用打字稿时:请注意,给定字符串数组时,返回类型将为“string | undefined”,而不是“string”。 (3认同)

Ben*_*bin 19

原型方法

如果您计划大量获取随机值,则可能需要为其定义函数.

首先,将它放在代码中的某处:

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

现在:

[1,2,3,4].sample() //=> a random element
Run Code Online (Sandbox Code Playgroud)

代码根据CC0 1.0许可条款发布到公共领域.

  • 应避免扩展本机对象类型。我删除了我的答案,因为它得到了很多支持,但却宣扬了不好的做法。有关此问题的更多讨论,请参阅 /sf/ask/982392631/ 和 https://eslint.org/docs/rules/no-扩展原生 (15认同)
  • @MarkusAmaltheaMagnuson 这是一个很好的观点。然而,在原型上定义自定义方法不一定是个问题,特别是如果在库代码之外谨慎地这样做的话。该原型提供了一个相当漂亮的替代解决方案(在我的主观意见中),并且暴露了该语言中有时被忽视但有趣的部分,至少在谨慎使用时是这样。对于几乎所有应用程序代码,这不会导致问题,因此这取决于品味问题。 (4认同)
  • @KenSharp它允许您在任何数组上调用`.sample()`以获取随机项 (2认同)

Cra*_*Tim 17

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

在@Markus的答案基础上,我们可以添加另一个原型函数:

Array.prototype.randomDiffElement = function(last) {
   if (this.length == 0) {
      return;
   } else if (this.length == 1) {
      return this[0];
   } else {
      var num = 0;
      do {
         num = Math.floor(Math.random() * this.length);
      } while (this[num] == last);
      return this[num];
   }
}
Run Code Online (Sandbox Code Playgroud)

并执行如下:

var myRandomDiffElement = myArray.randomDiffElement(lastRandomElement)
Run Code Online (Sandbox Code Playgroud)


Ank*_*oni 14

~~速度要快得多Math.Floor(),因此在使用UI元素生成输出时进行性能优化,~~赢得游戏.更多信息

var rand = myArray[~~(Math.random() * myArray.length)];
Run Code Online (Sandbox Code Playgroud)

但是,如果你知道数组将有比你想要在Bitwise运算符之间重新考虑的数百万个元素,并且Math.Floor()因为按位运算符在大数字上表现得很奇怪.请参阅以下使用输出解释的示例.更多信息

var number = Math.floor(14444323231.2); // => 14444323231
var number = 14444323231.2 | 0; // => 1559421343
Run Code Online (Sandbox Code Playgroud)

  • 使用“按位非”运算符虽然速度更快,但可读性较差,因此您必须选择对您更重要的内容 (3认同)

Ton*_*gan 14

许多提供的解决方案都向特定数组添加了一个方法,这限制了它只能用于该数组。该解决方案是可重用的代码,适用于任何数组,并且可以确保类型安全。

打字稿

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

JavaScript

function randChoice(arr) {
  return arr[Math.floor(Math.random() * arr.length)]
}
Run Code Online (Sandbox Code Playgroud)


fox*_*ris 10

最短的版本:

var myArray = ['January', 'February', 'March']; 
var rand = myArray[(Math.random() * myArray.length) | 0]
Run Code Online (Sandbox Code Playgroud)

  • `| 是什么意思?0` 做吗? (6认同)
  • 它会将 Float 转换为 Int,与 Math.floor 相同。 (4认同)
  • @KenSharp` | 0`本身是一个不做任何操作的按位操作,但在javascript浮点数[在任何按位操作之前转换为整数](https://huytd.github.io/bitwise-float-int-trick.html).所以它就像`+''`实际上没有做任何事情,但可以用来将事物转换成字符串. (4认同)
  • 它与“Math.floor”不同,但在这里这样做是正确的。它是一个运算符,因此它比“Math.floor”更快,因为在运行某些代码时的任何时候都可以执行“Math.floor = someOtherFunction”,而它们不能对“|”执行相同的操作。另一方面,至于 `Math.floor` 和 `|` 不同,请尝试 `Math.floor(-1.5)` 与 `-1.5 |` 0`。顺便说一下,你不需要括号。`|` 的优先级非常低。 (3认同)

Ste*_*han 10

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

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

将该方法用于测试目的,如果有充分的理由不将数组仅保存在单独的变量中。否则其他答案(floor(random()*length并使用单独的函数)是您要走的路。


I.G*_*ual 7

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

var result = ['January', 'February', 'March'][Math.floor(Math.random() * 3)]
Run Code Online (Sandbox Code Playgroud)

数组的第二部分是一个访问操作,如JavaScript中的为什么[5,6,8,7] [1,2] = 8?

  • 此类代码是一种糟糕且有害的做法。它不应该在生产中使用。它具有低可读性,并且具有硬编码的数组长度。更改数组输入的人最终可能会忘记编辑硬编码的长度。 (6认同)
  • 我喜欢它的可读性,我自己也得出了同样的解决方案 (2认同)

Man*_*ngo 7

如果您需要多次获取随机项目,那么显然您会使用函数。一种方法是使该函数成为 的方法Array.prototype,但这通常会让您因篡改内置原型而受到谴责。

\n

但是,您可以将该方法添加到特定数组本身:

\n
var months = [\'January\', \'February\', \'March\'];\nmonths.random = function() {\n    return this[Math.floor(Math.random()*this.length)];\n};\n
Run Code Online (Sandbox Code Playgroud)\n

这样您就可以months.random()随意使用,而不会干扰通用的Array.prototype.

\n

与任何随机函数一样,您面临着连续获得相同值的风险。如果您不想\xe2\x80\x99 那样,您将需要使用另一个属性跟踪先前的值:

\n
months.random=function() {\n    var random;\n    while((random=this[Math.floor(Math.random()*this.length)]) == this.previous);\n    this.previous=random;\n    return random;\n};\n
Run Code Online (Sandbox Code Playgroud)\n

如果你经常做这种事情,并且你不想篡改Array.prototype,你可以这样做:

\n
function randomValue() {\n    return this[Math.floor(Math.random()*this.length)];\n}\n\nvar data = [ \xe2\x80\xa6 ];\nvar moreData = [ \xe2\x80\xa6 ];\n\ndata.random=randomValue;\nmoreData.random=randomValue;\n
Run Code Online (Sandbox Code Playgroud)\n


Sea*_*ull 6

编辑数组原型可能有害。这是一个简单的功能来完成这项工作。

function getArrayRandomElement (arr) {
  if (arr && arr.length) {
    return arr[Math.floor(Math.random() * arr.length)];
  }
  // The undefined will be returned if the empty array was passed
}
Run Code Online (Sandbox Code Playgroud)

用法:

// Example 1
var item = getArrayRandomElement(['January', 'February', 'March']);

// Example 2
var myArray = ['January', 'February', 'March'];
var item = getArrayRandomElement(myArray);
Run Code Online (Sandbox Code Playgroud)


Nat*_*han 5

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

const Faker = require('faker');
Faker.random.arrayElement(['January', 'February', 'March']);
Run Code Online (Sandbox Code Playgroud)

正如评论者提到的那样,通常不应在生产代码中使用此库。

  • 对于像这样的简单问题,不需要为整个库添加依赖项,这会使代码膨胀。如果有的话,您可能会从`Faker`中推荐实际方法,该方法选择一个随机数组元素。 (7认同)
  • 像这样的“简单问题”通常是由图书馆解决的,这些图书馆为数百人已经不得不面对的问题提供了一个简单的解决方案。这些库通常很强大并且调试良好,并且可以处理我们不想重新实现的各种警告。这通常是我建议使用库的情况。 (2认同)
  • 我正在研究已经使用 faker.js 的测试,所以这对我来说是一个有用的答案。 (2认同)

Kam*_*ski 5

要获得加密强度强的随机项形式数组,请使用

let rndItem = a=> a[rnd()*a.length|0];
let rnd = ()=> crypto.getRandomValues(new Uint32Array(1))[0]/2**32;

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

console.log( rndItem(myArray) )
Run Code Online (Sandbox Code Playgroud)