Wybierz losową właściwość z obiektu Javascript


89

Załóżmy, że masz obiekt JavaScript, taki jak {'cat': 'meow', 'dog': 'hau' ...} Czy istnieje bardziej zwięzły sposób na wybranie losowej właściwości z obiektu niż ten długi, zawiły sposób, który wymyśliłem :

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, wybierz ponownie wybraną odpowiedź ... @kennytm odpowiedział poprawnie przed innymi. Odpowiedź Davida to po prostu złe kodowanie (chociaż działa)
vsync

Zwróć uwagę, że pytanie i odpowiedzi w rzeczywistości zwracają wartość losowej właściwości obiektu, a nie losowej właściwości, jak sugerowałby tytuł pytania.
kontur

Odpowiedzi:


182

Wybrana odpowiedź będzie działać dobrze. Jednak ta odpowiedź będzie działać szybciej:

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

3
to jest lepsze, ponieważ nie używa pętli
Dominic

14
Zrobiłem kilka testów i wydaje się, że wybrana odpowiedź działa dobrze, a wybór właściwości jest bezstronny (w przeciwieństwie do spekulacji w odpowiedziach); jednak testowałem na obiekcie ze 170 000 kluczykami i tutaj rozwiązanie było około dwa razy szybsze niż wybrane rozwiązanie.
Dragonfly

8
Czy << 0 (przesunięcie bitu w lewo o 0) jest skróconą metodą pisania Math.round ()?
SystemicPlural

4
Ten jsperf jsperf.com/random-object-property-selection porównuje tę odpowiedź i wybraną odpowiedź. Ta odpowiedź działa 3x lepiej w przypadku mniejszych obiektów (100 właściwości). W przypadku większych obiektów (100 tys. Właściwości) różnica spada do 2x lepiej.
Constablebrew

2
@MuhammadUmer - No. Math.random()zwraca liczbę z zakresu [0,1).
Yay295

74

Pobieranie losowego elementu ze strumienia

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

2
Czy standard ECMAScript mówi coś o właściwościach, które są zawsze przemierzane w tej samej kolejności? Obiekty w większości implementacji mają stabilną kolejność, ale zachowanie jest nieokreślone w specyfikacji: stackoverflow.com/questions/280713/ ...
Brendan Berg

4
Wydaje się, że jest to pochylenie w kierunku pierwszego elementu obiektu. Jeszcze nie wiem, dlaczego!
Cole Gleason,

7
To nigdy nie wybierze pierwszej właściwości (Math.random jest zawsze <1), a następnie każda liczba będzie miała 0,5 szansy na wybranie. Czyli 0,5 dla drugiej właściwości, 0,25 dla trzeciej, 0,125 dla czwartej itd.
SystemicPlural

4
Niektóre poprawki: Ta funkcja może wybrać pierwszą właściwość. W pierwszej iteracji przyrost prefiksu przy zliczaniu powoduje, że prawa strona równania daje wynik 1/1 == 1. Ponieważ Math.random jest zawsze w zakresie [0,1) (zero do jednego, wyłączając jeden), wyrażenie przyjmuje wartość true i zostaje wybrana pierwsza właściwość. Jeśli chodzi o rozkład losowania, jest jednolity. W przypadku jednej nieruchomości istnieje 100% szansa, że ​​zostanie ona wybrana. Przy dwóch istnieje 50% szansy, że jeden z nich zostanie wybrany. Z trzema 33,3%. I tak dalej. To rozwiązanie ma minimalny ślad pamięci.
Constablebrew

3
@davidhadas Rozważ sekwencję trzech elementów. Pierwszy jest wybierany z prawdopodobieństwem 1. Jednak można go zastąpić (zauważ, że nie wracamy od razu!) Drugim elementem z prawdopodobieństwem 1/2. Drugi element można z kolei zastąpić trzecim elementem z prawdopodobieństwem 1/3. Czyli otrzymujemy P (pierwszy) = P (pierwszy wybrany) * P (drugi niezebrany) * P (trzeci niezebrany) = 1 * 1/2 * 2/3 = 1/3; P (drugi) = P (drugi zbierany) * P (trzeci nie wybierany) = 1/2 * 1/3 = 1/3; P (trzeci) = P (trzeci zbiór) = 1/3.
Martin Törnwall

