Reputation: 77
I have a data dictionary like this:
var data = {
'text1': 1,
'text2': 2,
'text3': 3,
...
'text20': 20
];
I need to pick a random selection of those keys and then shuffle it's values. In the example, it should write something like this:
> 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 } ]
For now I'm extracting the keys into another array and sorting by Math.random() but I'm stuck at swaping the values because no key should have the same value it initially had.
How would you swap key/values here?
Thanks
Upvotes: 1
Views: 1506
Reputation: 7536
Use an implementation of random that randomizes a discrete set of values, such as Math.rand seen here. For each index, randomize Math.rand(index, length-1)
to get a list of random indexes, the location off all indices will change.
Upvotes: 0
Reputation: 4335
It uses a combination of three functions (including the Array shuffle prototype method).
Here is the complete code:
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, Best Regards.
Upvotes: 0
Reputation: 147413
Been a while since this was answered, but I was working on shuffling and found the following to be by far the fastest implementation with an evenly random distribution.
It's fast because it only makes one call to Math.random on each iteration, all the rest is done by property access. It doesn't modify the array, just reassigns values.
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;
}
Upvotes: 0
Reputation: 655309
You could do this:
Here’s an example implementation:
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;
}
Upvotes: 1
Reputation: 3214
I put together a possible solution using underscore.js to simplify traversing the object and arrays in a cross browser manner:
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));
This isn't necessarily an optimal approach but it seems to meet your requirements. I first grab all of the keys and sort them randomly. I then loop through the random keys creating a new object with one key and the following keys value. That way you'll always end up with a different value associated with each key. If you need it to work when the value of num passed in to the function == the number of keys in the data then you'll have to add a little more code - I'll leave that as an exercise for the reader :)
You can have a play with this code on jsfiddle:
Upvotes: 1