เลือกคุณสมบัติแบบสุ่มจากวัตถุ Javascript


90

สมมติว่าคุณมีวัตถุ Javascript เช่น {'cat': 'meow', 'dog': 'woof' ... } มีวิธีที่กระชับกว่านี้ในการเลือกคุณสมบัติแบบสุ่มจากวัตถุมากกว่าวิธีที่คดเคี้ยวยาว ๆ ที่ฉันคิดขึ้นมา :

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 โปรดเลือกคำตอบที่คุณเลือกอีกครั้ง ... @kennytm ตอบถูกก่อนคนอื่น คำตอบของเดวิดคือการเข้ารหัสที่ไม่ดี (แม้ว่าจะใช้งานได้)
vsync

โปรดทราบว่าคำถามและคำตอบนั้นมองหาการส่งคืนค่าของคุณสมบัติของวัตถุแบบสุ่มไม่ใช่คุณสมบัติสุ่มตามที่ชื่อคำถามจะแนะนำ
kontur

คำตอบ:


184

คำตอบที่เลือกจะใช้ได้ดี อย่างไรก็ตามคำตอบนี้จะทำงานได้เร็วขึ้น:

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

3
ดีกว่าเพราะไม่ใช้ลูป
โดมินิก

14
ฉันทำการทดสอบบางอย่างและปรากฏว่าคำตอบที่เลือกใช้งานได้ดีและการเลือกคุณสมบัตินั้นไม่มีอคติ (ตรงกันข้ามกับการคาดเดาในคำตอบ) อย่างไรก็ตามฉันทดสอบกับวัตถุที่มี 170,000 คีย์และโซลูชันที่นี่เร็วกว่าโซลูชันที่เลือกประมาณสองเท่า
Dragonfly

8
<< 0 (bitshift ไปทางซ้ายด้วย 0) เป็นวิธีการเขียนแบบชวเลข Math.round () หรือไม่?
SystemicPlural

4
jsperf jsperf.com/random-object-property-selection นี้จะเปรียบเทียบคำตอบนี้และคำตอบที่เลือก คำตอบนี้ทำงานได้ดีขึ้น 3x สำหรับวัตถุขนาดเล็ก (คุณสมบัติ 100 รายการ) วัตถุที่ใหญ่กว่า (คุณสมบัติ 100k) ความแตกต่างจะลดลงเป็น 2x ดีกว่า
Constablebrew

2
@MuhammadUmer - No Math.random()ส่งคืนตัวเลขในช่วง [0,1)
Yay295

74

เลือกองค์ประกอบแบบสุ่มจากสตรีม

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

2
มาตรฐาน ECMAScript พูดอะไรเกี่ยวกับคุณสมบัติที่ถูกส่งผ่านในลำดับเดียวกันหรือไม่? ออบเจ็กต์ในการนำไปใช้งานส่วนใหญ่มีลำดับที่คงที่ แต่พฤติกรรมไม่ได้กำหนดไว้ในข้อมูลจำเพาะ: stackoverflow.com/questions/280713/…
Brendan Berg

4
สิ่งนี้ดูเหมือนจะเอียงไปทางองค์ประกอบแรกในวัตถุ ฉันยังไม่ทราบสาเหตุ!
Cole Gleason

7
สิ่งนี้จะไม่เลือกคุณสมบัติแรก (Math.random เสมอ <1) และหลังจากนั้นแต่ละหมายเลขจะมีโอกาส 0.5 ในการถูกเลือก ดังนั้น 0.5 สำหรับคุณสมบัติที่สอง 0.25 สำหรับ 3, 0.125 สำหรับ 4 เป็นต้น
SystemicPlural

4
การแก้ไขบางประการ: ฟังก์ชันนี้สามารถเลือกคุณสมบัติแรกได้ ในการวนซ้ำครั้งแรกการเพิ่มคำนำหน้าต่อจำนวนทำให้ด้านขวามือของสมการประเมินเป็น 1/1 == 1 เนื่องจาก Math.random อยู่ในช่วง [0,1) เสมอ (ศูนย์ถึงหนึ่งไม่รวมหนึ่ง) นิพจน์ประเมินค่าเป็นจริงและคุณสมบัติแรกถูกเลือก เท่าที่การกระจายของการสุ่มเลือกเป็นไปอย่างสม่ำเสมอ ด้วยคุณสมบัติเดียวมีโอกาส 100% ที่จะถูกเลือก เมื่อมีทั้งสองจะมีโอกาส 50% ที่จะถูกเลือก ด้วยสาม 33.3% และอื่น ๆ โซลูชันนี้มีพื้นที่หน่วยความจำน้อยที่สุด
Constablebrew

3
@davidhadas พิจารณาลำดับของสามองค์ประกอบ อันแรกจะถูกเลือกด้วยความน่าจะเป็น 1 อย่างไรก็ตามมันอาจถูกแทนที่ (โปรดสังเกตว่าเราจะไม่กลับมาในทันที!) โดยองค์ประกอบที่สองที่มีความน่าจะเป็น 1/2 องค์ประกอบที่สองอาจถูกแทนที่ด้วยองค์ประกอบที่สามโดยมีความน่าจะเป็น 1/3 ดังนั้นเราจึงได้ P (ครั้งแรก) = P (หยิบครั้งแรก) * P (ไม่ได้หยิบครั้งที่สอง) * P (ไม่ได้หยิบครั้งที่สาม) = 1 * 1/2 * 2/3 = 1/3; P (วินาที) = P (หยิบครั้งที่สอง) * P (ไม่ได้หยิบครั้งที่สาม) = 1/2 * 1/3 = 1/3; P (ที่สาม) = P (หยิบครั้งที่สาม) = 1/3
Martin Törnwall

