Choisissez une propriété aléatoire dans un objet Javascript

89

Supposons que vous ayez un objet Javascript comme {'cat': 'meow', 'dog': 'woof' ...} Y a-t-il un moyen plus concis de choisir une propriété aléatoire de l'objet que cette longue méthode que j'ai trouvée :

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
la source
OP, veuillez resélectionner votre réponse sélectionnée ... @kennytm y a répondu correctement avant les autres. La réponse de David est tout simplement un mauvais codage (bien que cela fonctionne)
vsync
Notez que la question et les réponses cherchent en fait à renvoyer la valeur d'une propriété d'objet aléatoire, et non une propriété aléatoire comme le suggère le titre de la question.
kontur

Réponses:

182

La réponse choisie fonctionnera bien. Cependant, cette réponse fonctionnera plus rapidement:

var randomProperty = function (obj) {
    var keys = Object.keys(obj);
    return obj[keys[ keys.length * Math.random() << 0]];
};
Lawrence Whiteside
la source
3
c'est mieux car il n'utilise pas de boucle
Dominic
14
J'ai fait quelques tests, et il semble que la réponse choisie fonctionne très bien et que le choix de la propriété est impartial (contrairement aux spéculations parmi les réponses); cependant, j'ai testé sur un objet avec 170 000 clés et la solution ici était environ deux fois plus rapide que la solution choisie.
Dragonfly
8
<< 0 (décalage de bits vers la gauche de 0) est-il une méthode abrégée pour écrire Math.round ()?
SystemicPlural
4
Ce jsperf jsperf.com/random-object-property-selection compare cette réponse et la réponse choisie. Cette réponse fonctionne mieux de 3x pour les objets plus petits (100 propriétés). Objets plus grands (propriétés de 100k), la différence diminue deux fois mieux
Constablebrew
2
@MuhammadUmer - No. Math.random()renvoie un nombre compris entre [0,1).
Yay295
74

Choisir un élément aléatoire dans un flux

function pickRandomProperty(obj) {
    var result;
    var count = 0;
    for (var prop in obj)
        if (Math.random() < 1/++count)
           result = prop;
    return result;
}
David Leonard
la source
2
Le standard ECMAScript dit-il quelque chose sur les propriétés toujours parcourues dans le même ordre? Les objets dans la plupart des implémentations ont un ordre stable, mais le comportement n'est pas défini dans la spécification: stackoverflow.com/questions/280713/…
Brendan Berg
4
Cela semble avoir un biais vers le premier élément de l'objet. Je n'ai pas encore compris pourquoi!
Cole Gleason
7
Cela ne sélectionnera jamais la première propriété (Math.random est toujours <1) et après cela, chaque nombre aura 0,5 chance d'être sélectionné. Donc 0,5 pour la deuxième propriété, 0,25 pour la 3e, 0,125 pour la 4e etc.
SystemicPlural
4
Quelques corrections: Cette fonction permet de sélectionner la première propriété. Lors de la première itération, l'incrément du préfixe sur count fait évaluer le côté droit de l'équation à 1/1 == 1. Puisque Math.random est toujours dans l'intervalle [0,1) (zéro à un, à l'exclusion d'un), l'expression prend la valeur true et la première propriété est sélectionnée. En ce qui concerne la distribution de la sélection aléatoire, elle est uniforme. Avec une propriété, il y a 100% de chances qu'elle soit sélectionnée. Avec deux, il y a 50% de chances que l'un ou l'autre soit sélectionné. Avec trois un 33,3%. Etc. Cette solution a une empreinte mémoire minimale.
Constablebrew
3
@davidhadas Considérez une séquence de trois éléments. Le premier est choisi avec une probabilité de 1. Cependant, il peut être remplacé (notez que nous ne revenons pas immédiatement!) Par le deuxième élément avec une probabilité de 1/2. Le deuxième élément pourrait à son tour être remplacé par le troisième élément, avec une probabilité de 1/3. Nous obtenons donc P (premier) = P (premier choisi) * P (deuxième non choisi) * P (troisième non choisi) = 1 * 1/2 * 2/3 = 1/3; P (deuxième) = P (deuxième choisi) * P (troisième non choisi) = 1/2 * 1/3 = 1/3; P (troisième) = P (troisième choisi) = 1/3.
Martin Törnwall
19

