118

Suppose you have a Javascript object like:

{cat: 'meow', dog: 'woof', snake: 'hiss'}

Is there a more concise way to pick a random property from the object than this long winded way I came up with:

function pickRandomProperty(obj) {
    var prop, len = 0, randomPos, pos = 0;
    for (prop in obj) {
        if (obj.hasOwnProperty(prop)) {
            len += 1;
        }
    }
    randomPos = Math.floor(Math.random() * len);
    for (prop in obj) {
        if (obj.hasOwnProperty(prop)) {
            if (pos === randomPos) {
                return prop;
            }
            pos += 1;
        }
    }       
}
Bemmu
  • 17,849
  • 16
  • 76
  • 93
  • 1
    Note that the question and answers actually look for returning _the value_ of a random object property, not a random property as the question title would suggest. – kontur Oct 19 '20 at 11:20

9 Answers9

237

The chosen answer will work well. However, this answer will run faster:

var randomProperty = function (obj) {
    var keys = Object.keys(obj);
    return obj[keys[ keys.length * Math.random() << 0]];
};
Popnoodles
  • 28,090
  • 2
  • 45
  • 53
Lawrence Whiteside
  • 2,594
  • 1
  • 16
  • 8
  • 3
    this is better as it doesn't use a loop – Dominic Mar 28 '14 at 11:02
  • 16
    I did some testing, and it appears that the chosen answer works just fine and that the choice of property is unbiased (contrary to speculations among the responses); however, I tested on an object with 170,000 keys and the solution here was around twice as fast as the chosen solution. – Dragonfly May 04 '14 at 18:33
  • Another advantage to this method is that if you need to pick several random properties you can first compute "keys" which is the time-consuming part of the function. You can then grab random properties almost instantaneously. – Dragonfly May 04 '14 at 18:44
  • 11
    Is << 0 (bitshift to the left by 0) a shorthand method of writing Math.round()? – SystemicPlural Aug 29 '14 at 14:55
  • 1
    @David Leonard's answer is correct, however the solution requires walking through the entire loop, O(n). This answer is also O(n) ([see this SO question](http://stackoverflow.com/questions/7716812/object-keys-complexity)), but does appear to better optimized. – Constablebrew Oct 12 '14 at 10:25
  • Great function! For those that need to support older browsers, go with this [polyfill](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys#Polyfill). – dvlden Oct 16 '14 at 17:52
  • 4
    This jsperf http://jsperf.com/random-object-property-selection benchmarks this answer and the chosen answer. This answer performs better by 3x for smaller objects (100 properties). Larger objects (100k properties) the difference drops to 2x better. – Constablebrew Mar 13 '15 at 06:43
  • 1
    shouldn't it be `keys.length -1 `, because array starts at 0 – Muhammad Umer Dec 11 '15 at 22:54
  • 2
    @MuhammadUmer - No. `Math.random()` returns a number in the range [0,1). – Yay295 Dec 12 '15 at 02:15
  • yes that's what i am saying, array with 3 elements, will have length of 3. And when Math.random returns 1 then you will have 3 x1 = 3.. which means arr[3] will be possibility, when arr[2] will be the highest element – Muhammad Umer Dec 12 '15 at 07:04
  • 3
    @SystemicPlural It's more as a shorthand of `parseInt(keys.length * Math.random(), 0)` – Alserda May 11 '17 at 09:29
  • O(1) should be possible if you could somehow access the underlying hash table and do a random select. If you get multiple items in the bucket you can just randomly select from there too. Hope they include something like this in the next version of JavaScript. – martian17 Dec 02 '20 at 16:24
  • @MuhammadUmer That's not a problem as Math.random() will never return exactly 1. – Matt Kieran Feb 04 '21 at 22:02
  • I personally think this answer's function should be called `randomValue()`, since it returns the value, right? If you'd like to return the property, use `return keys[ keys.length * Math.random() << 0];` instead. Thank you for sending me in the right direction! – tyler.frankenstein Jan 16 '22 at 04:50
76

Picking a random element from a stream

function pickRandomProperty(obj) {
    var result;
    var count = 0;
    for (var prop in obj)
        if (Math.random() < 1/++count)
           result = prop;
    return result;
}
Dominic
  • 62,658
  • 20
  • 139
  • 163
David Leonard
  • 1,694
  • 1
  • 17
  • 14
  • 2
    Does the ECMAScript standard say anything about the properties always being traversed in the same order? Objects in most implementations have stable ordering, but the behavior is undefined in the spec: http://stackoverflow.com/questions/280713/elements-order-for-in-loop-in-javascript/280861#280861 – Brendan Berg Sep 20 '10 at 18:46
  • I believe this is guaranteed to work as long as we hit each property exactly once while iterating (and iteration would be useless if that weren't guaranteed!) – Jeff Hemphill May 02 '13 at 12:54
  • 4
    This seems to have a skew towards the first element in the object. I haven't figured out why yet! – Cole Gleason Jan 11 '14 at 08:14
  • 7
    This will never select the first property (Math.random is always < 1) and after that each number will have a 0.5 chance of being selected. So 0.5 for the second property, 0.25 for the 3rd, 0.125 for the 4th etc. – SystemicPlural Aug 29 '14 at 14:41
  • @ColeGleason there is no bias, this function is uniformly distributed. – Constablebrew Oct 12 '14 at 10:31
  • Does not work for functions that are within object. It returns the string. – dvlden Oct 16 '14 at 17:37
  • 4
    Some corrections: This function can select the first property. On the first iteration, the prefix increment on count makes the right hand side of the equation evaluate to 1/1 == 1. Since Math.random is always in the range [0,1) (zero to one, excluding one), the expression evaluates to true and the first property is selected. As far as the distribution of the random selection goes, it is uniform. With one property there is a 100% chance it will be selected. With two there is a 50% chance either will be selected. With three a 33.3%. And so on. This solution has a minimal memory footprint. – Constablebrew Mar 13 '15 at 06:51
  • Does not offer equal oppertunity to all + does not gurentee to select anything - may return ane empty result – davidhadas Apr 29 '16 at 16:22
  • 3
    @davidhadas Consider a sequence of three elements. The first one is picked with a probability of 1. However, it might be replaced (notice that we don't return immediately!) by the second element with a probability of 1/2. The second element might in turn be replaced by the third element, with a probability of 1/3. So we get P(first) = P(first picked) * P(second not picked) * P(third not picked) = 1 * 1/2 * 2/3 = 1/3; P(second) = P(second picked) * P(third not picked) = 1/2 * 1/3 = 1/3; P(third) = P(third picked) = 1/3. – Martin Törnwall Aug 01 '16 at 08:42
  • I don't understand what the complaint is about this. I [tested it](https://codepen.io/ashleedawg/pen/gOOjNgp) with millions of loops and confirmed that the distribution is random. Perhaps the tendency towards number 1 that others claim to have seen, can be explained by [Benford's Law](https://en.wikipedia.org/wiki/Benford%27s_law) or a similar hypothesis.... As a JS novice, I can't speak to the "quality of code", but the fact is, this function produces the result that it's supposed to. – ashleedawg Nov 12 '19 at 06:25
27

I didn't think any of the examples were confusing enough, so here's a really hard to read example doing the same thing.

Edit: You probably shouldn't do this unless you want your coworkers to hate you.

var animals = {
    'cat': 'meow',
    'dog': 'woof',
    'cow': 'moo',
    'sheep': 'baaah',
    'bird': 'tweet'
};

// Random Key
console.log(Object.keys(animals)[Math.floor(Math.random()*Object.keys(animals).length)]);

// Random Value
console.log(animals[Object.keys(animals)[Math.floor(Math.random()*Object.keys(animals).length)]]);

Explanation:

// gets an array of keys in the animals object.
Object.keys(animals) 

// This is a number between 0 and the length of the number of keys in the animals object
Math.floor(Math.random()*Object.keys(animals).length)

// Thus this will return a random key
// Object.keys(animals)[0], Object.keys(animals)[1], etc
Object.keys(animals)[Math.floor(Math.random()*Object.keys(animals).length)]

// Then of course you can use the random key to get a random value
// animals['cat'], animals['dog'], animals['cow'], etc
animals[Object.keys(animals)[Math.floor(Math.random()*Object.keys(animals).length)]]

Long hand, less confusing:

var animalArray  = Object.keys(animals);
var randomNumber = Math.random();
var animalIndex  = Math.floor(randomNumber * animalArray.length);

var randomKey    = animalArray[animalIndex];
// This will course this will return the value of the randomKey
// instead of a fresh random value
var randomValue  = animals[randomKey]; 
Paul J
  • 1,489
  • 1
  • 17
  • 19
  • 2
    I like this the most, with explanations and everything and also includes an actual example POJO. Great Answers, deserves more upvotes! Just makes everything so much easier to understand! – Tigerrrrr Nov 12 '19 at 18:39
17

If you are capable of using libraries, you may find that Lo-Dash JS library has lots of very useful methods for such cases. In this case, go ahead and check _.sample().

(Note Lo-Dash convention is naming the library object _. Don't forget to check installation in the same page to set it up for your project.)

_.sample([1, 2, 3, 4]);
// → 2

In your case, go ahead and use:

_.sample({
    cat: 'meow',
    dog: 'woof',
    mouse: 'squeak'
});
// → "woof"
Selfish
  • 6,023
  • 4
  • 44
  • 63
15

You can just build an array of keys while walking through the object.

var keys = [];
for (var prop in obj) {
    if (obj.hasOwnProperty(prop)) {
        keys.push(prop);
    }
}

Then, randomly pick an element from the keys:

return keys[keys.length * Math.random() << 0];
HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • 14
    Object.keys is useful here `var keys = Object.keys(obj)` – whadar Jan 06 '13 at 15:11
  • 1
    Dang << is so much more graceful than using Math.floor(), probably less expensive too. I've really gotta get down and learn how to use those bitwise operators. – Paul J Nov 21 '16 at 18:20
  • 5
    In this case the usage of the bitwise operator is more likely a hack, because it needs an integer as an input, it converts the number. Applying `<< 0` to an integer will do nothing. `parseInt()` will do the same job. So nothing to learn here except of writing less understandable code. – landunder Feb 08 '17 at 14:13
6

If you're using underscore.js you can do:

_.sample(Object.keys(animals));

Extra:

If you need multiple random properties add a number:

_.sample(Object.keys(animals), 3);

If you need a new object with only those random properties:

const props = _.sample(Object.keys(animals), 3);
const newObject = _.pick(animals, (val, key) => props.indexOf(key) > -1);
Nelu
  • 16,644
  • 10
  • 80
  • 88
  • 1
    `_.sample` does not appear to take a second argument. If you want e.g. two distinct properties you must remove the first and sample again. – Rick Mohr Aug 28 '23 at 12:56
2

You can use the following code to pick a random property from a JavaScript object:

function randomobj(obj) {
var objkeys = Object.keys(obj)
return objkeys[Math.floor(Math.random() * objkeys.length)]
}
var example = {foo:"bar",hi:"hello"}
var randomval = example[randomobj(example)] // will return to value
// do something
Grant Miller
  • 27,532
  • 16
  • 147
  • 165
Lol Super
  • 93
  • 1
  • 2
  • While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. – Nic3500 Sep 05 '18 at 00:33
0

Another simple way to do this would be defining a function that applies Math.random() function.

This function returns a random integer that ranges from the 'min'

function getRandomArbitrary(min, max) {
  return Math.floor(Math.random() * (max - min) + min);
}

Then, extract either a 'key' or a 'value' or 'both' from your Javascript object each time you supply the above function as a parameter.

var randNum = getRandomArbitrary(0, 7);
var index = randNum;
return Object.key(index); // Returns a random key
return Object.values(index); //Returns the corresponding value.
Sushant Rajbanshi
  • 1,985
  • 1
  • 19
  • 20
  • Do you mean Object.values(someObject)[index]? – Bemmu Jan 02 '18 at 08:06
  • The **index** variable that I've used to store the generated **random** number is just a container, nothing special. Had I not stored the generated number into another variable then each instance of the function `getRandomArbitrary` would generate a new random number each time it's called. – Sushant Rajbanshi Jan 02 '18 at 10:52
0

A lot of great answers here, so let me just try to spread the awareness of the bitwise NOT (~) operator in its double-trouble variant (which I'm pretty sure I learned about on StackOverflow, anways).

Typically, you'd pick a random number from one to ten like this:

Math.floor(Math.random()*10) + 1

But bitwise operation means rounding gets done faster, so the following implementation has the potential to be noticeably more performant, assuming you're doing enough truckloads of these operations:

~~(Math.random()*10) + 1