Scegli una proprietà casuale da un oggetto Javascript


89

Supponi di avere un oggetto Javascript come {'cat': 'meow', 'dog': 'woof' ...} Esiste un modo più conciso per scegliere una proprietà casuale dall'oggetto rispetto a questo modo prolisso che mi è venuto in mente :

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;
        }
    }       
}

OP, per favore scegli di nuovo la risposta selezionata ... @kennytm ha risposto correttamente prima di altri. La risposta di David è semplicemente una cattiva codifica (anche se funziona)
vsync

Nota che la domanda e le risposte cercano effettivamente di restituire il valore di una proprietà di oggetto casuale, non una proprietà casuale come suggerirebbe il titolo della domanda.
kontur

Risposte:


182

La risposta scelta funzionerà bene. Tuttavia, questa risposta verrà eseguita più velocemente:

var randomProperty = function (obj) {
    var keys = Object.keys(obj);
    return obj[keys[ keys.length * Math.random() << 0]];
};

3
questo è meglio in quanto non utilizza un loop
Dominic

14
Ho fatto alcuni test e sembra che la risposta scelta funzioni bene e che la scelta della proprietà sia imparziale (contrariamente alle speculazioni tra le risposte); tuttavia, ho provato su un oggetto con 170.000 chiavi e la soluzione qui era circa due volte più veloce della soluzione scelta.
Dragonfly

8
<< 0 (bitshift a sinistra di 0) è un metodo abbreviato per scrivere Math.round ()?
SystemicPlural

4
Questo jsperf jsperf.com/random-object-property-selection confronta questa risposta e la risposta scelta. Questa risposta ha un rendimento migliore di 3 volte per oggetti più piccoli (100 proprietà). Oggetti più grandi (proprietà 100k) la differenza scende a 2x meglio.
Constablebrew

2
@MuhammadUmer - No. Math.random()restituisce un numero compreso nell'intervallo [0,1).
Yay295

74

Scegliere un elemento casuale da un flusso

function pickRandomProperty(obj) {
    var result;
    var count = 0;
    for (var prop in obj)
        if (Math.random() < 1/++count)
           result = prop;
    return result;
}

2
Lo standard ECMAScript dice qualcosa sulle proprietà che vengono sempre attraversate nello stesso ordine? Gli oggetti nella maggior parte delle implementazioni hanno un ordine stabile, ma il comportamento non è definito nelle specifiche: stackoverflow.com/questions/280713/…
Brendan Berg

4
Questo sembra avere un'inclinazione verso il primo elemento dell'oggetto. Non ho ancora capito perché!
Cole Gleason,

7
Questo non selezionerà mai la prima proprietà (Math.random è sempre <1) e dopo di che ogni numero avrà una probabilità di 0,5 di essere selezionato. Quindi 0,5 per la seconda proprietà, 0,25 per la 3a, 0,125 per la 4a ecc.
Plurale sistemico

4
Alcune correzioni: questa funzione può selezionare la prima proprietà. Alla prima iterazione, l'incremento del prefisso al conteggio fa sì che il lato destro dell'equazione valuti 1/1 == 1. Poiché Math.random è sempre compreso nell'intervallo [0,1) (da zero a uno, escluso uno), l'espressione restituisce true e viene selezionata la prima proprietà. Per quanto riguarda la distribuzione della selezione casuale, è uniforme. Con una proprietà c'è una probabilità del 100% che venga selezionata. Con due c'è una probabilità del 50% che sia selezionata. Con tre un 33,3%. E così via. Questa soluzione ha un ingombro di memoria minimo.
Constablebrew

3
@davidhadas Considera una sequenza di tre elementi. Il primo viene scelto con una probabilità di 1. Tuttavia, potrebbe essere sostituito (nota che non torniamo immediatamente!) Dal secondo elemento con una probabilità di 1/2. Il secondo elemento potrebbe a sua volta essere sostituito dal terzo elemento, con una probabilità di 1/3. Quindi otteniamo P (primo) = P (primo selezionato) * P (secondo non selezionato) * P (terzo non selezionato) = 1 * 1/2 * 2/3 = 1/3; P (secondo) = P (secondo selezionato) * P (terzo non selezionato) = 1/2 * 1/3 = 1/3; P (terzo) = P (terzo scelto) = 1/3.
Martin Törnwall

