当通过同时查看2个或多个关键点来确定唯一性时,如何从对象数组中删除重复项



我有一个很长的对象数组(>10-0000(,其中包含我想要删除的重复对象。

为了定位重复项,我必须查看两个对象属性:a, b

这里有一些通过一个属性删除对象的优雅答案:JavaScript:删除共享相同属性值的对象的重复项

例如

const uniq = _.uniq(arr, ele => ele.value}); 

以下是解决方案的输出:

const arr = [{a:1, b:1}, {a:1, b:1}, {a:2, b:2}];
const removeDuplcatesByTwoKeys = (arr, ['a', 'b']) => // only elements that are duplicates for both key values;
result: const arr = [{a:2, b:2}];

我试过_.uniq(arr, ele => ele.value && ele.otherValue});,但它不起作用。

另一种方法是创建由这些值键控的现有值的映射,例如

function unique(arr, keyProps) {
let map = new Map();
const kvArray = arr.map(entry => {
return keyProps.map(k => entry[k]).join('|');
})
kvArray.map(kv => {
if(map.has(kv)) {
const val = map.get(kv)
map.set(kv, val + 1)
} else {
map.set(kv, 1)
}
})
}

尽管这会告诉你重复的是什么,但从原始数组中删除它们的最佳方法是什么?这感觉像是一个比需要的更复杂的解决方案

通过两个属性从对象数组中删除重复项的性能方法是什么

您可以将具有这两个属性的_.uniq用作JSON字符串这样,每个元素都可以通过一个统一的系统与其他元素进行比较。

例如,

const arr = [{a:1, b:1}, {a:1, b:1}, {a:2, b:2}];
const removeDuplcatesByTwoKeys = _.uniq(arr, el => JSON.stringify({a: el.a, b: el.b}));
console.log(removeDuplcatesByTwoKeys)
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>

这也可以通过以下方式解决:

const removeDuplcatesByTwoKeys = array.filter((val, index) => {
return array.findIndex((row) => (
row.a === val.a && row.b === val.b
))
})

我已经读到findIndex在大型阵列中不具有性能,但在这方面并不是100%。这种方法允许您根据需要检查尽可能多的密钥,而不必关心顺序。

数组的一个问题是O(n(查找时间。大O概念,根本没有办法绕过它。我在这里的第一个建议是研究用O(1(查找时间存储数据的其他方法。在JavaScript中,您的首选解决方案是使用Map、Set或简单的JavaScript对象。你在这里的选择真的取决于你的需要。

映射是一个键值对系统。因此,您可以通过键设置和获取值。这与JavaScript对象非常相似。主要区别在于Map是有序的,因此可以对其进行迭代,并保证结果将按插入时间排序。此外,Map的键可以是任何数据类型,而JavaScript对象可能只有一个字符串。

集合基本上是一个O(1(查找数组。这里的限制是不能有重复的值,尽管它仍然是按插入时间排序的。

如果你无法控制如何接收数据,这实际上成为了一个很常见的面试问题。虽然解决这个问题很容易,但真正的挑战在于以一种高效的方式解决它。普遍接受的解是O(n(。您只需对数组进行迭代,然后将值或标识特征添加到Set中。当你遇到一个已经在集合中的值时,你可以跳过它。在数组的一次迭代结束时,你会得到所有唯一的值。包罗万象的算法根本没有办法更快地解决这个问题。

对于您的特定问题,我可能建议使用映射,以便您可以使用对象的字符串化值作为键。当您希望使用对象时,您也可以使用一个集合并只解析JSON。第三种可能也是理想的解决方案是,如果一个对象包含一个唯一的值,比如id。在这种情况下,您可以只使用这个id作为数组中的键。这样可以防止对象属性排序出现问题。

const arr = [{a:1, b:1}, {a:1, b:1}, {a:2, b:2}];
const map = new Map();
arr.forEach((val) => {
const stringified = JSON.stringify(val);
if (!map.has(stringified)) {
map.set(stringified, val);
}
});
console.log(map.values()); // MapIterator { { a: 1, b: 1 }, { a: 2, b: 2 } }

我会犹豫在浏览器中使用这个解决方案,因为我不确定是否会采用最近的功能,如地图和集合。然而,在node.js中,这将是最具性能的方法。

最新更新