如何对含数组属性的对象数组按指定值关联同值元素排序
对象数组关联规则排序实现方案
需求说明
待排序数组结构如下:
[ {id: 1, values: ['a','b']}, {id: 2, values: ['c','d']}, {id: 3, values: ['b']}, {id: 4, values: ['c']}, {id: 5, values: ['e']}, ]
排序规则:
- 传入指定字符串作为起始锚点,所有
values属性包含该锚点的元素排在最前 - 剩余元素中,和已排序元素存在公共
values值的元素按关联批次依次向后排列 - 和所有已排序元素无公共关联值的元素排在末尾
例:传入锚点值'b'时,排序后元素id顺序为1,3,2,4,5。
实现说明
Array.sort原生的比较函数是基于两两对比逻辑,无法直接记录全局关联值的遍历状态,因此不能直接写一个无状态的比较函数实现需求,需要先遍历计算元素的排序优先级/顺序,再返回排序结果,逻辑步骤如下:
- 浅拷贝原数组避免修改源数据,初始化已排序结果数组、关联值集合
- 先筛出所有包含起始锚点的元素作为第一批次,加入已排序结果,同时把这些元素的所有
values值存入关联值集合 - 循环遍历剩余未排序元素,每轮筛出所有和当前关联值集合存在交集的元素,作为下一批次加入结果,同时把这批元素的
values并入关联值集合,直到某一轮没有新的关联元素被筛出 - 最后把剩余无任何关联的元素直接追加到结果末尾即可
完整代码
/** * 按关联规则排序对象数组 * @param {Array} targetArr 待排序数组 * @param {string} anchorVal 起始锚点值 * @param {string} [arrProp='values'] 存储关联值的数组属性名 * @returns {Array} 排序后的新数组 */ function sortByRelated(targetArr, anchorVal, arrProp = 'values') { const unSorted = [...targetArr] const sorted = [] const relatedValueSet = new Set() // 筛选第一批次:包含锚点值的元素 for (let i = unSorted.length - 1; i >= 0; i--) { const currentItem = unSorted[i] if (currentItem[arrProp].includes(anchorVal)) { sorted.push(currentItem) currentItem[arrProp].forEach(val => relatedValueSet.add(val)) unSorted.splice(i, 1) } } // 循环筛选后续关联批次 let foundNewRelated = true while (foundNewRelated && unSorted.length > 0) { foundNewRelated = false const currentBatch = [] for (let i = unSorted.length - 1; i >= 0; i--) { const currentItem = unSorted[i] const hasCommonValue = currentItem[arrProp].some(val => relatedValueSet.has(val)) if (hasCommonValue) { currentBatch.push(currentItem) currentItem[arrProp].forEach(val => relatedValueSet.add(val)) unSorted.splice(i, 1) foundNewRelated = true } } sorted.push(...currentBatch) } // 追加无关联的剩余元素 sorted.push(...unSorted) return sorted } // 测试调用 const testArr = [ {id: 1, values: ['a','b']}, {id: 2, values: ['c','d']}, {id: 3, values: ['b']}, {id: 4, values: ['c']}, {id: 5, values: ['e']}, ] const result = sortByRelated(testArr, 'b') console.log(result.map(item => item.id)) // 输出 [1, 3, 2, 4, 5],符合预期
补充说明
如果必须要传入Array.sort()使用,可以先调用上述逻辑给每个元素预先生成排序权重值,再在比较函数中对比权重即可,核心判断逻辑和上述实现完全一致。
内容的提问来源于stack exchange,提问作者Darkmagister
相关产品推荐
相关产品推荐

