如何用Lodash或原生JS按state和details正确实现多键分组?
解决按state和details的id集合多键分组的问题
问题描述
使用Lodash按state和details字段分组时出现错误:state为rejected但details中id不同的条目(docId 333333和444444)被归为同一组;同时state为accepted但details中id顺序不同的条目(docId 222222和1111111)未被正确合并。预期分组规则是:仅当state相同且details中的id集合完全一致(与顺序无关)时才归为一组。
预期分组结果:
[ { "docId": "222222,1111111", "details": [ { "id": 20656, "type": "Claimant", "name": "First Name Last Name" }, { "id": 10000, "type": "Fellow", "name": "Fellow First Name Fellow Last Name" } ], "state": "accepted" }, { "docId": "333333", "details": [ { "id": 10000, "type": "Fellow", "name": "Fellow First Name Fellow Last Name" } ], "state": "rejected" }, { "docId": "444444", "details": [ { "id": 20656, "type": "Claimant", "name": "First Name Last Name" } ], "state": "rejected" } ]
错误原因
原代码中直接将details数组转为字符串作为分组键的一部分:${details},${state}。由于数组中的对象转字符串默认返回[object Object],导致所有单元素details的条目都会生成相同的键(如[object Object],rejected),从而被错误归组;同时,顺序不同的相同id集合会生成不同的数组字符串,导致本该合并的条目被分开。
解决方案
核心是生成唯一标识state和details id集合的分组键:
- 提取每个条目中
details的所有id - 对id数组进行排序(消除顺序影响)
- 将排序后的id数组转为字符串,与state拼接作为分组键
Lodash实现
const data = [ { docId: 222222, state: "accepted", details: [ { id: 20656, type: "Claimant", name: "First Name Last Name" }, { id: 10000, type: "Fellow", name: "Fellow First Name Fellow Last Name" } ] }, { docId: 1111111, state: "accepted", details: [ { id: 10000, type: "Fellow", name: "Fellow First Name Fellow Last Name" }, { id: 20656, type: "Claimant", name: "First Name Last Name" } ] }, { docId: 333333, state: "rejected", details: [ { id: 10000, type: "Fellow", name: "Fellow First Name Fellow Last Name" } ] }, { docId: 444444, state: "rejected", details: [ { id: 20656, type: "Claimant", name: "First Name Last Name" } ] } ]; const grouped = _(data) .groupBy(item => { // 生成唯一分组键:state + 排序后的id字符串 const sortedIds = _.sortBy(item.details, 'id').map(d => d.id).join(','); return `${item.state}_${sortedIds}`; }) .map((items) => ({ docId: items.map(item => item.docId).join(','), details: items[0].details, state: items[0].state })) .value(); console.log(grouped);
原生JavaScript实现
const data = [ { docId: 222222, state: "accepted", details: [ { id: 20656, type: "Claimant", name: "First Name Last Name" }, { id: 10000, type: "Fellow", name: "Fellow First Name Fellow Last Name" } ] }, { docId: 1111111, state: "accepted", details: [ { id: 10000, type: "Fellow", name: "Fellow First Name Fellow Last Name" }, { id: 20656, type: "Claimant", name: "First Name Last Name" } ] }, { docId: 333333, state: "rejected", details: [ { id: 10000, type: "Fellow", name: "Fellow First Name Fellow Last Name" } ] }, { docId: 444444, state: "rejected", details: [ { id: 20656, type: "Claimant", name: "First Name Last Name" } ] } ]; // 分组逻辑 const groupedMap = data.reduce((acc, item) => { // 生成分组键 const sortedIds = item.details .map(d => d.id) .sort((a, b) => a - b) .join(','); const key = `${item.state}_${sortedIds}`; if (!acc[key]) { acc[key] = []; } acc[key].push(item); return acc; }, {}); // 转换为预期格式 const grouped = Object.values(groupedMap).map(items => ({ docId: items.map(item => item.docId).join(','), details: items[0].details, state: items[0].state })); console.log(grouped);
说明
- 排序id数组是为了消除details中id顺序不同的影响,确保相同id集合的条目生成相同的分组键
- 两种实现均会输出符合预期的分组结果,后续可在此基础上添加排序逻辑
内容的提问来源于stack exchange,提问作者Whoopcg
相关产品推荐
相关产品推荐

