面试遇到问数组去重,一开始我以为是简单的数组去重,后来面试官追问到其它极限情况呢?或者其它数据结构,所以简单研究了一下数据去重的情况,路过的大佬们指导指导。
数字数组和字符串数组
直接使用Set最优雅最高效,如果数组内包含undefined、null、""、0等临界值也会被去重。
const arr = [1, undefined, null, null, 2, 3, undefined, 0, 0, '', '','1','1','2','2','3'];
const result = Array.from(new Set(arr));
console.log(result); // [1, undefined, null, 2, 3, 0, '', '1', '2', '3']
对象数组
// 下面这种方法优雅且高效,但是会用后面的数据覆盖前面的数据,且对undefined、null、{}没有做处理
const arr = [
{ id: 1, name: 'A' },
{ id: 2, name: 'B' },
{ id: 1, name: 'C' },
];
const result = Array.from(
new Map(arr.map((item) => [`${item.id}`, item])).values()
);
console.log(result); // [{id: 1, name: 'C'},{id: 2, name: 'B'}]
// 下面这种方法更加推荐
const result = [];
const seen = new Set();
// 如果想实现上面的效果只需要去掉!seen.has(item.id)这段代码即可
for (const item of arr) {
if (item && Object.hasOwn(item, 'id') && !seen.has(item.id)) {
seen.add(item.id);
result.push(item);
}
}
以上是比较常见的数据结构,下面为不太常见的复杂数据结构。
多维数组
:::info 多维数据内的数据复杂程度非常高,且一般伴随着大数据量这一特性一起出现,下面将尽可能的展开说说不同情况的个人理解。
:::
二维数组
const arr = [
[1, 2],
[3, 4],
[1, 2],
[3, 4, 5],
[3, 4],
[],
[],
[undefined, 1],
[undefined],
['', 0],
['', 0],
['', undefined],
];
const result = [];
const seen = new Set();
for (const item of arr) {
const key = item.join('|'); // 用分隔符拼接
if (!seen.has(key)) {
seen.add(key);
result.push(item);
}
}
// [1, 2],[3, 4],[3, 4, 5],[],[undefined, 1],[undefined],['', 0],['', undefined]
// 下面这种用map的方法,性能略差,但是自由度很高,可以通过key和item控制内容和去重的判断内容
const map = new Map();
for (const item of arr) {
const key = JSON.stringify(item);
if (!map.has(key)) {
map.set(key, item);
}
}
console.log(map.values());
// [1, 2],[3, 4],[3, 4, 5],[],[undefined, 1],[undefined],['', 0],['', undefined]
三维及以上数组
const arr = [
[1, [2, 3]],
[1, [2, 3]],
[[]],
[[]],
[undefined],
[undefined],
[null],
[0],
[0],
[[0],0],
[[0],undefined],
];
// 用自定义的hash算法
function deepHash(value) {
const type = typeof value;
if (value === null) return "null";
if (type === "number" || type === "boolean") return value + "";
if (type === "string") return `"${value}"`;
if (Array.isArray(value)) {
let hash = "A"; // A = array
for (let i = 0; i < value.length; i++) {
hash += "|" + deepHash(value[i]);
}
return hash;
}
// 如果未来有对象,也支持
if (type === "object") {
let hash = "O";
const keys = Object.keys(value).sort(); // 避免乱序
for (const k of keys) {
hash += "|" + k + ":" + deepHash(value[k]);
}
return hash;
}
}
function deepUnique(arr) {
const seen = new Set();
const result = [];
for (const item of arr) {
const key = deepHash(item); // 非 JSON
const key = JSON.stringify(item); // 使用JSON字符串
if (!seen.has(key)) {
seen.add(key);
result.push(item);
}
}
return result;
}
const temp = deepUnique(arr);
console.log(temp);
// [[1,[2,3]],[[]],[null],[null],[0],[[0],0],[[0],null]]
树状对象数组
// 全局去重
function dedupeTree(tree, getKey) {
const seen = new Set();
function traverse(nodes) {
const result = [];
for (const node of nodes) {
const key = getKey(node);
if (seen.has(key)) continue;
seen.add(key);
const newNode = { ...node };
if (Array.isArray(newNode.children)) {
newNode.children = traverse(newNode.children);
}
result.push(newNode);
}
return result;
}
return traverse(tree);
}
// 层级去重
function dedupeTreeLevel(nodes, key = "id") {
const seen = new Set();
const result = [];
for (const node of nodes) {
if (!seen.has(node[key])) {
seen.add(node[key]);
const newNode = { ...node };
if (newNode.children) {
newNode.children = dedupeTreeLevel(newNode.children, key);
}
result.push(newNode);
}
}
return result;
}