【问题标题】:How to copy and randomize copied elements in the same loop?如何在同一个循环中复制和随机化复制的元素?
【发布时间】:2017-04-29 07:49:39
【问题描述】:

我正在尝试复制数组中的偶数,并在同一个 for 循环中随机化新复制的元素:

var arr=[0,1,2,3,4,5,6,7,8];
var arr2=[];
for(var i=0;i<arr.length;i++){
  if(arr[i]%2==0){
    arr2.splice(Math.random()*arr2.length,0,arr[i]);
  }
}
document.write(arr2);

概念很简单:在新数组的随机位置插入新复制的元素,但是serval输出表明它不正确:

2,6,4,8,0
4,8,6,2,0
6,2,8,4,0

最后总是有 0。代码有什么问题?还是我的概念错了?

【问题讨论】:

  • 你不需要循环。您可以在 one 简洁的代码行中使用.filter(), .sort(), and Math.random()stackoverflow.com/questions/41134857/…
  • 获取偶数索引列表(简单)。 Shuffle that list randomly。然后在复制时使用这些索引访问源数组。这种方法保证每个元素都被复制,并且没有元素覆盖另一个元素。

标签: javascript arrays algorithm for-loop random


【解决方案1】:

这是一个 scopedShuffle 函数:

function scopedShuffle(a){
  var n = a.slice(), l = n.length;
  n.sort(function(b, c){
    return 0.5 - Math.floor(Math.random()*(l+1))/l;
  });
  return n;
}
van resultArray = scopedShuffle(yourArrayHere);

这里的关键是n 的作用域使用var n = a.slice(),这样原始数组就不会改变。

不过,我会使用构造函数:

function ShuffleMaster(inputArray){
  var a = inputArray;
  if(!(a instanceof Array)){
    throw new Error('inputArray must be an Array');
  }
  this.getInputArray = function(){
    return a;
  }
  this.setInputArray = function(inputArray){
    if(!(inputArray instanceof Array)){
      throw new Error('inputArray must be an Array');
    }
    a = inputArray;
    return this; // allows for daisy-chaining
  }
  this.shuffle = function(){
    var n = a.slice(), l = n.length;
    n.sort(function(b, c){
     return 0.5 - Math.floor(Math.random()*(l+1))/l;
    });
    return n;
  }
}
var myArray = [0, 7, 21, 26, 78, 756];
var sM = new ShuffleMaster(myArray);
console.log(sM.shuffle()); console.log(sM.shuffle());
console.log(sM.setInputArray([4, 5, 85, 46, 11]).shuffle());
console.log(sM.getInputArray());

这是一个简单的闭包风格,其中一个自执行函数的作用域是新数组,因此可以在下一次调用时访问它而无需传递原始参数...几乎像静态一样。

var scopedShuffle = (function(){
  var a;
  return function(inputArray){
    if(inputArray){
      a = inputArray.slice();
    }
    var n = a.slice(), l = n.length;
    n.sort(function(b, c){
      return 0.5 - Math.floor(Math.random()*(l+1))/l;
    });
    return n;
  }
})();
console.log(scopedShuffle(['a', 2, 4, 'g', 'apes']));
console.log(scopedShuffle()); console.log(scopedShuffle());
console.log(scopedShuffle(['learning', 'you', 'now', 'are', 'monkeys', 42, 'life', 'itself', 'the Universe']));
console.log(scopedShuffle()); console.log(scopedShuffle());

【讨论】:

    【解决方案2】:

    Math.random() 返回一个介于 0(包含)和 1(不包含)之间的数字。这意味着Math.random()*arr2.length 将始终小于arr2.length,因此不会将任何元素拼接到数组的末尾 - 除非在新数组为空时循环的第一次迭代,所以0 最终成为它的第一个也是唯一的元素。但随后的迭代会插入到末尾以外的任意位置,因此总是将0 推到右侧。

    您可以通过将Math.random()*arr2.length 更改为Math.random() * (arr2.length + 1) 来修复它。

    另请注意,.splice() 似乎并不关心您是否传递一个不是整数的数字,但为了简洁起见,我更喜欢使用 Math.floor(Math.random() * (arr2.length + 1))

    演示:

    function makeArray() {
      var arr=[0,1,2,3,4,5,6,7,8];
      var arr2=[];
      for(var i=0;i<arr.length;i++){
        if(arr[i]%2==0){
          arr2.splice(Math.floor(Math.random()*(arr2.length+1)), 0, arr[i]);
        }
      }
      return arr2;
    }
                      
    console.log(JSON.stringify(makeArray()));
    console.log(JSON.stringify(makeArray()));
    console.log(JSON.stringify(makeArray()));
    console.log(JSON.stringify(makeArray()));
    console.log(JSON.stringify(makeArray()));
    console.log(JSON.stringify(makeArray()));
    console.log(JSON.stringify(makeArray()));

    【讨论】:

      【解决方案3】:

      简单的解决方案:长链方法(slice,filter,sort)。

      先用.slice(0)克隆,再用.filter()过滤掉奇数,最后用.sortMath.random()随机洗牌。

      var arr2 = arr.filter(function(a) {
        return (a % 2) === 1; // Return true if a is even
      }).sort(function() {
        return 0.5 - Math.random();
      };
      

      查看示例:

      function shuffleIt() {
        var arr = document.getElementById('numberlist').value.split(' ');
        var arr2 = arr.filter(function(a){return a%2 === 0}).sort(function(){return 0.5-Math.random()});
        document.getElementById('output').innerHTML = arr2.join(',');
      }
      Type a list of numbers (separate with spaces)
      <input type="text" id="numberlist" value="1 2 3 4 5 6 7 8"/>
      <button onclick="shuffleIt()">Shuffle evens (click multiple times to get different results) </button>
      <br/>
      <b>Output:</b>
      <div id="output"></div>

      详细解释:

      1. .filter() 在数组的每个元素上调用传递给它的回调函数。如果函数返回 true,则保留元素;如果为假,则将其删除。 a % 2 === 0 是检查 a 是否为偶数的条件,因此会删除所有非偶数。此方法不会改变数组,而是返回一个新数组。

        filter() 方法创建一个新数组,其中包含所有通过所提供函数实现的测试的元素。

      https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

      1. .sort 对数组进行排序。它一次比较两个项目,使用从 ma 函数返回的数字的符号来决定哪个在另一个之前:

      如果 compareFunction(a, b) 小于 0,则将 a 排序到比 b 低的索引,即 a 排在第一位。 如果 compareFunction(a, b) [传入的回调函数] 返回 0,则 a 和 b 彼此保持不变,但对所有不同元素进行排序。注意:ECMAscript 标准不保证这种行为,因此并非所有浏览器(例如,至少可以追溯到 2003 年的 Mozilla 版本)都尊重这一点。 如果 compareFunction(a, b) 大于 0,则将 b 排序到比 a 低的索引。 当给定一对特定的元素 a 和 b 作为其两个参数时,compareFunction(a, b) 必须始终返回相同的值。如果返回不一致的结果,则排序顺序未定义。

      https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

      如您所见,返回数字的符号决定了哪个数组项先出现。 (0.5-Math.random()) 随机返回一个正数或负数(从技术上讲,0 可能是可能的,但不太可能),因此对数组进行随机排序。 注意:这个函数改变了数组,但是我们使用.filter()创建了一个新数组,所以原来的仍然没有改变。

      【讨论】:

        猜你喜欢
        • 2011-12-28
        • 2011-03-25
        • 2022-01-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-05-31
        相关资源
        最近更新 更多