Escolha uma propriedade aleatória de um objeto Javascript

88

Suponha que você tenha um objeto Javascript como {'cat': 'meow', 'dog': 'woof' ...} Existe uma maneira mais concisa de escolher uma propriedade aleatória do objeto do que esta maneira prolixa que criei :

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
fonte
OP, por favor, escolha novamente sua resposta selecionada ... @kennytm respondeu corretamente antes dos outros. A resposta de David é simplesmente codificação ruim (embora funcione)
vsync

Respostas:

179

A resposta escolhida funcionará bem. No entanto, esta resposta será executada mais rapidamente:

var randomProperty = function (obj) {
    var keys = Object.keys(obj);
    return obj[keys[ keys.length * Math.random() << 0]];
};
Lawrence Whiteside
fonte
3
isso é melhor porque não usa um loop
Dominic
13
Fiz alguns testes e parece que a resposta escolhida funciona bem e que a escolha da propriedade é imparcial (ao contrário das especulações entre as respostas); no entanto, testei em um objeto com 170.000 chaves e a solução aqui foi cerca de duas vezes mais rápida que a solução escolhida.
Dragonfly
8
<< 0 (bitshift para a esquerda em 0) é um método abreviado de escrever Math.round ()?
SystemicPlural
4
Este jsperf jsperf.com/random-object-property-selection compara esta resposta e a resposta escolhida. Essa resposta tem um desempenho 3x melhor para objetos menores (100 propriedades). Objetos maiores (100k propriedades) a diferença cai para 2x melhor.
Constablebrew
2
@MuhammadUmer - No. Math.random()retorna um número no intervalo [0,1).
Yay295
74

Escolher um elemento aleatório de um fluxo

function pickRandomProperty(obj) {
    var result;
    var count = 0;
    for (var prop in obj)
        if (Math.random() < 1/++count)
           result = prop;
    return result;
}
David Leonard
fonte
2
O padrão ECMAScript diz algo sobre as propriedades sempre serem percorridas na mesma ordem? Os objetos na maioria das implementações têm ordem estável, mas o comportamento é indefinido na especificação: stackoverflow.com/questions/280713/…
Brendan Berg
4
Isso parece ter uma inclinação em direção ao primeiro elemento do objeto. Eu não descobri o porquê ainda!
Cole Gleason
7
Isso nunca selecionará a primeira propriedade (Math.random é sempre <1) e, depois disso, cada número terá 0,5 chance de ser selecionado. Portanto, 0,5 para a segunda propriedade, 0,25 para a 3ª, 0,125 para a 4ª etc.
SystemicPlural
4
Algumas correções: Esta função permite selecionar a primeira propriedade. Na primeira iteração, o incremento do prefixo na contagem faz com que o lado direito da equação seja avaliado como 1/1 == 1. Como Math.random está sempre no intervalo [0,1) (zero a um, excluindo um), a expressão é avaliada como verdadeira e a primeira propriedade é selecionada. No que diz respeito à distribuição da seleção aleatória, ela é uniforme. Com uma propriedade, há 100% de chance de ela ser selecionada. Com dois, há 50% de chance de qualquer um ser selecionado. Com três a 33,3%. E assim por diante. Esta solução ocupa um espaço mínimo de memória.
Constablebrew
3
@davidhadas Considere uma sequência de três elementos. O primeiro é escolhido com uma probabilidade de 1. No entanto, ele pode ser substituído (observe que não retornamos imediatamente!) Pelo segundo elemento com uma probabilidade de 1/2. O segundo elemento pode, por sua vez, ser substituído pelo terceiro elemento, com uma probabilidade de 1/3. Assim, obtemos P (primeiro) = P (primeiro escolhido) * P (segundo não escolhido) * P (terceiro não escolhido) = 1 * 1/2 * 2/3 = 1/3; P (segundo) = P (segundo escolhido) * P (terceiro não escolhido) = 1/2 * 1/3 = 1/3; P (terceiro) = P (terceiro escolhido) = 1/3.
Martin Törnwall
17

Não achei que nenhum dos exemplos fosse confuso o suficiente, então aqui está um exemplo muito difícil de ler fazendo a mesma coisa.

Edit: Você provavelmente não deve fazer isso a menos que queira que seus colegas de trabalho o odeiem.

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

Explicação:

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

Mão longa, menos 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]; 
Paul J
fonte
3
esta é realmente a solução mais razoável
Paweł
2
Eu gosto mais disso, com explicações e tudo e também inclui um POJO de exemplo real. Ótimas respostas, merece mais votos positivos! Isso torna tudo muito mais fácil de entender!
Tigerrrrr
15

Você pode simplesmente construir um array de chaves enquanto caminha pelo objeto.

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

Em seguida, escolha aleatoriamente um elemento das chaves:

return keys[keys.length * Math.random() << 0];
Kennytm
fonte
13
Object.keys é útil aquivar keys = Object.keys(obj)
whadar
Dang << é muito mais elegante do que usar Math.floor (), provavelmente mais barato também. Eu realmente tenho que aprender a usar esses operadores bit a bit.
Paul J de
5
Neste caso, o uso do operador bit a bit é mais provavelmente um hack, porque ele precisa de um inteiro como entrada, ele converte o número. Aplicar << 0a um número inteiro não fará nada. parseInt()fará o mesmo trabalho. Portanto, nada a aprender aqui, exceto escrever um código menos compreensível.
terra em
13

Se você for capaz de usar bibliotecas, poderá descobrir que a biblioteca JS do Lo-Dash tem vários métodos muito úteis para tais casos. Nesse caso, vá em frente e verifique _.sample().

(Observe que a convenção Lo-Dash está nomeando o objeto de biblioteca _. Não se esqueça de verificar a instalação na mesma página para configurá-la para seu projeto.)

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

No seu caso, vá em frente e use:

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

Se estiver usando o underscore.js, você pode fazer:

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

Extra:

Se você precisar de várias propriedades aleatórias, adicione um número:

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

Se você precisar de um novo objeto apenas com essas propriedades aleatórias:

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

Outra maneira simples de fazer isso seria definir uma função que aplique a Math.random()função.

Esta função retorna um inteiro aleatório que varia de 'min'

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

Em seguida, extraia uma 'chave' ou um 'valor' ou 'ambos' de seu objeto Javascript sempre que fornecer a função acima como um parâmetro.

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
fonte
Você quer dizer Object.values ​​(someObject) [index]?
Bemmu
A variável de índice que usei para armazenar o número aleatório gerado é apenas um contêiner, nada de especial. Se eu não tivesse armazenado o número gerado em outra variável, cada instância da função getRandomArbitrarygeraria um novo número aleatório cada vez que fosse chamada.
Sushant Chaudhary
0

Em um objeto JSON, você deve colocar isto:

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

Essa função retornará o interior de uma propriedade aleatória.

Sybsuper
fonte
0

Você pode usar o seguinte código para escolher uma propriedade aleatória de um objeto 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
Rs super
fonte
Embora este código possa responder à pergunta, fornecer contexto adicional sobre como e / ou por que ele resolve o problema melhoraria o valor da resposta a longo prazo.
Nic3500