Lodash 源码阅读-unzip
概述
unzip函数用于将打包的数组解包,即将一个分组的数组(如[[1, 3], [2, 4]])转换为一个重组的数组(如[[1, 2], [3, 4]])。它实质上是将多个数组中对应位置的元素重新组合成新数组,可视为zip操作的逆向过程。该函数会忽略非数组类型的元素,并基于最长数组元素进行结果填充。
前置学习
依赖函数
arrayFilter: 过滤数组中的元素,返回符合条件的元素组成的新数组isArrayLikeObject: 检查值是否为类数组对象nativeMax: 获取两个数值中的最大值baseTimes: 调用迭代函数 n 次,返回由迭代函数返回值组成的数组arrayMap: 对数组中的每个元素执行迭代函数,返回新数组baseProperty: 创建一个返回对象指定属性值的函数
技术知识
- 数组操作与转换
- 高阶函数的应用
- 函数式编程思想
- JavaScript 中的类数组对象概念
- 矩阵转置操作原理
源码实现
function unzip(array) {
if (!(array && array.length)) {
return [];
}
var length = 0;
array = arrayFilter(array, function (group) {
if (isArrayLikeObject(group)) {
length = nativeMax(group.length, length);
return true;
}
});
return baseTimes(length, function (index) {
return arrayMap(array, baseProperty(index));
});
}
实现思路
unzip首先检查输入是否为有效数组,- 然后过滤出所有类数组对象元素,同时计算出最长子数组的长度。
- 接着,基于这个最大长度创建结果数组,对于每个索引位置,从所有子数组中提取相同索引位置的元素形成新数组。
本质上,这是一个矩阵转置操作,将"行优先"的数据结构转换为"列优先"的结构。
源码解析
1. 参数校验与初始化
if (!(array && array.length)) {
return [];
}
var length = 0;
这段代码首先检查传入的array参数是否为真值且具有length属性。如果参数无效(例如为null、undefined或空数组),直接返回空数组。然后初始化length变量用于后续记录最长子数组的长度。
2. 数据预处理与有效性过滤
array = arrayFilter(array, function (group) {
if (isArrayLikeObject(group)) {
length = nativeMax(group.length, length);
return true;
}
});
使用arrayFilter过滤原数组,只保留类数组对象元素。对于每个符合条件的元素,通过nativeMax更新length变量,确保它始终等于所有子数组中的最大长度。这样处理确保了即使子数组长度不一,也能基于最长的子数组创建结果。
例如,对于输入[[1, 2, 3], [4, 5], 'not-an-array']:
- 第一个元素
[1, 2, 3]是类数组对象,保留,length更新为 3 - 第二个元素
[4, 5]是类数组对象,保留,length保持为 3 - 第三个元素
'not-an-array'不是类数组对象,被过滤掉 - 过滤后的数组为
[[1, 2, 3], [4, 5]],length为 3
3. 结果生成与矩阵转置
return baseTimes(length, function (index) {
return arrayMap(array, baseProperty(index));
});
这是实现矩阵转置的核心逻辑。baseTimes创建一个长度为length的新数组,对于每个索引位置index:
- 使用
baseProperty(index)创建一个函数,该函数返回对象的第index个属性值 - 使用
arrayMap对过滤后的数组中的每个子数组应用这个函数,提取第index个元素 - 最终结果是一个新数组,其中每个元素都是原数组中相同位置的元素集合
例如,对于过滤后的数组[[1, 2, 3], [4, 5]]和length=3:
- 索引 0:提取每个子数组的第 0 个元素,得到
[1, 4] - 索引 1:提取每个子数组的第 1 个元素,得到
[2, 5] - 索引 2:提取每个子数组的第 2 个元素,得到
[3, undefined](注意第二个子数组没有第 2 个元素) - 最终结果为
[[1, 4], [2, 5], [3, undefined]]
总结
- 巧用
nativeMax动态记录最大长度,解决了输入数组不等长的问题 - 通过
baseProperty与arrayMap组合实现了高效的矩阵转置操作 - 采用函数式编程思想,将复杂操作分解为独立且可组合的函数
- 使用
arrayFilter确保只处理有效输入,提高了代码的健壮性