Je ne pensais pas qu'aucun des exemples était assez déroutant, alors voici un exemple vraiment difficile à lire faisant la même chose.

Edit: Vous ne devriez probablement pas faire cela à moins que vous ne vouliez que vos collègues vous détestent.

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)]]);

Explication:

// 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)]]

Main longue, moins déroutante:

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
la source
4
c'est en fait la solution la plus raisonnable
Paweł
2
Je l'aime le plus, avec des explications et tout et comprend également un exemple réel de POJO. Excellentes réponses, mérite plus de votes positifs! Rend tout tellement plus facile à comprendre!
Tigerrrrr
1
Meilleure solution! Cela devrait être le plus voté.
nilsoviani
15

Vous pouvez simplement créer un tableau de clés tout en parcourant l'objet.

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

Ensuite, choisissez au hasard un élément parmi les clés:

return keys[keys.length * Math.random() << 0];
KennyTM
la source
13
Object.keys est utile icivar keys = Object.keys(obj)
whadar
Dang << est tellement plus gracieux que d'utiliser Math.floor (), probablement moins cher aussi. Je dois vraiment descendre et apprendre à utiliser ces opérateurs au niveau du bit.
Paul J
5
Dans ce cas, l'utilisation de l'opérateur au niveau du bit est plus probablement un hack, car il a besoin d'un entier comme entrée, il convertit le nombre. Appliquer << 0à un entier ne fera rien. parseInt()fera le même travail. Donc rien à apprendre ici si ce n'est d'écrire du code moins compréhensible.
landunder
13

Si vous êtes capable d'utiliser des bibliothèques, vous constaterez peut-être que la bibliothèque Lo-Dash JS a beaucoup de méthodes très utiles pour de tels cas. Dans ce cas, allez-y et vérifiez _.sample().

(Notez que la convention Lo-Dash nomme l'objet de bibliothèque _. N'oubliez pas de vérifier l'installation dans la même page pour la configurer pour votre projet.)

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

Dans votre cas, allez-y et utilisez:

_.sample({
    cat: 'meow',
    dog: 'woof',
    mouse: 'squeak'
});
// → "woof"
Égoïste
la source
3

Si vous utilisez underscore.js, vous pouvez faire:

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

Supplémentaire:

Si vous avez besoin de plusieurs propriétés aléatoires, ajoutez un nombre:

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

Si vous avez besoin d'un nouvel objet avec uniquement ces propriétés aléatoires:

const props = _.sample(Object.keys(animals), 3);
const newObject = _.pick(animals, (val, key) => props.indexOf(key) > -1);
Nelu
la source
0

Une autre façon simple de le faire serait de définir une fonction qui s'applique Math.random() fonction.

Cette fonction renvoie un entier aléatoire qui va du 'min'

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

Ensuite, extrayez une «clé» ou une «valeur» ou «les deux» de votre objet Javascript à chaque fois que vous fournissez la fonction ci-dessus comme paramètre.

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 Chaudhary
la source
Voulez-vous dire Object.values ​​(someObject) [index]?
Bemmu le
La variable d' index que j'ai utilisée pour stocker le nombre aléatoire généré n'est qu'un conteneur, rien de spécial. Si je n'avais pas stocké le nombre généré dans une autre variable, chaque instance de la fonction getRandomArbitrarygénérerait un nouveau nombre aléatoire à chaque fois qu'elle est appelée.
Sushant Chaudhary
0

Dans un objet JSON, vous devez placer ceci:

var object={
  "Random": function() {
    var result;
    var count = 0;
    for (var prop in this){
      if (Math.random() < 1 / ++count&&prop!="Random"){
        result = this[prop];
      }
    }
    return result;
  }
}

Cette fonction renverra l'intérieur d'une propriété aléatoire.

Sybsuper
la source
0

Vous pouvez utiliser le code suivant pour sélectionner une propriété aléatoire à partir d'un objet JavaScript:

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
Lol Super
la source
Bien que ce code puisse répondre à la question, fournir un contexte supplémentaire sur la façon et / ou pourquoi il résout le problème améliorerait la valeur à long terme de la réponse.
Nic3500