小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
前言
上篇,我们一起学些了querystring, compose , promise的顺序执行,今天我们继续数组的奇妙之旅。
大家,动起来。
数组数据合并
这里数组合并说的两个数组,各有所需的数据的一部分,然后合并起来。 更多详情参见:两个数组数据的高效合并方案。
其里面巧妙的应用了 Array.from , reduce, includes等等数组方法以及迭代器等等思想。
贴一段代码:
export function mergeArray<S = any, T = any, R = any>(targetArr: T[] = [], sourceArr: S[] = [], options: MergeOptions<S, T> = DEFAULT_MERGE_OPTIONS): R[] {
// 有一个不是数组
if (!Array.isArray(sourceArr) || !Array.isArray(targetArr)) {
// return [...targetArr] as any[];
return targetArr as any;
}
const opt: MergeOptions = { ...DEFAULT_MERGE_OPTIONS, ...options };
// 判断sourceKey和sourceProperties
if (typeof opt.sourceKey !== "string") {
console.error("无效的soureKey");
return targetArr as any[];
}
const wTypes = ["string", "number"];
// TODO:: 更安全的检查
const getSKeyFn = typeof opt.sourceKey === "function" ? opt.sourceKey : (s: S) => s[opt.sourceKey as string];
let { targetKey } = opt;
if (targetKey == null) {
targetKey = opt.sourceKey;
}
const getTKeyFn = typeof targetKey === "function" ? targetKey : (t: T) => t[opt.targetKey as string];
const objMap: Record<string, S> = sourceArr.reduce((obj: Record<string, S>, cur: S) => {
const key = getSKeyFn(cur);
if (wTypes.includes(typeof key)) {
obj[cur[key]] = cur
}
return obj;
}, Object.create(null));
const { desc, sourceKey, sKMap = null } = opt;
const sourceLen = sourceArr.length;
let hitCounts = 0;
let walkCounts = 0;
let resultArr = Array.from(targetArr);
const targetLen = targetArr.length;
let tempTObj, keyValue, tempSObj;
const stepIter = getStepIter(0, targetLen - 1, desc);
while (stepIter.hasNext()) {
const index = stepIter.current;
walkCounts++
if (walkCounts > MAX_WLAK_COUNT) {
console.error(`mergeArray 遍历次数超过最大遍历次数 ${MAX_WLAK_COUNT}, 终止遍历,请检查程序逻辑`);
break;
}
tempTObj = resultArr[index];
// 目标比对的键值
keyValue = tempTObj[getTKeyFn(tempTObj)];
if (keyValue == null || (tempSObj = objMap[keyValue]) == null || tempSObj[sourceKey] != keyValue) {
stepIter.next()
continue;
}
resultArr[index] = mergeObject(tempTObj, tempSObj, undefined, sKMap);
hitCounts++
if (hitCounts >= sourceLen) {
break;
}
stepIter.next()
}
console.log(`mergeArray:: sourceArr(${sourceLen}), 统计:遍历次数${walkCounts}, 命中次数${hitCounts}`);
return resultArr as any[];
}
分组
就是把某些属性值相同值得项分为为一组:
const hasOwn = Object.prototype.hasOwnProperty;
function group(arr, fn) {
// 不是数组
if (!Array.isArray(arr)) {
return arr;
}
// 不是函数
if (typeof fn !== "function") {
return Array.from(arr);
}
var result = {};
var v;
arr.forEach((val, index) => {
v = fn(val, index);
if (!hasOwn.call(result, v)) {
result[v] = []
}
result[v].push(val);
});
return result;
}
let result = group(["apple", "pear", "orange", "peach"],
v => v.length);
console.log(result);
// { '4': [ 'pear' ], '5': [ 'apple', 'peach' ], '6': [ 'orange' ] }
result = group([{
name: "tom",
score: 88
},{
name: "Jim",
score: 80
},{
name: "Nick",
score: 88
}], v=> v.score)
console.log(result);
//{
// '80': [ { name: 'Jim', score: 80 } ],
// '88': [ { name: 'tom', score: 88 }, { name: 'Nick', score: //88 } ]
//}
目前这种实现是有一些缺点的,如果你第二个参数函数最后返回值是对象,因为对象作为键的时候,会被转为 [object Object]字符串,所以啊,都是相同的项。
result = group([{
name: "tom",
score: 88
},{
name: "Jim",
score: 80
},{
name: "Nick",
score: 88
}], v=> v)
console.log(result);
{
'[object Object]': [
{ name: 'tom', score: 88 },
{ name: 'Jim', score: 80 },
{ name: 'Nick', score: 88 }
]
}
不过这也算是合理的,如果计算返回是对象,大概率都是不同的对象,即使使用Set来排他,也是不合理的。
小结
今天学习了数组的分组和合并。 今天你收获了吗?
引用
数组 MDN
JS数组奇巧淫技
25个你不得不知道的数组reduce高级用法
13 个 JS 数组精简技巧,一起来看看
JavaScript数组操作_专题_脚本之家