如何使用Javascript高效从指定数组拆分出无重复元素的随机分组
解决方案
核心思路
- 提前统计每个款式的可用库存,避免随机抽取时的无效重试
- 构造子数组时仅从「有剩余库存」且「当前子数组未使用过」的款式中随机选择,从根源上避免子数组内部出现重复
- 全程仅需一次遍历即可完成拆分,时间复杂度为O(N),N为产品总数量,无额外性能损耗
实现代码
/** * 随机拆分产品数组为指定数量的子数组,保证每个子数组内部无重复款式 * @param {Array} productArray 原始产品数组 * @param {Number} groupCount 要拆分的子数组数量 * @returns {Array} 拆分后的子数组集合 */ function splitUniqueGroups(productArray, groupCount) { // 1. 统计每个款式的库存数量 const stock = {} productArray.forEach(item => { stock[item] = (stock[item] || 0) + 1 }) // 2. 计算每个子数组的基础长度,按总长度均分 const perGroupLength = Math.floor(productArray.length / groupCount) const result = Array.from({length: groupCount}, () => []) // 3. 逐个填充每个子数组 for (let i = 0; i < groupCount; i++) { const currentGroup = result[i] const usedInCurrent = new Set() // 记录当前子数组已使用的款式,避免重复 while (currentGroup.length < perGroupLength) { // 筛选可用款式:有库存 + 当前子数组未使用 const availableItems = Object.keys(stock) .filter(item => stock[item] > 0 && !usedInCurrent.has(item)) .map(Number) // 如果你的款式是字符串类型可以删除这行 if (availableItems.length === 0) { throw new Error('当前产品数组无法拆分为要求的子数组,存在款式库存超过子数组数量') } // 随机选一个可用款式 const selected = availableItems[Math.floor(Math.random() * availableItems.length)] currentGroup.push(selected) usedInCurrent.add(selected) stock[selected] -= 1 } } // 4. 处理剩余零散元素(总长度不能被子数组数量整除的场景) let remainingItems = [] Object.entries(stock).forEach(([item, count]) => { remainingItems = remainingItems.concat(Array(count).fill(Number(item))) }) remainingItems.forEach((item, idx) => { result[idx % groupCount].push(item) }) return result } // 测试用例 const productArray = [1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10] console.log(splitUniqueGroups(productArray, 5))
方案优势
- 无重试机制,一次生成结果,不管产品数组多大都能保持极高的执行效率
- 适配通用场景:支持任意长度的产品数组、任意数量的子数组拆分
- 自带合法性校验,如果输入的数组无法满足拆分要求会直接抛出明确错误,避免死循环
内容的提问来源于stack exchange,提问作者Andy Lam
相关产品推荐
相关产品推荐

