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

Javascript 当通过同时查看两个或多个关键点来确定唯一性时,如何从对象数组中删除重复项,javascript,performance,duplicates,Javascript,Performance,Duplicates,我有一个很长的对象数组(>10_000),其中包含我想删除的重复对象 为了找到副本,我必须查看两个对象属性:a,b 对于通过一个属性删除对象,这里有一些优雅的答案: 例如 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']) =

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

为了找到副本,我必须查看两个对象属性:
a,b

对于通过一个属性删除对象,这里有一些优雅的答案:

例如

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}];
我尝试了
\uq.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)
        }
    })
}
虽然这会告诉您重复项是什么,但从原始阵列中删除它们的最佳方法是什么?这感觉像是一个比需要更复杂的解决方案


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

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

比如说,

const arr=[{a:1,b:1},{a:1,b:1},{a:2,b:2}];
const removeDuplcatesByTwoKeys=uuq.uniq(arr,el=>JSON.stringify({a:el.a,b:el.b}));
console.log(移除uplvatesbytwokeys)

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

   const removeDuplcatesByTwoKeys = array.filter((val, index) => {
     return array.findIndex((row) => (
       row.a === val.a && row.b === val.b
     ))
   })
我已经读到,
findIndex
在大型阵列上性能不佳,但在这方面不是100%。
此方法允许您根据需要检查任意多个键,而不必考虑顺序

数组的一个问题是O(n)查找时间,根本没有办法解决这个问题。我在这里的第一个建议是研究以O(1)查找时间存储数据的其他方法。在JavaScript中,您的解决方案将使用、或简单的JavaScript。你在这里的选择取决于你的需要

映射是一个键值对系统。这样,您就可以通过一个键来设置和获取一个值。这与JavaScript对象非常相似。主要区别在于,映射是有序的,因此可以在保证结果按插入时间排序的情况下对其进行迭代。此外,映射的键可以是任何数据类型,而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中,这将是最有效的方法。

建议的解决方案适用于整数数组,但这是一个对象数组,由于数组中对象之间的两个属性相等,需要删除重复项。您从何处获取此数组?因为在这样的情况下,典型的解决方案是“不要使用数组,使用一种旨在使其成为快速高效操作的数据结构”,所以某种类型的列表管理器,对要筛选的属性使用B树,以便在插入时进行筛选,以及后期处理。@Mike'Pomax'Kamermans-这是来自用户上传的CSV文件的数据。API需要一个对象数组。我怀疑有一种方法可以在不改变API的情况下实现这一点。这种方法的潜在缺陷是什么?我认为数据是统一的,所以键顺序应该无关紧要。@zero\u我能想到的唯一陷阱是如果元素之间的键顺序不同,除此之外,我认为这是实现目标的一种简单方法。findIndex在幕后的工作原理是它遍历数组,直到您提供的函数返回true。它基本上是一个for循环,将其呈现为一个O(n)函数。此解决方案工作得非常好,但会随着数据集的增长而变慢,因为它是一个O(n^2)解决方案