如何从数组中获取指定数量的唯一随机元素 自定义实现问题求助
实现方案
原代码存在的问题
- 变量
x初始赋值为0,后续没有赋值逻辑就直接用于for循环判断,for循环完全不会执行,result初始为空数组会导致后续逻辑报错 - 用switch判断
this指向的数组实例的写法扩展性极差,每新增一个需要特殊配置的数组就要新增case分支,维护成本极高 - 现有的去重逻辑仅校验了前两个元素是否重复,当x取值大于2时无法覆盖所有元素的去重校验
- 直接修改Array原型属于侵入式修改,建议先做重复定义校验,避免和其他代码冲突
推荐实现方案
方案1:调用时动态传参指定x值(最灵活)
直接在调用方法时传入x的取值,无需提前绑定数组和x的对应关系,适配所有数组场景:
// 先判断原型上是否已有该方法,避免重复定义覆盖 if (!Array.prototype.getUniqueRandom) { Array.prototype.getUniqueRandom = function (x) { // 边界校验:x不能大于数组长度,否则无法取到足够的不重复值 if (x > this.length) throw new Error('取值数量不能超过数组长度'); // 没有传参时默认x为2 x = x || 2; const result = new Set(); // 用Set自动去重,直到拿到x个不重复的随机值 while (result.size < x) { const randomIndex = Math.floor(Math.random() * this.length); result.add(this[randomIndex]); } // 转换为数组后取前两项返回指定格式 const resArr = Array.from(result); return `${resArr[0]} + ${resArr[1]}`; } } // 调用示例 array1.getUniqueRandom(2) array2.getUniqueRandom(3)
方案2:给数组绑定固定x值,无需传参
如果你希望固定每个数组对应的x值,调用时不用传参,可以给数组实例增加自定义属性存储x值,方法内部直接读取即可:
if (!Array.prototype.getUniqueRandom) { Array.prototype.getUniqueRandom = function () { // 读取数组自身绑定的randomCount属性,没有设置的话默认取2 const x = this.randomCount ?? 2; if (x > this.length) throw new Error('取值数量不能超过数组长度'); const result = new Set(); while (result.size < x) { const randomIndex = Math.floor(Math.random() * this.length); result.add(this[randomIndex]); } const resArr = Array.from(result); return `${resArr[0]} + ${resArr[1]}`; } } // 给不同数组绑定对应的x值 const array1 = [1,2,3,4]; array1.randomCount = 2; const array2 = ['a','b','c','d','e']; array2.randomCount = 3; // 调用时不用传参 array1.getUniqueRandom() array2.getUniqueRandom()
可选优化:用洗牌算法实现更高效的随机抽取
如果数组长度很大、x取值也很大的场景,用Set循环的方式性能会下降,可以用Fisher-Yates洗牌算法实现随机抽取,时间复杂度更低:
if (!Array.prototype.getUniqueRandom) { Array.prototype.getUniqueRandom = function (x = 2) { if (x > this.length) throw new Error('取值数量不能超过数组长度'); // 拷贝原数组避免修改原数组内容 const copyArr = [...this]; // 洗牌算法打乱数组 for (let i = copyArr.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [copyArr[i], copyArr[j]] = [copyArr[j], copyArr[i]]; } // 取前x个元素 const resArr = copyArr.slice(0, x); return `${resArr[0]} + ${resArr[1]}`; } }
内容的提问来源于stack exchange,提问作者Moosike
相关产品推荐
相关产品推荐

