# 数组交集
function baseIntersection (arrays, iteratee) {
const includes = arrayIncludes;
const length = arrays[0].length;
const othLength = arrays.length; // 获取传入的数组数量
const result = [];
let array;
let maxLength = Infinity;
let othIndex = othLength;
// 遍历数组,寻找数组元素最少的那组元素个数——交集数量
while (othIndex--) {
array = arrays[othIndex];
if (othIndex && iteratee) {
array = map(array, (value) => iteratee(value));
}
maxLength = Math.min(array.length, maxLength);
}
array = arrays[0];
let index = -1;
// 以第一个数组为基准,如果它存在的元素,其他元素也存在,则为交集元素,否则不为交集元素。
outer:
while(++index < length && result.length < maxLength) {
let value = array[index]; // 将第一个数组每一项依次取出
const computed = iteratee ? iteratee(value) : value;
if (!includes(result, computed)) { // 去重,重复元素不算交集
othIndex = othLength; // 初始化为数组的总长度
while(--othIndex) { // 从后向前遍历传入的数组
if (!includes(arrays[othIndex], computed)) { // 如果这个元素只要不在其中一个数组中,就跳出循环
continue outer;
}
}
result.push(value);
}
}
}
这段代码有点复杂,需要多看几遍。
需求:寻找N个数组共同存在的交集元素。
核心思想如下:
如果某个元素是arrays
的交集元素,则此元素肯定存在于所有的arrays
中,也肯定存在于第一个数组中。反之,如果在第一个数组中不存在的元素,肯定不是交集元素。
因此,根据这个特征,我们可以以第一个数组为标准,将第一个数组的元素依次取出,如果这个元素在其他数组都存在,那么这个元素肯定是交集元素。
# 值得回味的代码
# 确定交集长度
while (othIndex--) {
array = arrays[othIndex];
if (othIndex && iteratee) {
array = map(array, (value) => iteratee(value));
}
maxLength = Math.min(array.length, maxLength);
}
这段代码,能够确定最后交集数组的长度,因为这些数组中最短的长度,就是最后交集数组的长度。可以参考上面那段话,仔细回味一下。
# 验证是否为交集元素
if (!includes(result, computed)) { // 去重,重复元素不算交集
othIndex = othLength; // 初始化为数组的总长度
while(--othIndex) { // 从后向前遍历传入的数组
if (!includes(arrays[othIndex], computed)) { // 如果这个元素只要不在其中一个数组中,就跳出循环
continue outer;
}
}
result.push(value);
}
这段代码的第一句,就保证了交集去重。当元素已经存在于交集中时,不再进行判断。
然后获取传入的数组总个数,因为第一组的元素,每一次循环都需要在余下所有数组中查找是否存在。而--othIndex
为0时,就会结束循环,能够保证不会循环到第一个数组(第一个数组为参照数组)。
当元素在其他任意一个数组中不存在,表示此元素不为交集元素。
# 亮点
无论第一个数组是否为最短的,但是第一个(任意一个)数组中肯定是存在完整的交集结果。因此只需要知道最后交集的长度,就可以取得完整的交集数据。
可以说非常厉害了,值得回味。