Перемешивание свойств массива в JavaScript - PullRequest
1 голос
/ 23 мая 2011

У меня есть словарь данных, как это:

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 (), но я застрял при обмене значениями, потому что ни один ключ не должен иметь то же значение, которое изначально имело.

Как бы вы поменяли здесь ключи / значения?

Спасибо

Ответы [ 5 ]

1 голос
/ 23 мая 2011

Вы можете сделать это:

  • собрать имена и соответствующие значения в двух массивах имена и значения
  • перетасовать оба массива независимодруг друга
  • взять первые 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;
}
1 голос
/ 23 мая 2011

Я собрал возможное решение, используя 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/

0 голосов
/ 03 сентября 2013

Используется комбинация из трех функций (включая метод-прототип 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);
}  

Проверьте все сообщения ,С наилучшими пожеланиями.

0 голосов
/ 09 июня 2011

Прошло некоторое время с тех пор, как на этот вопрос был дан ответ, но я работал над тасованием и обнаружил, что следующее является самой быстрой реализацией с равномерно случайным распределением.

Это быстро, потому что он делает только один вызов 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;
  }
0 голосов
/ 23 мая 2011

Используйте реализацию random, которая рандомизирует дискретный набор значений, например Math.and, видимый здесь .Для каждого индекса рандомизируйте Math.rand(index, length-1), чтобы получить список случайных индексов, расположение всех индексов изменится.

...