Elija una propiedad aleatoria de un objeto Javascript

89

Suponga que tiene un objeto Javascript como {'gato': 'miau', 'perro': 'guau' ...} ¿Hay una forma más concisa de elegir una propiedad aleatoria del objeto que esta forma larga y prolija que se me ocurrió? :

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
fuente
OP, vuelva a elegir su respuesta seleccionada ... @kennytm la respondió correctamente antes que los demás. La respuesta de David es simplemente una mala codificación (aunque funciona)
vsync
Tenga en cuenta que la pregunta y las respuestas en realidad buscan devolver el valor de una propiedad de objeto aleatorio, no una propiedad aleatoria como sugiere el título de la pregunta.
kontur

Respuestas:

182

La respuesta elegida funcionará bien. Sin embargo, esta respuesta se ejecutará más rápido:

var randomProperty = function (obj) {
    var keys = Object.keys(obj);
    return obj[keys[ keys.length * Math.random() << 0]];
};
Lawrence Whiteside
fuente
3
esto es mejor ya que no usa un bucle
Dominic
14
Hice algunas pruebas y parece que la respuesta elegida funciona bien y que la elección de la propiedad es imparcial (contrariamente a las especulaciones entre las respuestas); sin embargo, probé en un objeto con 170,000 claves y la solución aquí fue aproximadamente dos veces más rápida que la solución elegida.
Dragonfly
8
¿Es << 0 (desplazamiento de bits a la izquierda en 0) un método abreviado para escribir Math.round ()?
SystemicPlural
4
Este jsperf jsperf.com/random-object-property-selection compara esta respuesta y la respuesta elegida. Esta respuesta funciona mejor por 3 veces para objetos más pequeños (100 propiedades). Objetos más grandes (propiedades de 100k) la diferencia se reduce a 2x mejor.
Constablebrew
2
@MuhammadUmer: No. Math.random()devuelve un número en el rango [0,1).
Yay295
74

Elegir un elemento aleatorio de una secuencia

function pickRandomProperty(obj) {
    var result;
    var count = 0;
    for (var prop in obj)
        if (Math.random() < 1/++count)
           result = prop;
    return result;
}
David Leonard
fuente
2
¿El estándar ECMAScript dice algo sobre las propiedades que siempre se atraviesan en el mismo orden? Los objetos en la mayoría de las implementaciones tienen un orden estable, pero el comportamiento no está definido en la especificación: stackoverflow.com/questions/280713/…
Brendan Berg
4
Esto parece tener un sesgo hacia el primer elemento del objeto. ¡Aún no he descubierto por qué!
Cole Gleason
7
Esto nunca seleccionará la primera propiedad (Math.random es siempre <1) y después de eso, cada número tendrá una probabilidad de 0.5 de ser seleccionado. Así que 0,5 para la segunda propiedad, 0,25 para el tercero, 0.125 para el cuarto, etc.
SystemicPlural
4
Algunas correcciones: esta función puede seleccionar la primera propiedad. En la primera iteración, el incremento del prefijo en el recuento hace que el lado derecho de la ecuación se evalúe como 1/1 == 1. Dado que Math.random siempre está en el rango [0,1) (cero a uno, excluyendo uno), la expresión se evalúa como verdadera y se selecciona la primera propiedad. En cuanto a la distribución de la selección aleatoria, es uniforme. Con una propiedad hay un 100% de posibilidades de que sea seleccionada. Con dos hay un 50% de posibilidades de que cualquiera de los dos sea seleccionado. Con tres un 33,3%. Y así. Esta solución tiene una huella de memoria mínima.
Constablebrew
3
@davidhadas Considere una secuencia de tres elementos. El primero se elige con una probabilidad de 1. Sin embargo, podría ser reemplazado (¡observe que no regresamos inmediatamente!) Por el segundo elemento con una probabilidad de 1/2. El segundo elemento podría a su vez ser reemplazado por el tercer elemento, con una probabilidad de 1/3. Entonces obtenemos P (primero) = P (primero elegido) * P (segundo no elegido) * P (tercero no elegido) = 1 * 1/2 * 2/3 = 1/3; P (segundo) = P (segundo elegido) * P (tercero no elegido) = 1/2 * 1/3 = 1/3; P (tercero) = P (tercer elegido) = 1/3.
Martin Törnwall
19

No pensé que ninguno de los ejemplos fuera lo suficientemente confuso, así que aquí hay un ejemplo realmente difícil de leer que hace lo mismo.

Editar: probablemente no deberías hacer esto a menos que quieras que tus compañeros de trabajo te odien.

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

Explicación:

// 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 larga, 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
fuente
4
esta es en realidad la solución más razonable
Paweł
2
Este es el que más me gusta, con explicaciones y todo y también incluye un ejemplo real de POJO. Great Answers, merece más upvotes! ¡Simplemente hace que todo sea mucho más fácil de entender!
Tigerrrrr
1
¡Mejor solución! Este debería ser el más votado.
nilsoviani
15

Puede simplemente construir una matriz de claves mientras camina por el objeto.

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

Luego, elija al azar un elemento de las claves:

return keys[keys.length * Math.random() << 0];
Kennytm
fuente
13
Object.keys es útil aquívar keys = Object.keys(obj)
whadar
Dang << es mucho más elegante que usar Math.floor (), probablemente también menos costoso. Realmente tengo que bajarme y aprender a usar esos operadores bit a bit.
Paul J
5
En este caso, es más probable que el uso del operador bit a bit sea un truco, ya que necesita un número entero como entrada, convierte el número. Aplicar << 0a un número entero no hará nada. parseInt()hará el mismo trabajo. Así que no hay nada que aprender aquí excepto escribir un código menos comprensible.
aterrizó
13

Si es capaz de utilizar bibliotecas, es posible que encuentre que la biblioteca Lo-Dash JS tiene muchos métodos muy útiles para tales casos. En este caso, continúe y verifique _.sample().

(Tenga en cuenta que la convención de Lo-Dash es nombrar el objeto de la biblioteca _. No olvide verificar la instalación en la misma página para configurarla para su proyecto).

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

En su caso, siga adelante y utilice:

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

Si está utilizando underscore.js , puede hacer lo siguiente:

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

Extra:

Si necesita varias propiedades aleatorias, agregue un número:

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

Si necesita un nuevo objeto con solo esas propiedades aleatorias:

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

Otra forma sencilla de hacer esto sería definir una función que se aplique Math.random() función.

Esta función devuelve un número entero aleatorio que va desde el 'min'

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

Luego, extraiga una 'clave' o un 'valor' o 'ambos' de su objeto Javascript cada vez que proporcione la función anterior como 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
fuente
¿Quieres decir Object.values ​​(someObject) [index]?
Bemmu
La variable de índice que he usado para almacenar el número aleatorio generado es solo un contenedor, nada especial. Si no hubiera almacenado el número generado en otra variable, cada instancia de la función getRandomArbitrarygeneraría un nuevo número aleatorio cada vez que se llama.
Sushant Chaudhary
0

En un objeto JSON debes colocar esto:

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

Esa función devolverá el interior de una propiedad aleatoria.

Sybsuper
fuente
0

Puede utilizar el siguiente código para elegir una propiedad aleatoria de un 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
Jajaja super
fuente
Si bien este código puede responder a la pregunta, proporcionar un contexto adicional sobre cómo y / o por qué resuelve el problema mejoraría el valor de la respuesta a largo plazo.
Nic3500