
在前端或后端开发中,我们经常会遇到需要对数据列表进行复杂排序的场景。其中一种常见的需求是,数据项之间存在层级关系(例如,父子关系),同时每个数据项还有自身的显示优先级。我们的目标是将子项紧随其父项之后显示,并且在同级(包括顶层父项和同一父项下的子项)之间,需要根据display_priority进行升序排列。
考虑以下原始数据结构,它是一个包含多个对象的数组,每个对象有id、name、reference_id和display_priority字段:
const rawData = [
{ id: 3, name: 'hello world', reference_id: null, display_priority: 10},
{ id: 6, name: 'hello world', reference_id: 2 , display_priority: 30},
{ id: 1, name: 'hello world', reference_id: 2, display_priority: 40 },
{ id: 4, name: 'hello world', reference_id: null, display_priority: 80},
{ id: 2, name: 'hello world', reference_id: null, display_priority: 100 },
{ id: 5, name: 'hello world', reference_id: 3, display_priority: 110 },
];我们期望的输出结果应遵循以下规则:
根据上述规则,期望的输出如下:
[
{ id: 3, name: 'hello world', reference_id: null, display_priority: 10 }, // 父项
{ id: 5, name: 'hello world', reference_id: 3, display_priority: 110 }, // 子项,display_priority: 110
{ id: 4, name: 'hello world', reference_id: null, display_priority: 80 }, // 父项
{ id: 2, name: 'hello world', reference_id: null, display_priority: 100 }, // 父项
{ id: 6, name: 'hello world', reference_id: 2, display_priority: 30}, // 子项,display_priority: 30
{ id: 1, name: 'hello world', reference_id: 2, display_priority: 40}, // 子项,display_priority: 40
]可以看到,id: 3是第一个父项(display_priority: 10),其子项id: 5(reference_id: 3)紧随其后。接着是id: 4(display_priority: 80)和id: 2(display_priority: 100)这两个顶层父项,它们之间按display_priority排序。最后是id: 2的子项id: 6和id: 1,它们也按display_priority排序。
要实现这种复杂的重排序,我们面临以下几个主要挑战:
原始尝试的reduce方法虽然试图解决父子插入问题,但它存在局限性:它依赖于父项在数组中出现的顺序,如果父项在子项之后,则可能无法正确插入。更重要的是,它没有完全处理display_priority的排序逻辑。
为了克服上述挑战,我们将采用一个结构化的分步方法:
首先,我们需要对原始数据进行预处理,以便快速查找元素及其子项,并将所有元素分为顶层父项和子项。
// 示例数据
const rawData = [
{ id: 3, name: 'hello world', reference_id: null, display_priority: 10},
{ id: 6, name: 'hello world', reference_id: 2 , display_priority: 30},
{ id: 1, name: 'hello world', reference_id: 2, display_priority: 40 },
{ id: 4, name: 'hello world', reference_id: null, display_priority: 80},
{ id: 2, name: 'hello world', reference_id: null, display_priority: 100 },
{ id: 5, name: 'hello world', reference_id: 3, display_priority: 110 },
];
const parents = []; // 存储所有顶层父项
const childrenMap = new Map(); // 存储父项ID到其子项列表的映射
rawData.forEach(item => {
if (item.reference_id === null) {
parents.push(item);
} else {
if (!childrenMap.has(item.reference_id)) {
childrenMap.set(item.reference_id, []);
}
childrenMap.get(item.reference_id).push(item);
}
});在构建最终数组之前,我们需要确保所有顶层父项和每个父项下的子项都已根据display_priority进行排序。
// 排序顶层父项
parents.sort((a, b) => a.display_priority - b.display_priority);
// 排序每个父项下的子项
childrenMap.forEach(childList => {
childList.sort((a, b) => a.display_priority - b.display_priority);
});现在我们有了排序好的顶层父项和每个父项下排序好的子项列表,可以开始构建最终的有序数组。
遍历排序后的parents数组。对于每个父项,将其添加到结果数组中,然后查找其在childrenMap中对应的子项列表,并将这些子项也依次添加到结果数组中。
const reorderedArray = [];
parents.forEach(parent => {
reorderedArray.push(parent); // 添加父项
if (childrenMap.has(parent.id)) {
const sortedChildren = childrenMap.get(parent.id);
reorderedArray.push(...sortedChildren); // 添加排序后的子项
}
});将上述步骤整合到一个函数中,可以得到一个完整的解决方案:
/**
* 根据父子关系和显示优先级对数组进行重排序。
* 父项通过 reference_id: null 标识,子项通过 reference_id 关联父项 id。
* 所有同级元素(包括顶层父项和同一父项下的子项)按 display_priority 升序排列。
*
* @param {Array<Object>} data 原始数据数组,每个对象需包含 id, reference_id, display_priority。
* @returns {Array<Object>} 重排序后的数组。
*/
function reorderArrayByParentAndPriority(data) {
if (!data || data.length === 0) {
return [];
}
const parents = []; // 存储所有顶层父项
const childrenMap = new Map(); // 存储父项ID到其子项列表的映射
// 步骤一:构建数据索引与分类
data.forEach(item => {
// 确保 display_priority 存在且为数字,否则默认为一个大值以防排序异常
const priority = typeof item.display_priority === 'number' ? item.display_priority : Infinity;
const itemWithPriority = { ...item, display_priority: priority };
if (item.reference_id === null) {
parents.push(itemWithPriority);
} else {
if (!childrenMap.has(item.reference_id)) {
childrenMap.set(item.reference_id, []);
}
childrenMap.get(item.reference_id).push(itemWithPriority);
}
});
// 步骤二:应用显示优先级排序
// 排序顶层父项
parents.sort((a, b) => a.display_priority - b.display_priority);
// 排序每个父项下的子项
childrenMap.forEach(childList => {
childList.sort((a, b) => a.display_priority - b.display_priority);
});
// 步骤三:构建最终有序数组
const reorderedArray = [];
parents.forEach(parent => {
reorderedArray.push(parent); // 添加父项
if (childrenMap.has(parent.id)) {
const sortedChildren = childrenMap.get(parent.id);
reorderedArray.push(...sortedChildren); // 添加排序后的子项
}
});
return reorderedArray;
}
// 原始数据
const rawData = [
{ id: 3, name: 'hello world', reference_id: null, display_priority: 10},
{ id: 6, name: 'hello world', reference_id: 2 , display_priority: 30},
{ id: 1, name: 'hello world', reference_id: 2, display_priority: 40 },
{ id: 4, name: 'hello world', reference_id: null, display_priority: 80},
{ id: 2, name: 'hello world', reference_id: null, display_priority: 100 },
{ id: 5, name: 'hello world', reference_id: 3, display_priority: 110 },
];
const result = reorderArrayByParentAndPriority(rawData);
console.log(result);
/*
期望输出:
[
{ id: 3, name: 'hello world', reference_id: null, display_priority: 10 },
{ id: 5, name: 'hello world', reference_id: 3, display_priority: 110 },
{ id: 4, name: 'hello world', reference_id: null, display_priority: 80 },
{ id: 2, name: 'hello world', reference_id: null, display_priority: 100 },
{ id: 6, name: 'hello world', reference_id: 2, display_priority: 30 },
{ id: 1, name: 'hello world', reference_id: 2, display_priority: 40 }
]
*/通过本教程,我们学习了如何处理一个常见的复杂数组重排序问题,即同时考虑父子关系和显示优先级。关键在于将问题分解为几个可管理的步骤:首先进行数据索引和分类,然后对各个层级的数据独立进行优先级排序,最后将这些排序好的数据结构化地组合起来。这种分步处理的方法不仅使代码逻辑清晰,而且在处理大规模数据时也能保持较好的性能。这种策略可以推广到更复杂的树形结构排序场景,只需递归地应用类似的分层排序思想。
以上就是高效重排数组:基于父子关系与显示优先级的复杂排序策略的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号