19

ฉันไม่คิดว่าตัวอย่างใด ๆ จะทำให้สับสนพอดังนั้นนี่เป็นตัวอย่างที่อ่านยากจริงๆที่ทำในสิ่งเดียวกัน

แก้ไข:คุณไม่ควรทำเช่นนี้เว้นแต่คุณต้องการให้เพื่อนร่วมงานเกลียดคุณ

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

คำอธิบาย:

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

มือยาวไม่สับสน:

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
นี่เป็นทางออกที่สมเหตุสมผลที่สุด
Paweł

2
ฉันชอบสิ่งนี้มากที่สุดพร้อมคำอธิบายและทุกอย่างและยังมี POJO ตัวอย่างจริง คำตอบที่ยอดเยี่ยมสมควรได้รับการโหวตเพิ่มขึ้น! ทำให้ทุกอย่างเข้าใจง่ายขึ้นมาก!
Tigerrrrr

15

คุณสามารถสร้างอาร์เรย์ของคีย์ในขณะที่เดินผ่านวัตถุ

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

จากนั้นสุ่มเลือกองค์ประกอบจากปุ่ม:

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

13
Object.keys มีประโยชน์ที่นี่var keys = Object.keys(obj)
whadar

แดง << มีความสง่างามกว่าการใช้ Math.floor () มากอาจจะไม่แพงด้วย ฉันต้องลงจริงๆและเรียนรู้วิธีใช้ตัวดำเนินการบิตเหล่านั้น
Paul J

5
ในกรณีนี้การใช้งานตัวดำเนินการ bitwise มีแนวโน้มที่จะแฮ็กเนื่องจากต้องการจำนวนเต็มเป็นอินพุตจึงจะแปลงตัวเลข การใช้<< 0กับจำนวนเต็มจะไม่ทำอะไรเลย parseInt()จะทำงานเดียวกัน จึงไม่มีอะไรให้เรียนรู้ที่นี่นอกจากการเขียนโค้ดที่เข้าใจได้น้อย
ฝั่ง

13

หากคุณสามารถใช้ไลบรารีได้คุณอาจพบว่าไลบรารีLo-Dash JS มีวิธีการที่มีประโยชน์มากมายสำหรับกรณีดังกล่าว ในกรณีนี้ให้ดำเนินการตรวจสอบ_.sample()ในกรณีนี้ไปข้างหน้าและตรวจสอบ

(หมายเหตุหลักการ Lo-Dash กำลังตั้งชื่อไลบรารีอ็อบเจ็กต์ _ อย่าลืมตรวจสอบการติดตั้งในเพจเดียวกันเพื่อตั้งค่าสำหรับโปรเจ็กต์ของคุณ)

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

ในกรณีของคุณให้ใช้:

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

3

หากคุณใช้underscore.jsคุณสามารถทำได้:

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

พิเศษ:

หากคุณต้องการคุณสมบัติสุ่มหลายรายการให้เพิ่มตัวเลข:

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

หากคุณต้องการวัตถุใหม่ที่มีคุณสมบัติสุ่มเท่านั้น:

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

0

อีกวิธีง่ายๆในการทำเช่นนี้คือการกำหนดฟังก์ชันที่ใช้ Math.random()ฟังก์ชัน

ฟังก์ชันนี้จะส่งคืนจำนวนเต็มสุ่มที่อยู่ในช่วง 'min'

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

จากนั้นแยก 'คีย์' หรือ 'ค่า' หรือ 'ทั้งสอง' ออกจากออบเจ็กต์ Javascript ของคุณทุกครั้งที่คุณใส่ฟังก์ชันข้างต้นเป็นพารามิเตอร์

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

คุณหมายถึง Object.values ​​(someObject) [index]?
Bemmu

ดัชนีตัวแปรที่ผมเคยใช้ในการจัดเก็บที่สร้างขึ้นแบบสุ่มจำนวนเป็นเพียงภาชนะที่ไม่มีอะไรพิเศษ หากฉันไม่ได้เก็บหมายเลขที่สร้างไว้ในตัวแปรอื่นดังนั้นแต่ละอินสแตนซ์ของฟังก์ชันgetRandomArbitraryจะสร้างหมายเลขสุ่มใหม่ทุกครั้งที่เรียก
Sushant Chaudhary

0

ในออบเจ็กต์ JSON คุณต้องวางสิ่งนี้:

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

ฟังก์ชันนั้นจะคืนค่าภายในของคุณสมบัติแบบสุ่ม


0

คุณสามารถใช้รหัสต่อไปนี้เพื่อเลือกคุณสมบัติแบบสุ่มจากวัตถุ 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

แม้ว่ารหัสนี้อาจตอบคำถามได้ แต่การให้บริบทเพิ่มเติมเกี่ยวกับวิธีการและ / หรือเหตุผลในการแก้ปัญหาจะช่วยเพิ่มมูลค่าในระยะยาวของคำตอบ
Nic3500
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.