
1. 问题定义与需求分析
在数据处理场景中,我们经常需要根据特定条件对数组进行分组或拆分。本教程关注一个特殊需求:给定一个包含多个javascript对象的数组,以及一个作为分割依据的字段名。我们需要将这个源数组拆分成多个子数组,其核心要求如下:
- 分割条件:当数组中的某个对象包含指定的字段时,该对象即被视为一个分割点。
- 重叠性:作为分割点的对象,必须同时包含在它所结束的当前子数组中,以及它所开始的下一个子数组中。
-
边缘情况处理:
- 如果数组的第一个对象就包含指定字段,它应作为第一个子数组的起始,但不应导致“前一个”子数组的分割。
- 如果数组的最后一个对象包含指定字段,它应作为最后一个子数组的结束,但不应导致“后一个”子数组的开始。
以下是示例数据及其期望的输出格式:
源数据:
const source = [
{ a:1, b:2, c: true},
{ d:1, e:2 },
{ x:1, y:2 },
{ q:1, s:2, c: true}, // 分割点,包含字段 'c'
{ da:1, eb:2 },
{ aaa:1, bbb:2 },
{ aa:1, bb:2 },
{ xa:1, ya:2 },
{ qa:1, sa:2, c: true} // 分割点,包含字段 'c'
];
const field = 'c';期望输出:
[
[
{ a:1, b:2, c: true},
{ d:1, e:2 },
{ x:1, y:2 },
{ q:1, s:2, c: true}, // 包含在第一个子数组的末尾
],
[
{ q:1, s:2, c: true}, // 包含在第二个子数组的开头 (与上一个重叠)
{ da:1, eb:2 },
{ aaa:1, bbb:2 },
{ aa:1, bb:2 },
{ xa:1, ya:2 },
{ qa:1, sa:2, c: true} // 包含在第二个子数组的末尾
]
]2. 核心算法解析
为了实现上述需求,我们可以采用单次遍历的策略,维护一个当前子集和一个最终结果集。
立即学习“Java免费学习笔记(深入)”;
2.1 初始化
- output = []: 这是一个空数组,用于存储最终生成的所有子数组。
- currentSet = []: 这是一个空数组,用于临时存储正在构建的当前子数组。
2.2 迭代逻辑
我们使用一个 for 循环遍历源数组 sourceArray 中的每一个对象。
-
添加当前元素:在每次循环的开始,无论当前对象是否是分割点,都将其添加到 currentSet 中。这是因为即使是分割点,它也总是属于它所结束的那个子集。
currentSet.push(sourceArray[i]);
-
分割条件判断:这是算法的核心部分。我们需要判断当前对象是否满足作为分割点的条件,并且该分割点不是数组的第一个或最后一个元素。
if (sourceArray[i].hasOwnProperty(fieldName) && i !== 0 && i !== sourceArray.length - 1) { // 执行分割操作 }- sourceArray[i].hasOwnProperty(fieldName):
- 这检查当前对象 sourceArray[i] 是否直接拥有 fieldName 这个属性。使用 hasOwnProperty 是一个良好的实践,它可以避免检查原型链上的属性,确保我们只关注对象自身的属性。
- i !== 0:
- 这个条件排除了数组的第一个元素。即使第一个元素包含 fieldName,它也只是作为第一个子数组的起点,而不应导致从一个不存在的“前一个”子数组进行分割。
- i !== sourceArray.length - 1:
- 这个条件排除了数组的最后一个元素。即使最后一个元素包含 fieldName,它也只是作为最后一个子数组的终点,而不应导致开启一个不存在的“后一个”子数组。
- sourceArray[i].hasOwnProperty(fieldName):
-
执行分割操作:如果上述三个条件都为真,说明我们找到了一个位于数组中间的有效分割点。
output.push(currentSet); // 将当前构建好的子数组添加到结果集中 currentSet = [sourceArray[i]]; // 重置 currentSet,并以当前分割点对象作为新子集的第一个元素
- output.push(currentSet):将当前已经累积的 currentSet 推入 output 数组。此时,currentSet 中包含了从上一个分割点(或数组开头)到当前分割点(包括当前分割点)的所有元素。
- currentSet = [sourceArray[i]]:这是实现“重叠”的关键一步。我们不是简单地清空 currentSet,而是用一个只包含当前分割点对象的数组来初始化 currentSet。这样,当前分割点对象就既是上一个子数组的结尾,也是下一个子数组的开头。
2.3 循环结束后的处理
当 for 循环结束后,currentSet 中可能还包含最后一个子数组的元素(从最后一个分割点到数组末尾的所有元素)。我们需要确保这个子数组也被添加到 output 中。
if (currentSet.length > 0) {
output.push(currentSet);
}这个条件判断是必要的,以防源数组为空或所有元素都已经被分割并推入 output。
3. 示例代码
将上述逻辑封装成一个可复用的函数:
/**
* 根据对象中特定字段的存在性拆分数组为重叠子集。
* 分割点对象会同时包含在它所结束的子集和它所开始的子集中。
*
* @param {Array










