数组中的数据根据key去重
实现一个函数,对数组中的对象根据指定的key进行去重,保留第一个或最后一个出现的元素
问题
在实际开发中,我们经常需要对对象数组进行去重操作。与基本类型数组去重不同,对象数组需要根据对象的某个属性(key)来判断是否重复。例如,一个用户列表中可能存在多个相同 id 的用户数据,我们需要根据 id 进行去重,只保留其中一条记录。
解答
/**
* 根据指定的key对对象数组进行去重
* @param {Array} arr - 需要去重的数组
* @param {String} key - 用于判断重复的属性名
* @param {Boolean} keepLast - 是否保留最后一个,默认保留第一个
* @returns {Array} 去重后的新数组
*/
function uniqueByKey(arr, key, keepLast = false) {
// 方法1: 使用 Map(推荐)
const map = new Map();
if (keepLast) {
// 保留最后一个:正序遍历,后面的会覆盖前面的
arr.forEach(item => {
map.set(item[key], item);
});
} else {
// 保留第一个:正序遍历,只在不存在时添加
arr.forEach(item => {
if (!map.has(item[key])) {
map.set(item[key], item);
}
});
}
return Array.from(map.values());
}
/**
* 方法2: 使用 reduce
*/
function uniqueByKeyReduce(arr, key, keepLast = false) {
const map = arr.reduce((acc, item) => {
if (keepLast || !acc.has(item[key])) {
acc.set(item[key], item);
}
return acc;
}, new Map());
return Array.from(map.values());
}
/**
* 方法3: 使用 filter + findIndex
* 注意:此方法性能较差,时间复杂度 O(n²)
*/
function uniqueByKeyFilter(arr, key, keepLast = false) {
return arr.filter((item, index) => {
const findIndex = keepLast
? arr.findLastIndex(obj => obj[key] === item[key])
: arr.findIndex(obj => obj[key] === item[key]);
return findIndex === index;
});
}
/**
* 方法4: 支持多个key去重
*/
function uniqueByKeys(arr, keys) {
const map = new Map();
arr.forEach(item => {
// 将多个key的值组合成唯一标识
const uniqueKey = keys.map(k => item[k]).join('|');
if (!map.has(uniqueKey)) {
map.set(uniqueKey, item);
}
});
return Array.from(map.values());
}
使用示例
// 示例数据
const users = [
{ id: 1, name: '张三', age: 25 },
{ id: 2, name: '李四', age: 30 },
{ id: 1, name: '张三(重复)', age: 26 },
{ id: 3, name: '王五', age: 28 },
{ id: 2, name: '李四(重复)', age: 31 }
];
// 1. 根据 id 去重,保留第一个
console.log(uniqueByKey(users, 'id'));
// 输出: [
// { id: 1, name: '张三', age: 25 },
// { id: 2, name: '李四', age: 30 },
// { id: 3, name: '王五', age: 28 }
// ]
// 2. 根据 id 去重,保留最后一个
console.log(uniqueByKey(users, 'id', true));
// 输出: [
// { id: 1, name: '张三(重复)', age: 26 },
// { id: 2, name: '李四(重复)', age: 31 },
// { id: 3, name: '王五', age: 28 }
// ]
// 3. 根据 name 去重
console.log(uniqueByKey(users, 'name'));
// 4. 多个key组合去重
const products = [
{ category: 'A', type: 'X', price: 100 },
{ category: 'A', type: 'Y', price: 200 },
{ category: 'A', type: 'X', price: 150 }, // 重复
{ category: 'B', type: 'X', price: 300 }
];
console.log(uniqueByKeys(products, ['category', 'type']));
// 输出: [
// { category: 'A', type: 'X', price: 100 },
// { category: 'A', type: 'Y', price: 200 },
// { category: 'B', type: 'X', price: 300 }
// ]
关键点
-
使用 Map 数据结构:Map 的 key 可以是任意类型,且能保持插入顺序,非常适合去重场景,时间复杂度为 O(n)
-
保留策略:通过
keepLast参数控制保留第一个还是最后一个重复项,灵活应对不同业务需求 -
性能考虑:推荐使用 Map 方案,避免使用 filter + findIndex 的嵌套循环方式(O(n²)复杂度)
-
多key去重:当需要根据多个属性组合判断唯一性时,可以将多个 key 的值拼接成字符串作为 Map 的键
-
不可变性:所有方法都返回新数组,不修改原数组,符合函数式编程思想
-
边界处理:需要考虑 key 不存在、值为 undefined/null 等边界情况,可根据实际需求添加校验逻辑
目录