function uniqBy(a, key) {
var seen = {};
return a.filter(function(item) {
var k = key(item);
return seen.hasOwnProperty(k) ? false : (seen[k] = true);
})
}
function uniq_fast(a) {
var seen = {};
var out = [];
var len = a.length;
var j = 0;
for(var i = 0; i < len; i++) {
var item = a[i];
if(seen[item] !== 1) {
seen[item] = 1;
out[j++] = item;
}
}
return out;
}
function uniq(a) {
var seen = {};
return a.filter(function(item) {
return seen.hasOwnProperty(item) ? false : (seen[item] = true);
});
}
function uniq_fast(a) {
var seen = {};
var out = [];
var len = a.length;
var j = 0;
for(var i = 0; i < len; i++) {
var item = a[i];
if(seen[item] !== 1) {
seen[item] = 1;
out[j++] = item;
}
}
return out;
}
/////
var r = [0,1,2,3,4,5,6,7,8,9],
a = [],
LEN = 1000,
LOOPS = 1000;
while(LEN--)
a = a.concat(r);
var d = new Date();
for(var i = 0; i < LOOPS; i++)
uniq(a);
document.write('<br>uniq, ms/loop: ' + (new Date() - d)/LOOPS)
var d = new Date();
for(var i = 0; i < LOOPS; i++)
uniq_fast(a);
document.write('<br>uniq_fast, ms/loop: ' + (new Date() - d)/LOOPS)
function uniq(a) {
return Array.from(new Set(a));
}
要么
let uniq = a => [...new Set(a)];
请注意,与python不同,ES6集按插入顺序迭代,因此此代码保留原始数组的顺序 .
但是,如果您需要具有唯一元素的数组,为什么不从一开始就使用集合?
发电机
"lazy",基于生成器的 uniq 版本可以在相同的基础上构建:
从参数中获取下一个值
如果已经看过,请跳过它
否则,将其生成并将其添加到已经看到的值集合中
function* uniqIter(a) {
let seen = new Set();
for (let x of a) {
if (!seen.has(x)) {
seen.add(x);
yield x;
}
}
}
// example:
function* randomsBelow(limit) {
while (1)
yield Math.floor(Math.random() * limit);
}
// note that randomsBelow is endless
count = 20;
limit = 30;
for (let r of uniqIter(randomsBelow(limit))) {
console.log(r);
if (--count === 0)
break
}
// exercise for the reader: what happens if we set `limit` less than `count` and why
3
这只是另一种解决方案,但与其他方案不同 .
function diffArray(arr1, arr2) {
var newArr = arr1.concat(arr2);
newArr.sort();
var finalArr = [];
for(var i = 0;i<newArr.length;i++) {
if(!(newArr[i] === newArr[i+1] || newArr[i] === newArr[i-1])) {
finalArr.push(newArr[i]);
}
}
return finalArr;
}
388
在任何地方(甚至在PhotoshopScript中)代码中理解和工作都非常简单 . 核实!
var peoplenames = new Array("Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl");
peoplenames = unique(peoplenames);
alert(peoplenames);
function unique(array){
var len = array.length;
for(var i = 0; i < len; i++) for(var j = i + 1; j < len; j++)
if(array[j] == array[i]){
array.splice(j,1);
j--;
len--;
}
return array;
}
//*result* peoplenames == ["Mike","Matt","Nancy","Adam","Jenny","Carl"]
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var uniq = names.reduce(function(a,b){
if (a.indexOf(b) < 0 ) a.push(b);
return a;
},[]);
console.log(uniq, names) // [ 'Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Carl' ]
// one liner
return names.reduce(function(a,b){if(a.indexOf(b)<0)a.push(b);return a;},[]);
更快的uniq与排序
可能有更快的方法,但这个很不错 .
var uniq = names.slice() // slice makes copy of array before sorting it
.sort(function(a,b){
return a > b;
})
.reduce(function(a,b){
if (a.slice(-1)[0] !== b) a.push(b); // slice(-1)[0] means last item in array without removing it (like .pop())
return a;
},[]); // this empty array becomes the starting value for a
// one liner
return names.slice().sort(function(a,b){return a > b}).reduce(function(a,b){if (a.slice(-1)[0] !== b) a.push(b);return a;},[]);
Vanilla JS: Remove duplicates using an Object like a Set
你总是可以尝试将它放入一个对象,然后迭代它的键:
function remove_duplicates(arr) {
var obj = {};
var ret_arr = [];
for (var i = 0; i < arr.length; i++) {
obj[arr[i]] = true;
}
for (var key in obj) {
ret_arr.push(key);
}
return ret_arr;
}
Vanilla JS: Remove duplicates by tracking already seen values (order-safe)
或者,对于订单安全版本,使用对象存储所有先前看到的值,并在添加到数组之前检查它的值 .
function remove_duplicates_safe(arr) {
var seen = {};
var ret_arr = [];
for (var i = 0; i < arr.length; i++) {
if (!(arr[i] in seen)) {
ret_arr.push(arr[i]);
seen[arr[i]] = true;
}
}
return ret_arr;
}
ECMAScript 6: Use the new Set data structure (order-safe)
ECMAScript 6添加了新的 Set 数据结构,它允许您存储任何类型的值 . Set.values 按插入顺序返回元素 .
function remove_duplicates_es6(arr) {
let s = new Set(arr);
let it = s.values();
return Array.from(it);
}
Example usage:
a = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
b = remove_duplicates(a);
// b:
// ["Adam", "Carl", "Jenny", "Matt", "Mike", "Nancy"]
c = remove_duplicates_safe(a);
// c:
// ["Mike", "Matt", "Nancy", "Adam", "Jenny", "Carl"]
d = remove_duplicates_es6(a);
// d:
// ["Mike", "Matt", "Nancy", "Adam", "Jenny", "Carl"]
8
最简单的一个我到目前为止已经遇到过 . 在es6 .
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl", "Mike", "Nancy"]
var noDupe = Array.from(new Set(names))
var data = [2,3,4,5,5,4];
var filter = function(value, index){ return this.indexOf(value) == index };
var filteredData = data.filter(filter, data );
document.body.innerHTML = '<pre>' + JSON.stringify(filteredData, null, '\t') + '</pre>';
Array.prototype.unique = function() {
var a = [];
for (i = 0; i < this.length; i++) {
var current = this[i];
if (a.indexOf(current) < 0) a.push(current);
}
return a;
}
function getDistinctObjArray(arr) {
var distinctArr = arr.filter(function(el) {
var isDup = el.inArray;
el.inArray = true;
return !isDup;
});
distinctArr.forEach(function(el) {
delete el.inArray;
});
return distinctArr;
}
2460
所以选项是:
let a = [11,22,11,22];
let b = []
b = [ ...new Set(a) ];
// b = [11, 22]
b = Array.from( new Set(a))
// b = [11, 22]
b = a.filter((val,i)=>{
return a.indexOf(val)==i
})
// b = [11, 22]
30 回答
一条线:
“聪明”,但天真的方式
基本上,我们迭代数组,并为每个元素检查数组中此元素的第一个位置是否等于当前位置 . 显然,这两个位置对于重复元素是不同的 .
使用过滤器回调的第3个(“this array”)参数,我们可以避免数组变量的闭包:
虽然简洁,但该算法对于大型阵列(二次时间)并不是特别有效 .
Hashtables救援
这就是通常的做法 . 我们的想法是将每个元素放在一个哈希表中,然后立即检查它的存在 . 这给了我们线性时间,但至少有两个缺点:
因为哈希键只能是Javascript中的字符串,所以此代码不区分数字和"numeric strings" . 也就是说,
uniq([1,"1"])
将仅返回[1]
出于同样的原因,所有对象都被视为相等:
uniq([{foo:1},{foo:2}])
将仅返回[{foo:1}]
.也就是说,如果你的数组只包含基元并且你不关心类型(例如它总是数字),那么这个解决方案是最佳的 .
两个世界中最好的
通用解决方案结合了两种方法:它使用基元的哈希查找和对象的线性搜索 .
排序| uniq的
另一种选择是先对数组进行排序,然后删除与前一个元素相等的每个元素:
同样,这不适用于对象(因为
sort
的所有对象都相同) . 另外,我们默默地改变原始阵列作为副作用 - 不好!但是,如果您的输入已经排序,则可以采用此方法(只需从上面删除sort
) .独特的......
有时需要根据除了相等之外的某些标准来统一列表,例如,过滤掉不同的对象,但共享一些属性 . 这可以通过传递回调来优雅地完成 . 此"key"回调应用于每个元素,并删除具有相同"keys"的元素 . 由于
key
应该返回一个原语,哈希表在这里工作正常:一个特别有用的
key()
是JSON.stringify
,它将删除物理上不同的对象,但"look"相同:如果
key
不是原始的,则必须求助于线性搜索:在ES6中,您可以使用
Set
:或
Map
:它们也适用于非原始键 .
图书馆
underscore和Lo-Dash都提供
uniq
方法 . 他们的算法基本上类似于上面的第一个片段,归结为:这是二次方的,但还有很好的额外好处,比如包装本机
indexOf
,通过键(iteratee
用他们的说法)进行单一化的能力,以及已经排序的数组的优化 .如果你正在使用jQuery并且在它之前没有一美元就无法忍受任何事情,它就像这样:
这也是第一个片段的变体 .
表现
函数调用在Javascript中很昂贵,因此上述解决方案虽然简洁,但效率不高 . 为了获得最佳性能,请将
filter
替换为循环并删除其他函数调用:这段丑陋的代码与上面的代码段#3相同,但速度提高了一个数量级(截至2017年它只有两倍的速度 - JS核心人员做得很好!)
ES6
ES6提供了Set对象,这使事情变得更加容易:
要么
请注意,与python不同,ES6集按插入顺序迭代,因此此代码保留原始数组的顺序 .
但是,如果您需要具有唯一元素的数组,为什么不从一开始就使用集合?
发电机
"lazy",基于生成器的
uniq
版本可以在相同的基础上构建:从参数中获取下一个值
如果已经看过,请跳过它
否则,将其生成并将其添加到已经看到的值集合中
这只是另一种解决方案,但与其他方案不同 .
在任何地方(甚至在PhotoshopScript中)代码中理解和工作都非常简单 . 核实!
https://jsfiddle.net/2w0k5tz8/
循环,删除重复项,并创建克隆数组占位符,因为不会更新数组索引 .
向后循环以获得更好的性能(您的循环不需要继续检查数组的长度)
拿到厌倦了用for循环或jQuery看到所有不好的例子 . Javascript现在拥有完美的工具:排序,映射和缩小 .
Uniq在保持现有订单的同时减少
更快的uniq与排序
可能有更快的方法,但这个很不错 .
更新2015:ES6版本:
在ES6中,您具有集合和传播,这使得删除所有重复项非常容易和高效:
基于事件排序:
有人询问是否根据有多少个唯一名称来排序结果:
This is probably one of the fastest way to remove permanently the duplicates from an array 比这里的大多数功能快10倍 . 在safari中快78倍
测试:http://jsperf.com/wgu
演示:http://jsfiddle.net/46S7g/
更多:https://stackoverflow.com/a/25082874/2450730
如果你不能阅读上面的代码问,请阅读javascript书或这里有一些关于更短代码的解释 . https://stackoverflow.com/a/21353032/2450730
这可以在ES6中缩短到
Here很好解释
Array.filter()
在ECMAScript 6(又名ECMAScript 2015)中,Set可用于过滤掉重复项 . 然后可以使用spread operator将其转换回数组 .
以下内容比列出的jQuery方法快80%以上(参见下面的测试) . 这是几年前类似问题的答案 . 如果我遇到最初提出它的人,我会发布信用证 . 纯JS .
我的测试用例比较:http://jsperf.com/remove-duplicate-array-tests
去这个:
现在uniqueArray不包含重复项 .
Vanilla JS: Remove duplicates using an Object like a Set
你总是可以尝试将它放入一个对象,然后迭代它的键:
Vanilla JS: Remove duplicates by tracking already seen values (order-safe)
或者,对于订单安全版本,使用对象存储所有先前看到的值,并在添加到数组之前检查它的值 .
ECMAScript 6: Use the new Set data structure (order-safe)
ECMAScript 6添加了新的
Set
数据结构,它允许您存储任何类型的值 .Set.values
按插入顺序返回元素 .Example usage:
最简单的一个我到目前为止已经遇到过 . 在es6 .
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
使用Underscore.js
它是一个包含许多用于操作数组的函数的库 .
_.uniq
Example
注意:Lo-Dash(underscore竞争对手)也提供了类似的.uniq实现 .
使用本机javascript函数从数组中删除重复项的最简洁方法是使用如下序列:
在reduce函数中不需要
slice
也不需要indexOf
,就像我在其他示例中看到的那样!然而,将它与过滤器功能一起使用是有意义的:另一种已经在少数浏览器上运行的ES6(2015)方法是:
甚至使用spread operator:
干杯!
您可以使用
filter
方法的第二个 - index - 参数在JavaScript中执行此操作:或者简而言之
使用jQuery快速而肮脏:
here is the simple method without any special libraries are special function,
我已经在其他一些问题上对dupe删除进行了详细的比较,但注意到这是我想在这里分享它的真实地方 .
我相信这是最好的方法
好吧..即使这个是O(n)而其他人都是O(n ^ 2)我很想看到这个缩小/查找表和filter / indexOf组合之间的基准比较(我选择Jeetendras非常好的实现https://stackoverflow.com/a/37441144/4543207) . 我准备一个100K项目数组,填充0-9999范围内的随机正整数,并删除重复项 . 我重复测试了10次,结果的平均值表明它们的性能不匹配 .
在firefox v47中减少&lut:14.85ms vs filter&indexOf:2836ms
在chrome v51中减少&lut:23.90ms vs filter&indexOf:1066ms
好吧到目前为止一切都那么好 . 但是这次以ES6风格正确地做到了 . 它看起来很酷..!但截至目前,它对抗强大的解决方案将如何表现对我来说是一个谜 . 让我们先看看代码,然后对它进行基准测试 .
哇,这很短..!但是性能怎么样?它很漂亮......由于过滤器/ indexOf的重量超过了我们的肩膀,我现在可以在0..99999的范围内测试1M的随机正整数项,以获得10次连续测试的平均值 . 我可以说这次是一场真正的比赛 . 自己看看结果:)
你会用哪一个..?好吧不是那么快......!不要被欺骗 . Map 处于位移状态 . 现在看......在上述所有情况下,我们填充一个大小为n的数组,其数量为范围<n . 我的意思是我们有一个100的数组,我们填充随机数0..9所以有确定的重复和“几乎”肯定每个数字都有重复 . 如果我们用随机数0..9999填充大小为100的数组怎么样 . 现在让我们看看 Map 在家玩 . 这次是一个100K项目的数组,但随机数范围是0..100M . 我们将进行100次连续测试以平均结果 . 好吧,让我们看看投注..! < - 没有错字
现在这是Map()的壮观复出..!可能现在你可以在想要删除欺骗时做出更好的决定 .
好吧,我们现在都很高兴 . 但是主角一直伴随着掌声 . 我相信你们有些人想知道Set对象会做什么 . 既然我们对ES6开放并且我们知道Map是之前游戏的赢家,那么让我们将Map与Set进行比较 . 这次典型的皇马对阵巴塞罗那比赛......或者是这样?让我们看看谁将赢得el classico :)
哇..男人..!出乎意料的是,它根本没有成为一个经典的 . 更像巴塞罗那足球俱乐部对阵CA Osasuna :))
使用数组过滤器和indexOf函数的单行版本:
通用功能方法
以下是ES2015的通用且严格功能的方法:
我们可以很容易地从
unqiueBy
派生unique
或使用更快的实现利用Set
:这种方法的好处:
通过使用单独的比较器功能
泛型解决方案
声明性和简洁的实现
重用其他小型通用函数
性能注意事项
uniqueBy
不如使用循环的命令式实现快,但由于其通用性,它更具表现力 .如果您在应用中将
uniqueBy
识别为具体性能损失的原因,请将其替换为优化代码 . 也就是说,首先以功能性的声明方式编写代码 . 之后,如果遇到性能问题,请尝试优化位置的代码,这是导致问题的原因 .内存消耗和垃圾收集
uniqueBy
利用隐藏在其体内的突变(push(x) (acc)
) . 它重用累加器而不是在每次迭代后丢弃它 . 这减少了内存消耗和GC压力 . 由于这种副作用包含在函数内部,因此外部的所有内容都保持纯净 .如果您有任何机会使用
你可以做到
https://github.com/mbostock/d3/wiki/Arrays#set_values
一种简单但有效的技术,是将
filter
方法与过滤器function(value, index){ return this.indexOf(value) == index }
结合使用 .代码示例:
另见this Fiddle .
除了比当前的答案更简单,更简洁的解决方案(减去未来的ES6),我对此进行了测试,并且速度也快得多:
一个警告:在IE9中添加了Array.lastIndexOf(),所以如果你需要低于它,你需要寻找其他地方 .
Solution 1
Solution 2 (using Set)
Test
Performance
当我测试chrome的性能(使用和不使用Set)时,我发现使用Set的实现速度要快得多!
这是一个简单的问题答案 .
最佳答案的复杂性为
O(n²)
,但这可以通过使用对象作为哈希来完成O(n)
:这适用于字符串,数字和日期 . 如果您的数组包含复杂对象(即,它们必须与
===
进行比较),则上述解决方案将不起作用 . 您可以通过在对象本身上设置标志来获取对象的O(n)
实现:所以选项是: