给定一个数组数组,识别重复项的有效方法是什么?
var array = [
[
11.31866455078125,
44.53836644772605
],
[ // <-- Here's the duplicate
11.31866455078125,
44.53836644772605
],
[
11.371536254882812,
44.53836644772605
],
[
11.371536254882812,
44.50140292110874
]
]
我一直在使用lodash
作为一个公认的依赖关系,我得到了如何返回&#34; unique&#34;列表使用_.uniqWith
和_.isEqual
:
_.uniqWith(array,_.isEqual)
随着将给予&#34;独特的&#34;列表的版本:
[
[ 11.31866455078125, 44.53836644772605 ],
[ 11.371536254882812, 44.53836644772605 ],
[ 11.371536254882812, 44.50140292110874 ]
]
但是,不仅仅是报告唯一元素,我只需要复制的元素,理想情况下是第一次出现的索引。
lodash
库中是否实际涵盖了我错过的一些方法组合?或者我只需要编写循环来比较元素。
可能只是在这方面过度了,所以对这个问题的新观点是值得欢迎的。
如果有适合的库方法,请尽量不重写函数,所以我基本上坚持:
仅使用&#34;唯一列表&#34;返回重复或至少比较差异。
基本上确定&#34;&#34;指数&#34;数组中的数组。虽然我认为一旦识别出重复的项目,可以使用_.isEqual
减少过滤器。
还试图避免创建一个对象Hash / Map并在这里计算键的出现次数,或者至少不作为一个单独的对象,以及可以在功能上完成的事情&#34; in-line&#34;
答案 0 :(得分:5)
Lodash提供了许多有用的功能来实现找到第一个重复索引 使用_.findIndex()和_.isEqual()以下代码将找到第一个重复索引:
var duplicateIndex = _.findIndex(array, function(value, index, collection) {
var equal = _.isEqual.bind(undefined, value);
return _.findIndex(collection.slice(0, index), equal) !== -1;
});
或者更快但更详细:
var duplicateIndex = _.findIndex(array, function(value, index, collection) {
var equal = _.isEqual.bind(undefined, value);
return _.findIndex(collection, function(val, ind) {
return ind < index && equal(val);
}) !== -1;
});
请注意,如果不存在重复,则会返回-1
简而言之,算法遍历数组并回顾当前元素是否已经存在。如果是,则返回当前的迭代索引
请检查工作demo。
答案 1 :(得分:2)
这是一种使用uniqWith()和difference()的方法:
_.indexOf(array, _.head(_.difference(array, _.uniqWith(array, _.isEqual))));
基本理念是:
uniqWith()
删除array
。difference()
将array
与无重复版本进行比较。这为我们提供了一系列重复项。1
。但是,如果您需要原始的索引,而不是重复,我们必须进行一些调整:
var duplicate = _.head(_.difference(array, _.uniqWith(array, _.isEqual)));
_.findIndex(array, _.unary(_.partial(_.isEqual, duplicate)));
我们仍然使用uniqWith()
和difference()
来查找duplicate
。但现在,我们正在使用findIndex()来获取索引。原因是我们需要使用isEqual()来查找副本的第一个位置,而不是第二个。我们使用partial()和unary()构建谓词。这次的结果是0
。
答案 2 :(得分:1)
你可以使用普通的&#39; javascript要做到这一点,它并不难,这是我的实现
for (var i = 0; i < array.length; i++) {
for (var j = i + 1; j < array.length; j++) {
// quick elimination by comparing subarray lengths
if (array[i].length !== array[j].length) {
continue;
}
// look for dupes
var dupe = true;
for (var k = 0; k < array[i].length; k++) {
if (array[i][k] !== array[j][k]) {
dupe = false;
break;
}
}
// if a dupe then print
if (dupe) {
console.debug("%d is a dupe", j);
}
}
}
关于这个实现的好处是它会打印多次,索引处的数组是多个dupe的欺骗,你可以使用这个事实来计算每个索引中的dupe!
这实际上是一种非常有效的方法,因为内部for
循环(j
)总是从外循环的下一个位置(i
)运行。所以你的支票数量减半。
这是一个plunk
答案 3 :(得分:1)
我不知道如何做到这一点,而不仅仅是自己编写算法。这个答案和其他发布的答案都不是很有效但应该没问题:
function findIndex(array, startingIndex, value) {
var predicate = _.partial(_.isEqual, value);
var arraySubset = array.slice(startingIndex+1);
var index = arraySubset.findIndex(predicate);
return index === -1 ? index : index+startingIndex+1;
}
function findDuplicates(array) {
return array.map((value, index) => {
return {
value,
index: findIndex(array, index, value)
};
}).filter(info => info.index !== -1);
}
findDuplicates([1, 2, 3, 4, 1, [ 3 ], [ 4 ], [ 3 ] ]);
// [ { value: 1, index: 4 }, { value: [ 3 ], index: 7 } ] // [ { value: 1, index: 4 }, { value: [ 3 ], index: 7 } ]
这基本上创建了一个数组的映射,在数组的其余部分调用.findIndex(),记下任何重复项的索引,返回有重复项的每个项的信息以及重复项的索引。
关于这一点的一个好处是,它可以适用于三次重复或任何数量的值。
答案 4 :(得分:1)
我认为构建LUT是进行比较时最有效的方法之一。下面的方法利用Array.prototype.reduce()
构造一个LUT,并最终通过删除一个而不是所有重复的元素来改变原始数组,而不管它们有多少。
var arr = [
[
11.31866455078125,
44.53836644772605
],
[
11.31866455078125,
44.53836644772605
],
[
11.371536254882812,
44.53836644772605
],
[
11.371536254882812,
44.50140292110874
]
];
arr.reduce((p,c,i)=> { var prop = c[0]+"" + c[1]+"";
p[prop] === void 0 ? p[prop] = i : p.dups.push(i);
return p;
},{dups:[]}).dups.reverse().forEach( i => arr.splice(i,1))
document.write('<pre>' + JSON.stringify(arr, 0, 2) + '</pre>');
&#13;
但是如果你希望通过保留原始数据来获得一个新阵列,那么显然会更快一些。