如何打乱数组但保留指定元素位置?播放列表固定Jingle需求
仅打乱播放列表中普通曲目,保留固定广告曲位置的实现方案
给定一个混合了固定广告曲(标题为jingle fixed)和普通曲目的播放列表数组,现有洗牌函数会打乱所有元素位置,但我们需要保持广告曲的位置完全不变,仅随机打乱普通曲目的顺序。
原代码示例
var arr = [ {'title': 'jingle fixed'}, {'title': 'track 0'}, {'title': 'track 1'}, {'title': 'jingle fixed'}, {'title': 'track 2'}, {'title': 'jingle fixed'}, {'title': 'track 3'}, {'title': 'track 4'}, {'title': 'track 5'} ]; function shuffle(playList) { let currentIndex = playList.length, randomIndex; while (currentIndex != 0) { randomIndex = Math.floor(Math.random() * currentIndex); currentIndex--; // How to except by playList.title? [playList[currentIndex], playList[randomIndex]] = [playList[randomIndex], playList[currentIndex]]; } return playList; }
解决方案
核心思路是:先提取所有普通曲目并洗牌,再遍历原数组保留广告曲位置,替换为洗牌后的普通曲目。
function shuffleOnlyTracks(playList) { // 提取所有非广告曲目 const tracks = playList.filter(item => item.title !== 'jingle fixed'); // 使用Fisher-Yates洗牌算法打乱曲目(公平无偏的洗牌方式) let currentIndex = tracks.length; while (currentIndex > 0) { const randomIndex = Math.floor(Math.random() * currentIndex); currentIndex--; [tracks[currentIndex], tracks[randomIndex]] = [tracks[randomIndex], tracks[currentIndex]]; } // 遍历原列表,保留广告曲位置,替换为打乱后的曲目 let trackPointer = 0; return playList.map(item => { return item.title === 'jingle fixed' ? item : tracks[trackPointer++]; }); } // 调用测试 const result = shuffleOnlyTracks(arr); console.log(result);
说明
- 提取并洗牌曲目:先筛选出所有非
jingle fixed的元素,用Fisher-Yates算法洗牌,这是业界公认的公平洗牌方式,避免了sort(() => Math.random() - 0.5)存在的概率偏斜问题。 - 保留广告曲位置:遍历原数组时,遇到广告曲直接返回原元素,遇到普通曲目则从洗牌后的列表中按顺序取出替换,完美保留了广告曲的原始位置。
内容的提问来源于stack exchange,提问作者jazzfriends
相关产品推荐
相关产品推荐

