挑战看200 个npm模块源码:第6个
目录:
一、介绍
1、原始类型
2、复合类型
3、具有自定义哈希函数的复合类型
二、个人手写
1、个人未看源码思路
2、个人手写代码
2.1:类型区分
2.2:原始数据类型去重
2.3:数组对象类型去重
2.4:具有自定义哈希函数的复合类型
2.5测试
三、源码分析
1、源码思路
四、总结
字数:大约900字
一、介绍
名称:dedupe
地址:https://github.com/seriousManual/dedupe.git
今天写的是,去重,面试中也经常遇到。
示例:
1、原始类型
js
import dedupe from 'dedupe'
const a = [1, 2, 2, 3]
const b = dedupe(a)
console.log(b)
//result: [1, 2, 3]
2、复合类型
js
import dedupe from 'dedupe'
const aa = [{a: 2}, {a: 1}, {a: 1}, {a: 1}]
const bb = dedupe(aa)
console.log(bb)
//result: [{a: 2}, {a: 1}]
3、具有自定义哈希函数的复合类型
js
const aaa = [
{a: 2, b: 1},
{a: 1, b: 2},
{a: 1, b: 3},
{a: 1, b: 4}
]
const bbb = dedupe(aaa, value => value.a)
console.log(bbb)
//result: [{a: 2, b: 1}, {a: 1,b: 2}]
二、个人手写
看了上面的示例,可以发现并不是简单的原始数据去重,刚好,可以稍微提升一点点难度。继续走。
1、个人未看源码思路
1、根据不同数据类型来针对性处理。
2、判断数组类型、复合类型、基本类型三种类型
3、对于原始数组类型--> 用ES6 Set()
4、数组对象类型--> reduce+JSON.stringify去重
5、复合类型--> Set 哈希方式去重
2、个人手写代码
2.1:类型区分
js
const deduplicate = (data, fn) => {
if (!Array.isArray(data) || !data.length) throw new Error("list is not a Array or length === 0!");
if (fn) {
// 复合数据类型去重
} else if (typeof data[0] === 'object') {
// 数组对象类型去重
} else {
// 原始类型去重
}
}
2.2:原始数据类型去重
js
const deduplicate = (data, fn) => {
if (!Array.isArray(data) || !data.length) throw new Error("list is not a Array or length === 0!");
if (fn) {
// 复合数据类型去重
} else if (typeof data[0] === 'object') {
// 数组对象类型去重
} else {
// 原始类型去重
return Array.from(new Set(data));
}
}
2.3:数组对象类型去重
js
const deduplicate = (data, fn) => {
if (!Array.isArray(data) || !data.length) throw new Error("list is not a Array or length === 0!");
if (fn) {
// 复合数据类型去重
} else if (typeof data[0] === 'object') {
// 数组对象类型去重
return data.reduce((prev, curr) => {
if (JSON.stringify(prev).indexOf(JSON.stringify(curr)) === -1) {
prev.push(curr);
}
return prev;
}, [])
} else {
// 原始类型去重
return Array.from(new Set(data));
}
}
2.4:具有自定义哈希函数的复合类型
js
const deduplicate = (data, fn) => {
if (!Array.isArray(data) || !data.length) throw new Error("list is not a Array or length === 0!");
if (fn) {
// 复合数据类型去重
const hashSet = new Set();
return data.filter((item) => {
const hash = getHash(item,fn);
return !hashSet.has(hash) ? hashSet.add(hash) : false;
});
} else if (typeof data[0] === 'object') {
// 数组对象类型去重
return data.reduce((prev, curr) => {
if (JSON.stringify(prev).indexOf(JSON.stringify(curr)) === -1) {
prev.push(curr);
}
return prev;
}, [])
} else {
// 原始类型去重
return Array.from(new Set(data));
}
}
const getHash = (obj,fn) => {
const _v = fn(obj);
const hash = _v || null;
return hash;
}
2.5测试
js
const aaa = [1, 2, 2, 3]
deduplicate(aaa); // [1,2,3]
const aaa = [{a: 2}, {a: 1}, {a: 1}, {a: 1}]
deduplicate(aaa); // [{"a": 2},{"a": 1}]
const aaa = [
{a: 2, b: 1},
{a: 1, b: 2},
{a: 1, b: 3},
{a: 1, b: 4}
]
deduplicate(aaa, obj => obj.a)
// [{"a": 2,"b": 1},
// {"a": 1,"b": 2}]
🍺🍺🍺
三、源码分析
ts
type Hasher<T> = (input: T) => string
function dedupe <T>(list: T[], hasher: Hasher<T> = JSON.stringify) {
const clone: T[] = []
const lookup: Record<string, boolean> = {}
for (let i = 0; i < list.length; i++) {
let entry = list[i]
let hashed = hasher(entry)
if (!lookup[hashed]) {
clone.push(entry)
lookup[hashed] = true
}
}
return clone
}
export default dedupe
3.1:源码思路
此源码没有区分数组具体类型,直接都是使用hash+JSON.stringify来实现。相比起来,他的代码量很少,很好理解,而我们的是区分来去处理,所以代码量有点多。但是都用JSON.stringify会影响性能。那我们来看看性能区别呢。
性能对比
1、原始数据数组 手写vs源码: 1ms vs 4ms
2、数据对象数组 手写vs源码: 1ms vs 1ms
3、具有自定义哈希函数的复合类型 手写vs源码: 1.5ms vs 1ms
性能方面综合而来,我们手写的胜出。 但是代码可读性,他的更佳。
四、总结
1、数组对象的 indexOf 方法使用的是全等比较(===)
2、尽可能的少用if else
3、多情况条件去重,用高阶函数(学了就要用,前面刚好学完)实现是个好的方式。
4、善于使用默认参数