从JavaScript数组中删除重复项
我有一个非常简单的JavaScript数组,可能包含或不包含重复。
names = new Array("Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl");
我需要删除重复项,并将唯一值放在一个新的数组中。
我可以指出我尝试过的所有代码,但是我认为这是无用的,因为它们不起作用。 我也接受jQuery解决scheme。
相关: 在JavaScript数组中查找重复值的最简单的方法
使用jQuery快速和肮脏:
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"]; var uniqueNames = []; $.each(names, function(i, el){ if($.inArray(el, uniqueNames) === -1) uniqueNames.push(el); });
“聪明”但天真的方式
uniqueArray = a.filter(function(item, pos) { return a.indexOf(item) == pos; })
基本上,我们遍历数组,并为每个元素,检查数组中的这个元素的第一个位置是否等于当前位置。 显然,这两个位置对于重复元素是不同的。
使用filtercallback的第三个(“this array”)参数,我们可以避免closures数组variables:
uniqueArray = a.filter(function(item, pos, self) { return self.indexOf(item) == pos; })
尽pipe简洁,但对于大型数组(二次时间)来说,该algorithm并不是特别有效。
哈希表的救援
function uniq(a) { var seen = {}; return a.filter(function(item) { return seen.hasOwnProperty(item) ? false : (seen[item] = true); }); }
这是通常的做法。 这个想法是把每个元素放在散列表中,然后立即检查它的存在。 这给了我们线性时间,但至less有两个缺点:
- 由于散列键只能是Javascript中的string,所以这段代码不能区分数字和“数字string”。 也就是说,
uniq([1,"1"])
只会返回[1]
- 出于同样的原因,所有的对象都被认为是相等的:
uniq([{foo:1},{foo:2}])
将只返回[{foo:1}]
。
也就是说,如果你的数组只包含原语,并且你不关心types(例如总是数字),这个解决scheme是最优的。
来自两个世界的最好
一个通用的解决scheme结合了两种方法:它使用哈希查找基元和线性search对象。
function uniq(a) { var prims = {"boolean":{}, "number":{}, "string":{}}, objs = []; return a.filter(function(item) { var type = typeof item; if(type in prims) return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true); else return objs.indexOf(item) >= 0 ? false : objs.push(item); }); }
sorting| uniq的
另一种select是先对数组进行sorting,然后删除与前一个元素相同的元素:
function uniq(a) { return a.sort().filter(function(item, pos, ary) { return !pos || item != ary[pos - 1]; }) }
同样,这不适用于对象(因为所有对象都是相同的)。 此外,我们默默地改变原来的数组作为副作用 – 不好! 但是,如果您的input已经sorting,这是要走的路(只是从上面删除sort
)。
独特的…
有时需要基于除了相等之外的某些标准来唯一化列表,例如过滤掉不同的对象,但共享一些属性。 这可以通过传递callback优雅地完成。 这个“关键”callback被应用到每个元素,而具有相同“关键字”的元素被移除。 由于key
被期望返回一个原语,哈希表将在这里工作正常:
function uniqBy(a, key) { var seen = {}; return a.filter(function(item) { var k = key(item); return seen.hasOwnProperty(k) ? false : (seen[k] = true); }) }
一个特别有用的key()
是JSON.stringify
,它将删除物理上不同的对象,但是“看起来”是一样的:
a = [[1,2,3], [4,5,6], [1,2,3]] b = uniqBy(a, JSON.stringify) console.log(b) // [[1,2,3], [4,5,6]]
如果key
不是原始的,则必须求助于线性search:
function uniqBy(a, key) { var index = []; return a.filter(function (item) { var k = key(item); return index.indexOf(k) >= 0 ? false : index.push(k); }); }
或者使用ES6中的Set
对象:
function uniqBy(a, key) { var seen = new Set(); return a.filter(item => { var k = key(item); return seen.has(k) ? false : seen.add(k); }); }
(有些人更喜欢!seen.has(k) && seen.add(k)
而不是seen.has(k) ? false : seen.add(k)
)。
图书馆
下划线和Lo-Dash都提供uniq
方法。 他们的algorithm基本上与上面的第一个片段类似,归结为:
var result = []; a.forEach(function(item) { if(result.indexOf(item) < 0) { result.push(item); } });
这是二次的,但是还有很多其他的好处,比如包装本地indexOf
,通过键iteratee
(用他们的说法iteratee
)以及对已经sorting的数组进行优化。
如果你使用的是jQuery,如果没有一美元,它就不能忍受任何东西,就像这样:
$.uniqArray = function(a) { return $.grep(a, function(item, pos) { return $.inArray(item, a) === pos; }); }
这也是第一个片段的变体。
性能
函数调用在Javascript中是很昂贵的,因此上面的解决scheme尽pipe简洁,但并不是特别有效。 为了获得最佳性能,请使用循环replacefilter
并去除其他函数调用:
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; }
这段丑陋的代码和上面的代码片段#3一样, 但速度要快一点 (截至2017年, 速度只有两倍 – JS核心人员做得很好!)
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)
厌倦了看到for循环或jQuery的所有不好的例子。 JavaScript现在有这个完美的工具:sorting,映射和减less。
Uniq在保持现有秩序的同时减less
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;},[]);
与sorting更快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;},[]);
更新2015年:ES6版本:
在ES6中,你有Sets和Spread,这使得它非常容易和高性能的删除所有重复:
var uniq = [ ...new Set(names) ]; // [ 'Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Carl' ]
根据发生情况sorting:
有人问到,根据有多less独特的名字来排列结果:
var names = ['Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Nancy', 'Carl'] var uniq = names .map((name) => { return {count: 1, name: name} }) .reduce((a, b) => { a[b.name] = (a[b.name] || 0) + b.count return a }, {}) var sorted = Object.keys(uniq).sort((a, b) => uniq[a] < uniq[b]) console.log(sorted)
使用Underscore.js
这是一个具有许多操作数组的函数的库。
这跟jQuery的tux和Backbone.js的吊带一起。
_.uniq
_.uniq(array, [isSorted], [iterator])
别名: 唯一
使用===生成数组的非重复版本,以testing对象相等性。 如果事先知道该数组已经sorting, 那么isSorted传递true会运行一个更快的algorithm。 如果你想基于转换计算唯一的项目,传递一个迭代器函数。
例
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"]; alert(_.uniq(names, false));
注意: Lo-Dash (一个下划线竞争者)也提供了一个可比的.uniq实现。
香草JS:使用像一个集合的对象删除重复
你总是可以把它放到一个对象中,然后遍历它的键:
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; }
香草JS:通过跟踪已经看到的值删除重复(订单安全)
或者,对于一个订单安全的版本,使用一个对象来存储所有以前看到的值,并在添加到数组之前对它进行检查。
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:使用新的设置数据结构(订单安全)
ECMAScript 6添加了新的Set
数据结构,它可以让你存储任何types的值。 Set.values
以插入顺序返回元素。
function remove_duplicates_es6(arr) { let s = new Set(arr); let it = s.values(); return Array.from(it); }
用法示例:
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"]
使用数组filter和indexOf函数的单行版本:
arr = arr.filter (function (value, index, array) { return array.indexOf (value) == index; });
使用原生javascript函数从数组中删除重复的最简洁方法是使用如下所示的序列:
vals.sort().reduce(function(a, b){ if (b != a[0]) a.unshift(b); return a }, [])
在reduce函数中不需要slice
或indexOf
,就像我在其他示例中看到的一样! 使用它和一个filter函数是合理的:
vals.filter(function(v, i, a){ return i == a.indexOf(v) })
另一个ES6(2015年)的方法,已经在几个浏览器上工作,是:
Array.from(new Set(vals))
或者甚至使用传播运算符 :
[...new Set(vals)]
干杯!
你可以简单地用JavaScript来完成,在filter
方法的第二个索引参数的帮助下:
var a = [2,3,4,5,5,4]; a.filter(function(value, index){ return a.indexOf(value) == index });
或者简而言之
a.filter((v,i) => a.indexOf(v)==i)
去这个:
var uniqueArray = duplicateArray.filter(function(elem, pos) { return duplicateArray.indexOf(elem) == pos; });
现在uniqueArray不包含重复项。
最简单的一个我跑到目前为止。 在es6中。
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl", "Mike", "Nancy"] var noDupe = Array.from(new Set(names))
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
我在其他一些问题上做了一些详细的比较,但是注意到这是我真正想在这里分享的地方。
我相信这是做到这一点的最好方法
var myArray = [100, 200, 100, 200, 100, 100, 200, 200, 200, 200], reduced = Object.keys(myArray.reduce((p,c) => (p[c] = true,p),{})); console.log(reduced);
以下比列出的jQuery方法快80%以上(见下面的testing)。 这是几年前类似问题的一个答案,如果我遇到原来提出的人,我会发信贷。 纯JS。
var temp = {}; for (var i = 0; i < array.length; i++) temp[array[i]] = true; var r = []; for (var k in temp) r.push(k); return r;
我的testing用例比较: http : //jsperf.com/remove-duplicate-array-tests
这是一个简单的答案。
var names = ["Alex","Tony","James","Suzane", "Marie", "Laurence", "Alex", "Suzane", "Marie", "Marie", "James", "Tony", "Alex"]; var uniqueNames = []; for(var i in names){ if(uniqueNames.indexOf(names[i]) === -1){ uniqueNames.push(names[i]); } }
在ECMAScript 6(又名ECMAScript 2015)中, Set
可以用来过滤重复项。 然后可以使用扩展运算符将其转换回数组。
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"], unique = [...new Set(names)];
最上面的答案有O(n)
的复杂性,但是这可以通过使用一个对象作为哈希来完成,只需要O(n)
:
function getDistinctArray(arr) { var dups = {}; return arr.filter(function(el) { var hash = el.valueOf(); var isDup = dups[hash]; dups[hash] = true; return !isDup; }); }
这将适用于string,数字和date。 如果您的数组包含复杂的对象(即,它们必须与===
比较),上述解决scheme将无法工作。 你可以通过在对象本身上设置一个标志来获得对象的O(n)
实现:
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; }
一条线:
let names = ['Mike','Matt','Nancy','Adam','Jenny','Nancy','Carl', 'Nancy']; let dup = [...new Set(names)]; console.log(dup);
除了比现在的答案更简单,更简洁的解决scheme(不包括面向未来的ES6)之外,我进行了testing,速度也更快:
var uniqueArray = dupeArray.filter(function(item, i, self){ return self.lastIndexOf(item) == i; });
一个警告:在IE9中添加Array.lastIndexOf(),所以如果你需要低于这个,你需要看看其他地方。
一个简单而有效的方法是将filter
方法与过滤function(value, index){ return this.indexOf(value) == index }
。
代码示例:
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>';
您也可以使用JavaScript实验室库中的Array.unique()
方法 – 或从那里窃取一个想法。
然而,这里的代码写得不是很好,因为它将unique()
方法声明为Array原型的一个属性,因此将它添加到每个Array中,打破for...in
function(因为for...in
循环中也会遍历unique
variables)。
这里是没有任何特殊库的简单方法是特殊function,
name_list = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"]; get_uniq = name_list.filter(function(val,ind) { return name_list.indexOf(val) == ind; }) console.log("Original name list:"+name_list.length, name_list) console.log("\n Unique name list:"+get_uniq.length, get_uniq)
通用function方法
ES2015是一个通用且严格function的方法:
// small, reusable auxiliary functions const apply = f => a => f(a); const flip = f => b => a => f(a) (b); const uncurry = f => (a, b) => f(a) (b); const push = x => xs => (xs.push(x), xs); const foldl = f => acc => xs => xs.reduce(uncurry(f), acc); const some = f => xs => xs.some(apply(f)); // the actual de-duplicate function const uniqueBy = f => foldl( acc => x => some(f(x)) (acc) ? acc : push(x) (acc) ) ([]); // comparators const eq = y => x => x === y; // string equality case insensitive :D const seqCI = y => x => x.toLowerCase() === y.toLowerCase(); // mock data const xs = [1,2,3,1,2,3,4]; const ys = ["a", "b", "c", "A", "B", "C", "D"]; console.log( uniqueBy(eq) (xs) ); console.log( uniqueBy(seqCI) (ys) );
所以选项是:
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]
$(document).ready(function() { var arr1=["dog","dog","fish","cat","cat","fish","apple","orange"] var arr2=["cat","fish","mango","apple"] var uniquevalue=[]; var seconduniquevalue=[]; var finalarray=[]; $.each(arr1,function(key,value){ if($.inArray (value,uniquevalue) === -1) { uniquevalue.push(value) } }); $.each(arr2,function(key,value){ if($.inArray (value,seconduniquevalue) === -1) { seconduniquevalue.push(value) } }); $.each(uniquevalue,function(ikey,ivalue){ $.each(seconduniquevalue,function(ukey,uvalue){ if( ivalue == uvalue) { finalarray.push(ivalue); } }); }); alert(finalarray); });
If by any chance you were using
D3.js
You could do
d3.set(["foo", "bar", "foo", "baz"]).values() ==> ["foo", "bar", "baz"]
Here is very simple for understanding and working anywhere (even in PhotoshopScript) code. Check it!
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"]
A slight modification of thg435's excellent answer to use a custom comparator:
function contains(array, obj) { for (var i = 0; i < array.length; i++) { if (isEqual(array[i], obj)) return true; } return false; } //comparator function isEqual(obj1, obj2) { if (obj1.name == obj2.name) return true; return false; } function removeDuplicates(ary) { var arr = []; return ary.filter(function(x) { return !contains(arr, x) && arr.push(x); }); }
This is probably one of the fastest way to remove permanently the duplicates from an array 10x times faster than the most functions here.& 78x faster in safari
function toUnique(a,b,c){ //array,placeholder,placeholder b=a.length;while(c=--b)while(c--)a[b]!==a[c]||a.splice(c,1) }
- Test: http://jsperf.com/wgu
- Demo: http://jsfiddle.net/46S7g/
- More: https://stackoverflow.com/a/25082874/2450730
if you can't read the code above ask, read a javascript book or here are some explainations about shorter code. https://stackoverflow.com/a/21353032/2450730
https://jsfiddle.net/2w0k5tz8/
function remove_duplicates(array_){ var ret_array = new Array(); for (var a = array_.length - 1; a >= 0; a--) { for (var b = array_.length - 1; b >= 0; b--) { if(array_[a] == array_[b] && a != b){ delete array_[b]; } }; if(array_[a] != undefined) ret_array.push(array_[a]); }; return ret_array; } console.log(remove_duplicates(Array(1,1,1,2,2,2,3,3,3)));
Loop through, remove duplicates, and create a clone array place holder because the array index will not be updated.
Loop backward for better performance ( your loop wont need to keep checking the length of your array)
This was just another solution but different than the rest.
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; }
If using AngularJs then use
filter
like this
angular.module("myApp", []).controller("myController", function($scope) { $scope.targetArray = ['Apple', 'Apple', 'Banana', 'Mango', 'Strawberry', 'Banana']; $scope.filteredArray = []; $scope.removeDuplicates = function() { $scope.filteredArray = $scope.targetArray.filter(function(item, pos) { return $scope.targetArray.indexOf(item) == pos; }); } });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script> <body ng-app="myApp"> <div ng-controller='myController'> <div> Actual Array: {{targetArray}} <br> Resulted Array: {{filteredArray}} <br> <button ng-click='removeDuplicates()'>Remove Duplicates</button> </div> </body>