【问题标题】:Shuffling array properties in JavaScript在 JavaScript 中洗牌数组属性
【发布时间】:2011-05-22 20:41:43
【问题描述】:

我有一个这样的数据字典:

var data = {
    'text1': 1,
    'text2': 2,
    'text3': 3,
    ...
    'text20': 20
];

我需要随机选择这些键,然后随机播放它的值。在示例中,它应该这样写:

> console.log(choose(data, 5));
[ { key: 'text15', value: 8 },
{ key: 'text6', value: 3 },
{ key: 'text3', value: 15 },
{ key: 'text19', value: 6 },
{ key: 'text8', value: 19 } ]

现在我将键提取到另一个数组中并按 Math.random() 排序,但我坚持交换值,因为没有键应该具有与最初相同的值。

您将如何在这里交换键/值?

谢谢

【问题讨论】:

  • 是否有更换?例如'text8' 的值可以出现两次吗?
  • 不,不能有重复值。
  • “任何键都不应该具有与最初相同的值”是什么意思?同样按关键函数Math.random() 排序不会给你一个等概率的排列;这取决于使用的排序算法。您必须通过Math.random() 获取 N 个密钥,存储这些密钥,然后按它们排序。
  • 因为我试图将第一个与最后一个的值交换,第二个与最后一个交换,但当然这会使中间的值具有相同的值。
  • 好吧,如果您不需要随机化,只需洗牌,将每个值向任一方向移动一步,然后取消移动/推送

标签: javascript random swap shuffle


【解决方案1】:

我使用underscore.js 整理了一个可能的解决方案,以简化以跨浏览器方式遍历对象和数组:

var data = {
    text1: 1,
    text2: 2,
    text3: 3,
    text4: 4,
    text5: 5,
    text6: 6,
    text7: 7,
    text8: 8,
    text9: 9,
    text10: 10
};

function choose(data, num)
{
    var keys = _.sortBy(
                    _.keys(data),
                    function(k)
                    {
                        return (Math.random() * 3) - 1;
                    }
                ),
        results = [],
        k1, k2;
    if (num > keys.length) {
        throw new Error('Impossible to retrieve more values than exist');
    }
    while (results.length < num) {
        k1 = k2 || keys.pop();
        k2 = keys.pop();
        results.push({key:k1, value: data[k2]});
    }
    return results;
}

console.log(choose(data, 5));

这不一定是最佳方法,但似乎可以满足您的要求。我首先抓住所有的钥匙并随机排序。然后,我循环遍历随机键,使用一个键和以下键值创建一个新对象。这样,您将始终得到与每个键关联的不同值。如果您需要它在传递给函数的 num 的值 == 数据中的键数时工作,那么您必须添加更多代码 - 我将把它作为练习留给读者:)

你可以在 jsfiddle 上玩一下这段代码:

http://jsfiddle.net/zVyQW/1/

【讨论】:

  • 与我的解决方案非常相似,但 ||技巧比增加两个计数器更好。我给你积分。
  • 如果你想要 n 个项目,这将失败,因为你需要 n+1 个键来获取第 n 个值。
  • @Gumbo - 如您所见,如果您阅读了我在代码下写的内容,那留给读者作为(相当微不足道的)练习......
  • 好吧,我没看过。但它可以很容易地修复:k2 = keys.pop() || results[0].key;
【解决方案2】:

你可以这样做:

  • 在两个数组names和values中收集名称和对应值
  • 相互独立地洗牌两个数组
  • 取两个数组的前 n 项并将它们组合起来

这是一个示例实现:

Array.prototype.shuffle = function() {
    for (var i=this.length-1, j, tmp; i>0; i--) {
        j = Math.round(Math.random()*i);
        tmp = this[i], this[i] = this[j], this[j] = tmp;
    }
    return this;
};

function choose(data, number) {
    var names = [], values = [], pick = [];
    for (var name in data) {
        if (data.hasOwnProperty(name)) {
            names.push(name);
            values.push(data[name]);
        }
    }
    names = names.shuffle(), values = values.shuffle();
    for (var i=Math.min(number >>> 0, names.length-1); i>=0; i--) {
        pick.push({key: names[i], value: values[i]});
    }
    return pick;
}

【讨论】:

    【解决方案3】:

    这个问题得到回答已经有一段时间了,但我正在研究洗牌,发现以下是迄今为止最快的实现,具有均匀随机分布。

    它很快,因为它在每次迭代中只调用一次 Math.random,其余的都是通过属性访问来完成的。它不会修改数组,只是重新分配值。

      function shuffle(a) {
        var t, j, i=a.length, rand=Math.random;
    
        // For each element in the array, swap it with a random
        // element (which might be itself)
        while (i--) {
          k = rand()*(i+1)|0;
          t = a[k];
          a[k]=a[i];
          a[i]=t;
        }
        return a;
      }
    

    【讨论】:

      【解决方案4】:

      它使用三个函数的组合(包括Array shuffle原型方法)。

      完整代码如下:

      var obj = {
          "red":"RED",
          "blue":"BLUE",
          "green":"GREEN",
          "yellow":"YELLOW",
          "purple":"PURPLE"
      };
      
      Array.prototype.shuffle = function(){
          for (var i = 0; i < this.length; i++){
              var a = this[i];
              var b = Math.floor(Math.random() * this.length);
              this[i] = this[b];
              this[b] = a;
          }
      }
      
      obj = shuffleProperties(obj); // run shuffle
      
      function shuffleProperties(obj) {
          var new_obj = {};
          var keys = getKeys(obj);
          keys.shuffle();
          for (var key in keys){
              if (key == "shuffle") continue; // skip our prototype method
              new_obj[keys[key]] = obj[keys[key]];
          }
          return new_obj;
      }
      
      function getKeys(obj){
          var arr = new Array();
          for (var key in obj)
              arr.push(key);
          return arr;
      }
      
      
      for(key in obj){
         alert(key);
      }  
      

      Check all post, 最好的问候。

      【讨论】:

        【解决方案5】:

        使用随机化一组离散值的 random 实现,例如 Math.rand seen here。对于每个索引,随机化Math.rand(index, length-1)得到一个随机索引列表,所有索引的位置都会改变。

        【讨论】:

          猜你喜欢
          • 2013-06-28
          • 1970-01-01
          • 2018-04-21
          • 2015-04-15
          • 1970-01-01
          • 1970-01-01
          • 2015-12-11
          • 1970-01-01
          相关资源
          最近更新 更多