在JavaScript中从关联数组中获取随机元素?

7
我正在使用JavaScript构建歌曲播放列表。我已经使用了一个关联数组foo--我的对象结构看起来类似于:
foo[songID] = songURL;

我正在尝试添加洗牌功能。我想从这个列表中随机选择一首歌曲。有没有简单的方法可以做到这一点 - 数组没有索引。

5个回答

11
你可以使用函数 Object.keys(object) 来获取对象的键数组。这个函数的非常好的文档可以在 MDN 找到。
另外,你似乎有两个不同但相关的问题。
你的主题询问如何从对象中获取随机元素。对于这个问题,
var randomProperty = function (object) {
  var keys = Object.keys(object);
  return object[keys[Math.floor(keys.length * Math.random())]];
};

但是你在问题正文中也问到了如何洗牌数组。为此,你需要一个某种类型的洗牌函数(最可能是Fisher-Yates算法的实现),然后直接使用该函数。

var objectKeysShuffled = function (object) {
    return shuffle(Object.keys(object));
};

@kennis - 应该是IE < 9。 - RobG
嗯,回过头来看,我误读了问题。它要求如何洗牌,而我提供了一个选择随机歌曲的函数...哎呀。需要注意的主要事项是,一旦你有了一个包含Object.keys的数组,就可以对其进行洗牌。 - Havvy

2

以下是我创建的函数,用于从音频元素的哈希中随机播放背景音乐。

this.bgm = {} //I later added audio elements to this
this.playRandomBGM = function()
{
    var keys = Object.keys(this.bgm);
    self.currentBGM = keys[Math.floor(keys.length * Math.random())];
    console.log("Playing random BGM: " + self.currentBGM);
    self.bgm[self.currentBGM].play();
}

这看起来非常像我的答案内联到一个函数中,该函数添加了大量与问题无关的嘈杂代码。你的第四和第五行与我的完全相同,只是你用self.currentBGM =替换了return,即使在所提出的问题中没有selfcurrentBGM - Havvy

0
你可以尝试这样做:
var obj = {
    'song1': 'http://...1',
    'song2': 'http://...2',
    'song3': 'http://...3',
    'song4': 'http://...4',
    'song5': 'http://...5',
    'song6': 'http://...6',
}, tempArr = [], len, rand, song;

for ( var key in obj )
    if ( obj.hasOwnProperty(key) )
        tempArr.push( obj[key] );

len = tempArr.length;
rand = Math.floor( Math.random() * len );
song = tempArr[rand];
document.write(song);

请注意,这只是一种绕过应该使用以下结构的数组的笨拙方法:
var songs = [
    {title: 'Song1', url: 'http://...1.mp3'},
    {title: 'Song2', url: 'http://...2.mp3'},
    {title: 'Song3', url: 'http://...3.mp3'}
];

0
function fetchRandom(arr) {
    var ret,
        i = 0;
    for (var key in arr){
        if (Math.random() < 1/++i){
           ret = key;
        }
    }
    return ret;
}

var randomSong = foo[fetchRandom(foo)];

你应该使用一个对象来实现这个功能。然后使用一个对象的索引数组进行随机化,但这应该可以回答你的问题。

微不足道的错误:这将始终给出第一个键,因为在第一次迭代期间,i === 0,并且1 / ++i将始终大于Math.random()。 - Havvy
1
你正在计算每个密钥的不同随机数,这并不容易。即使Math.random()是完美随机的,你的函数也不能给出真正的随机结果。第N个元素的概率将是(N/length)*Product([1, N), N/Length),而不是N/Length。 - Havvy

0

一种低效的方法是:

function randomProperty(obj) {
  var a = [];
  for (var p in obj) {
    if (obj.hasOwnProperty(p)) {
      a.push(p);
    }
  }
  return a.length? obj[ a[a.length * Math.random() | 0]] : void 0;
}

正如其他人所说,更有效的方法是存储属性名称数组并重复使用它,而不是每次都创建它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接