19

Non pensavo che nessuno degli esempi fosse abbastanza confuso, quindi ecco un esempio davvero difficile da leggere che fa la stessa cosa.

Modifica: probabilmente non dovresti farlo a meno che tu non voglia che i tuoi colleghi ti odino.

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

Spiegazione:

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

Mano lunga, meno confusa:

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

4
questa è in realtà la soluzione più ragionevole
Paweł

2
Questo mi piace di più, con spiegazioni e tutto e include anche un vero esempio di POJO. Ottime risposte, merita più voti positivi! Rende tutto molto più facile da capire!
Tigerrrrr

1
Soluzione migliore! Questo dovrebbe essere il più votato.
nilsoviani

15

Puoi semplicemente costruire un array di chiavi mentre cammini attraverso l'oggetto.

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

Quindi, scegli in modo casuale un elemento dalle chiavi:

return keys[keys.length * Math.random() << 0];

13
Object.keys è utile quivar keys = Object.keys(obj)
whadar

Dang << è molto più grazioso rispetto all'utilizzo di Math.floor (), probabilmente anche meno costoso. Devo davvero scendere e imparare a usare quegli operatori bit per bit.
Paul J

5
In questo caso l'utilizzo dell'operatore bit per bit è più probabile un hack, poiché ha bisogno di un intero come input, converte il numero. L'applicazione << 0a un numero intero non farà nulla. parseInt()farà lo stesso lavoro. Quindi niente da imparare qui tranne che scrivere codice meno comprensibile.
landunder l'

13

Se sei in grado di utilizzare le librerie, potresti scoprire che la libreria Lo-Dash JS ha molti metodi molto utili per questi casi. In questo caso, vai avanti e controlla _.sample().

(Nota la convenzione di Lo-Dash nomina l'oggetto libreria _. Non dimenticare di controllare l'installazione nella stessa pagina per configurarla per il tuo progetto.)

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

Nel tuo caso, vai avanti e usa:

_.sample({
    cat: 'meow',
    dog: 'woof',
    mouse: 'squeak'
});
// → "woof"

3

Se stai usando underscore.js puoi fare:

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

Extra:

Se hai bisogno di più proprietà casuali, aggiungi un numero:

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

Se hai bisogno di un nuovo oggetto con solo quelle proprietà casuali:

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

0

Un altro modo semplice per farlo sarebbe definire una funzione che applica la Math.random()funzione.

Questa funzione restituisce un numero intero casuale che va dal "min"

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

Quindi, estrai una "chiave" o un "valore" o "entrambi" dal tuo oggetto Javascript ogni volta che fornisci la funzione di cui sopra come parametro.

var randNum = getRandomArbitrary(0, 7);
var index = randNum;
return Object.key(index); // Returns a random key
return Object.values(index); //Returns the corresponding value.

Intendi Object.values ​​(someObject) [index]?
Bemmu

La variabile indice che ho usato per memorizzare il numero casuale generato è solo un contenitore, niente di speciale. Se non avessi memorizzato il numero generato in un'altra variabile, ogni istanza della funzione getRandomArbitrarygenererebbe un nuovo numero casuale ogni volta che viene chiamata.
Sushant Chaudhary

0

In un oggetto JSON devi inserire questo:

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;
  }
}

Quella funzione restituirà l'interno di una proprietà casuale.


0

È possibile utilizzare il codice seguente per selezionare una proprietà casuale da un oggetto 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

Sebbene questo codice possa rispondere alla domanda, fornire un contesto aggiuntivo su come e / o perché risolve il problema migliorerebbe il valore a lungo termine della risposta.
Nic3500
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.