19

Nie sądziłem, że którykolwiek z przykładów był wystarczająco zagmatwany, więc oto naprawdę trudny do odczytania przykład, który robi to samo.

Edycja: prawdopodobnie nie powinieneś tego robić, chyba że chcesz, aby Twoi współpracownicy Cię nienawidzili.

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

Wyjaśnienie:

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

Długa ręka, mniej zagmatwana:

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
to właściwie najbardziej rozsądne rozwiązanie
Paweł

2
Najbardziej podoba mi się to, z wyjaśnieniami i wszystkim, a także z rzeczywistym przykładem POJO. Świetne odpowiedzi, zasługują na więcej głosów! Po prostu sprawia, że ​​wszystko jest o wiele łatwiejsze do zrozumienia!
Tigerrrrr

1
Najlepsze rozwiązanie! To powinno być najczęściej głosowane.
nilsoviani

15

Możesz po prostu zbudować tablicę kluczy, przechodząc przez obiekt.

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

Następnie losowo wybierz element z klawiszy:

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

13
Przydaje się tu var keys = Object.keys(obj)
Object.keys

Dang << jest o wiele bardziej wdzięczny niż użycie Math.floor (), prawdopodobnie też tańszy. Naprawdę muszę zejść i nauczyć się używać tych operatorów bitowych.
Paul J

5
W tym przypadku użycie operatora bitowego jest bardziej prawdopodobne, ponieważ wymaga liczby całkowitej jako danych wejściowych, konwertuje liczbę. Zastosowanie << 0do liczby całkowitej nic nie da. parseInt()wykona tę samą pracę. Nie ma więc nic do nauczenia się z wyjątkiem pisania mniej zrozumiałego kodu.
landunder

13

Jeśli potrafisz korzystać z bibliotek, może się okazać, że biblioteka Lo-Dash JS ma wiele bardzo przydatnych metod w takich przypadkach. W takim przypadku śmiało sprawdź _.sample().

(Uwaga: Konwencja Lo-Dash nazywa obiekt biblioteki _. Nie zapomnij sprawdzić instalacji na tej samej stronie, aby skonfigurować ją dla swojego projektu.)

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

W twoim przypadku śmiało użyj:

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

3

Jeśli używasz underscore.js , możesz:

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

Dodatkowy:

Jeśli potrzebujesz wielu losowych właściwości, dodaj liczbę:

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

Jeśli potrzebujesz nowego obiektu z tylko tymi losowymi właściwościami:

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

0

Innym prostym sposobem na zrobienie tego byłoby zdefiniowanie funkcji, która stosuje Math.random()funkcję.

Ta funkcja zwraca losową liczbę całkowitą z zakresu od „min”

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

Następnie wyodrębnij „klucz”, „wartość” lub „oba” z obiektu Javascript za każdym razem, gdy podasz powyższą funkcję jako parametr.

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

Czy masz na myśli Object.values ​​(someObject) [index]?
Bemmu,

Wskaźnik zmienna, że mam używany do przechowywania wygenerowanych losowo numer jest tylko kontenerem, nic specjalnego. Gdybym nie zapisał wygenerowanej liczby w innej zmiennej, każde wystąpienie funkcji getRandomArbitrarygenerowałoby nową liczbę losową za każdym razem, gdy jest wywoływana.
Sushant Chaudhary

0

W obiekcie JSON musisz umieścić to:

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

Ta funkcja zwróci wewnętrzną właściwość losowej.


0

Możesz użyć następującego kodu, aby wybrać losową właściwość z obiektu 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

Chociaż ten kod może odpowiedzieć na pytanie, dostarczenie dodatkowego kontekstu dotyczącego tego, jak i / lub dlaczego rozwiązuje problem, poprawiłoby długoterminową wartość odpowiedzi.
Nic3500,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.