提问人:kramden88 提问时间:2/10/2012 最后编辑:starballkramden88 更新时间:9/26/2023 访问量:3016748
从 JS 数组中删除重复值 [duplicate]
Remove duplicate values from JS array [duplicate]
问:
我有一个非常简单的 JavaScript 数组,它可能包含也可能不包含重复项。
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
我需要删除重复项并将唯一值放入新数组中。
我可以指出我尝试过的所有代码,但我认为它没用,因为它们不起作用。我也接受jQuery解决方案。
类似问题:
答:
Vanilla JS:使用像 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:通过跟踪已看到的值来删除重复项(订单安全)
或者,对于顺序安全版本,使用对象来存储以前看到的所有值,并在添加到数组之前根据该值检查值。
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:使用新的 Set 数据结构(顺序安全)
ECMAScript 6 添加了新的 Data-Structure,它允许您存储任何类型的值。 按插入顺序返回元素。Set
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"]
评论
var c = Object.keys(b)
obj[arr[i]] = true;
i
true
TL;博士
uniq = [...new Set(array)];
(请注意,var uniq
将是一个数组......new Set(
) 将其转换为集合,但 [... ] 再次将其变回数组)
“聪明”但天真的方式
uniqueArray = a.filter(function(item, pos) {
return a.indexOf(item) == pos;
})
基本上,我们遍历数组,对于每个元素,检查该元素在数组中的第一个位置是否等于当前位置。显然,对于重复元素,这两个位置是不同的。
使用过滤器回调的第三个(“this array”)参数,我们可以避免数组变量的闭包:
uniqueArray = a.filter(function(item, pos, self) {
return self.indexOf(item) == pos;
})
虽然简洁,但此算法对于大型数组(二次时间)并不是特别有效。
哈希表来救援
function uniq(a) {
var seen = {};
return a.filter(function(item) {
return seen.hasOwnProperty(item) ? false : (seen[item] = true);
});
}
这就是通常的做法。这个想法是将每个元素放在一个哈希表中,然后立即检查它是否存在。这为我们提供了线性时间,但至少有两个缺点:
- 由于哈希键在 JavaScript 中只能是字符串或符号,因此此代码不区分数字和“数字字符串”。也就是说,将返回
uniq([1,"1"])
[1]
- 出于同样的原因,所有对象都将被视为相等:将返回 .
uniq([{foo:1},{foo:2}])
[{foo:1}]
也就是说,如果你的数组只包含基元,而你不关心类型(例如,它总是数字),那么这个解决方案是最佳的。
两全其美
通用解决方案结合了这两种方法:它对基元使用哈希查找,对对象使用线性搜索。
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);
});
}
排序 |优时班
另一种选择是先对数组进行排序,然后删除与前一个元素相等的每个元素:
function uniq(a) {
return a.sort().filter(function(item, pos, ary) {
return !pos || item != ary[pos - 1];
});
}
同样,这不适用于对象(因为所有对象都对 )。此外,我们默默地更改原始数组作为副作用 - 不好!但是,如果您的输入已经排序,则这是要走的路(只需从上面删除即可)。sort
sort
独特之处在于...
有时,需要根据除相等以外的某些条件对列表进行统一,例如,过滤掉不同但共享某些属性的对象。这可以通过传递回调来优雅地完成。此“键”回调将应用于每个元素,并删除具有相等“键”的元素。由于预期返回原语,因此哈希表在这里可以正常工作: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
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) {
let seen = new Set();
return a.filter(item => {
let k = key(item);
return seen.has(k) ? false : seen.add(k);
});
}
或 :Map
function uniqBy(a, key) {
return [
...new Map(
a.map(x => [key(x), x])
).values()
]
}
两者都适用于非原始键。
第一个还是最后一个?
通过键删除对象时,您可能希望保留“相等”对象中的第一个或最后一个。
使用上面的变体保留第一个,并使用 保留最后一个:Set
Map
function uniqByKeepFirst(a, key) {
let seen = new Set();
return a.filter(item => {
let k = key(item);
return seen.has(k) ? false : seen.add(k);
});
}
function uniqByKeepLast(a, key) {
return [
...new Map(
a.map(x => [key(x), x])
).values()
]
}
//
data = [
{a:1, u:1},
{a:2, u:2},
{a:3, u:3},
{a:4, u:1},
{a:5, u:2},
{a:6, u:3},
];
console.log(uniqByKeepFirst(data, it => it.u))
console.log(uniqByKeepLast(data, it => it.u))
图书馆
下划线和 Lo-Dash 都提供了方法。他们的算法基本上与上面的第一个片段相似,可以归结为:uniq
var result = [];
a.forEach(function(item) {
if(result.indexOf(item) < 0) {
result.push(item);
}
});
这是二次的,但也有不错的其他好处,比如包装原生,能够通过键(用他们的话来说)统一,以及对已经排序的数组的优化。indexOf
iteratee
如果你正在使用jQuery,并且无法忍受任何东西,那么它就像这样:
$.uniqArray = function(a) {
return $.grep(a, function(item, pos) {
return $.inArray(item, a) === pos;
});
}
这又是第一个代码段的变体。
性能
在 JavaScript 中,函数调用的成本很高,因此上述解决方案虽然简洁,但效率并不是特别高。为了获得最佳性能,请替换为循环并删除其他函数调用:filter
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)
ES6 (美)
ES6 提供了 Set 对象,这让事情变得简单多了:
function uniq(a) {
return Array.from(new Set(a));
}
或
let uniq = a => [...new Set(a)];
请注意,与 python 不同,ES6 集是按插入顺序迭代的,因此此代码保留了原始数组的顺序。
但是,如果您需要一个具有唯一元素的数组,为什么不从一开始就使用集合呢?
发电机
一个“惰性”的、基于生成器的版本可以建立在相同的基础上: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
评论
O(n^2)
["toString", "valueOf", "failed"]
toString
valueOf
Object.create(null)
{}
使用 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);
});
评论
inArray
使用下划线.js
它是一个包含大量用于操作数组的函数的库。
这是与jQuery的tux和Backbone.js的 吊带。
_.uniq(array, [isSorted], [iterator])
别名:unique
生成数组的无重复版本,使用 === 测试对象 平等。如果您事先知道数组已排序,则为 isSorted 传递 true 将运行更快的算法。如果你想 基于转换计算唯一项,传递迭代器函数。
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
alert(_.uniq(names, false));
注意:Lo-Dash(下划线竞争对手)也提供了类似的 .uniq 实现。
评论
[...new Set(Array)]
绰绰有余的伴侣
以下内容比列出的 jQuery 方法快 80% 以上(请参阅下面的测试)。 这是几年前类似问题的答案。如果我遇到最初提出它的人,我会发布信用。 纯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;
我的测试用例比较:http://jsperf.com/remove-duplicate-array-tests
评论
这是另一种使用 jQuery 的方法,
function uniqueArray(array){
if ($.isArray(array)){
var dupes = {}; var len, i;
for (i=0,len=array.length;i<len;i++){
var test = array[i].toString();
if (dupes[test]) { array.splice(i,1); len--; i--; } else { dupes[test] = true; }
}
}
else {
if (window.console) console.log('Not passing an array to uniqueArray, returning whatever you sent it - not filtered!');
return(array);
}
return(array);
}
作者: William Skidmore
function removeDuplicates(inputArray) {
var outputArray=new Array();
if(inputArray.length>0){
jQuery.each(inputArray, function(index, value) {
if(jQuery.inArray(value, outputArray) == -1){
outputArray.push(value);
}
});
}
return outputArray;
}
排名靠前的答案的复杂度为 ,但这可以通过使用对象作为哈希来完成: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;
});
}
这将适用于字符串、数字和日期。如果您的数组包含对象,则上述解决方案将不起作用,因为当强制转换为字符串时,它们都将具有值(或类似值),并且不适合用作查找值。您可以通过在对象本身上设置标志来获取对象的实现:"[object Object]"
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;
}
2019年编辑:现代版本的 JavaScript 使这个问题更容易解决。无论您的数组是否包含对象、字符串、数字或任何其他类型,都将起作用。Set
function getDistinctArray(arr) {
return [...new Set(arr)];
}
实现非常简单,不再需要定义函数。
评论
O(n)
使用数组和函数的单行版本:.filter
.indexOf
arr = arr.filter(function (value, index, array) {
return array.indexOf(value) === index;
});
ES6 方法
arr = arr.filter((value, index, array) =>
array.indexOf(value) === index
)
评论
如果你不想包含整个库,你可以用它来添加一个任何数组都可以使用的方法:
Array.prototype.uniq = function uniq() {
return this.reduce(function(accum, cur) {
if (accum.indexOf(cur) === -1) accum.push(cur);
return accum;
}, [] );
}
["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"].uniq()
如果您自己创建数组,则可以通过在插入数据时进行检查来节省循环和额外的唯一过滤器;
var values = [];
$.each(collection, function() {
var x = $(this).value;
if (!$.inArray(x, values)) {
values.push(x);
}
});
评论
厌倦了看到所有带有for循环或jQuery的坏例子。如今,Javascript 拥有完美的工具:排序、映射和归约。
Uniq reduce,同时保持现有秩序
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;},[]);
2015 年更新:ES6 版本:
在 ES6 中,你有 Sets 和 Spread,这使得删除所有重复项变得非常容易和高效:
var uniq = [ ...new Set(names) ]; // [ 'Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Carl' ]
按发生次数排序:
有人问如何根据有多少个唯一名称对结果进行排序:
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)
评论
"Nancy"
.sort(function(a,b){ return a > b ? 1 : a < b ? -1 : 0; })
[...new Set(names)]
对 thg435 使用自定义比较器的出色答案略作修改:
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);
});
}
$(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);
});
这可能是从数组中永久删除重复项的最快方法之一,比这里的大多数函数快 10 倍。
function toUnique(a,b,c){ //array,placeholder,placeholder
b=a.length;while(c=--b)while(c--)a[b]!==a[c]||a.splice(c,1)
}
- 测试:http://jsperf.com/wgu
- 演示:http://jsfiddle.net/46S7g/
- 更多: https://stackoverflow.com/a/25082874/2450730
如果你看不懂上面的代码,请问,读一本 JavaScript 书籍,或者这里有一些关于更短代码的解释。https://stackoverflow.com/a/21353032/2450730
评论
另一种无需编写大量代码即可执行此操作的方法是使用 ES5 方法:Object.keys
var arrayWithDuplicates = ['a','b','c','d','a','c'],
deduper = {};
arrayWithDuplicates.forEach(function (item) {
deduper[item] = null;
});
var dedupedArray = Object.keys(deduper); // ["a", "b", "c", "d"]
在函数中提取
function removeDuplicates (arr) {
var deduper = {}
arr.forEach(function (item) {
deduper[item] = null;
});
return Object.keys(deduper);
}
评论
arrayWithDuplicates
这是这个问题的简单答案。
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
可用于过滤掉重复项。然后可以使用 spread 运算符将其转换回数组。
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"],
unique = [...new Set(names)];
评论
new
new
删除字符串重复项的最简单方法是使用关联数组,然后遍历关联数组以恢复列表/数组。
如下图所示:
var toHash = [];
var toList = [];
// add from ur data list to hash
$(data.pointsToList).each(function(index, Element) {
toHash[Element.nameTo]= Element.nameTo;
});
// now convert hash to array
// don't forget the "hasownproperty" else u will get random results
for (var key in toHash) {
if (toHash.hasOwnProperty(key)) {
toList.push(toHash[key]);
}
}
瞧,现在重复的不见了!
删除重复项的最简单方法是执行 for 循环并比较不相同的元素并将它们推送到新数组中
var array = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var removeDublicate = function(arr){
var result = []
var sort_arr = arr.sort() //=> optional
for (var i = 0; i < arr.length; i++) {
if(arr[ i + 1] !== arr[i] ){
result.push(arr[i])
}
};
return result
}
console.log(removeDublicate(array))
==> ["Adam", "Carl", "Jenny", "Matt", "Mike", "Nancy"]
选择这个:
var uniqueArray = duplicateArray.filter(function(elem, pos) {
return duplicateArray.indexOf(elem) == pos;
});
现在 uniqueArray 不包含重复项。
以下脚本返回一个仅包含唯一值的新数组。它适用于字符串和数字。不需要额外的库,只需要 vanilla JS。
浏览器支持:
Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari
Basic support (Yes) 1.5 (1.8) 9 (Yes) (Yes)
https://jsfiddle.net/fzmcgcxv/3/
var duplicates = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl","Mike","Mike","Nancy","Carl"];
var unique = duplicates.filter(function(elem, pos) {
return duplicates.indexOf(elem) == pos;
});
alert(unique);
我知道我有点晚了,但这是使用 jinqJs 的另一种选择
var result = jinqJs().from(["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"]).distinct().select();
如果您有机会使用
D3.js
你可以做
d3.set(["foo", "bar", "foo", "baz"]).values() ==> ["foo", "bar", "baz"]
https://github.com/mbostock/d3/wiki/Arrays#set_values
评论
嵌套循环方法,用于删除数组中的重复项并保留元素的原始顺序。
var array = [1, 3, 2, 1, [5], 2, [4]]; // INPUT
var element = 0;
var decrement = array.length - 1;
while(element < array.length) {
while(element < decrement) {
if (array[element] === array[decrement]) {
array.splice(decrement, 1);
decrement--;
} else {
decrement--;
}
}
decrement = array.length - 1;
element++;
}
console.log(array);// [1, 3, 2, [5], [4]]
解释: 内部循环将数组的第一个元素与所有其他元素进行比较,这些元素以最高索引的元素开头。向第一个元素递减,从数组中拼接出一个副本。
当内部循环完成时,外部循环将递增到下一个元素进行比较,并重置数组的新长度。
复杂度为 O(n) 的 Vanilla JS 解决方案(此问题可能最快)。如果需要,修改 hashFunction 以区分对象(例如 1 和“1”)。第一种解决方案避免了隐藏循环(在 Array 提供的函数中很常见)。
var dedupe = function(a)
{
var hash={},ret=[];
var hashFunction = function(v) { return ""+v; };
var collect = function(h)
{
if(hash.hasOwnProperty(hashFunction(h)) == false) // O(1)
{
hash[hashFunction(h)]=1;
ret.push(h); // should be O(1) for Arrays
return;
}
};
for(var i=0; i<a.length; i++) // this is a loop: O(n)
collect(a[i]);
//OR: a.forEach(collect); // this is a loop: O(n)
return ret;
}
var dedupe = function(a)
{
var hash={};
var isdupe = function(h)
{
if(hash.hasOwnProperty(h) == false) // O(1)
{
hash[h]=1;
return true;
}
return false;
};
return a.filter(isdupe); // this is a loop: O(n)
}
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)));
循环、删除重复项并创建克隆数组占位符,因为数组索引不会更新。
向后循环以获得更好的性能(您的循环不需要不断检查数组的长度)
使用本机 javascript 函数从数组中删除重复项的最简洁方法是使用如下所示的序列:
vals.sort().reduce(function(a, b){ if (b != a[0]) a.unshift(b); return a }, [])
不需要 reduce 函数,就像我在其他示例中看到的那样!不过,将它与过滤器函数一起使用是有意义的:slice
indexOf
vals.filter(function(v, i, a){ return i == a.indexOf(v) })
另一种 ES6(2015) 方法已经在一些浏览器上工作:
Array.from(new Set(vals))
甚至使用点差运算符:
[...new Set(vals)]
干杯!
评论
set
.reduce()
不跨浏览器兼容,因为我必须应用poly-fill。不过,我很欣赏你的回复。developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/......
function arrayDuplicateRemove(arr){
var c = 0;
var tempArray = [];
console.log(arr);
arr.sort();
console.log(arr);
for (var i = arr.length - 1; i >= 0; i--) {
if(arr[i] != tempArray[c-1]){
tempArray.push(arr[i])
c++;
}
};
console.log(tempArray);
tempArray.sort();
console.log(tempArray);
}
除了是一个比当前答案更简单、更简洁的解决方案(减去面向未来的 ES6 解决方案)之外,我还对此进行了性能测试,它的速度也快得多:
var uniqueArray = dupeArray.filter(function(item, i, self){
return self.lastIndexOf(item) == i;
});
需要注意的是:Array.lastIndexOf() 是在 IE9 中添加的,所以如果你需要比这更低,你需要寻找其他地方。
var duplicates = function(arr){
var sorted = arr.sort();
var dup = [];
for(var i=0; i<sorted.length; i++){
var rest = sorted.slice(i+1); //slice the rest of array
if(rest.indexOf(sorted[i]) > -1){//do indexOf
if(dup.indexOf(sorted[i]) == -1)
dup.push(sorted[i]);//store it in another arr
}
}
console.log(dup);
}
duplicates(["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"]);
我已经在其他一些问题上对欺骗删除进行了详细的比较,但注意到这是真实的地方,我也想在这里分享它。
我相信这是最好的方法
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);
还行。。尽管这个是 O(n) 而其他是 O(n^2),但我很好奇地看到这个 reduce / 查找表和 filter/indexOf 组合之间的基准比较(我选择 Jeetendras 非常好的实现 https://stackoverflow.com/a/37441144/4543207)。我准备了一个 100K 的项目数组,里面填充了 0-9999 范围内的随机正整数,并删除了重复项。我重复测试了 10 次,结果的平均值表明它们在性能上不匹配。
- 在 firefox v47 中,reduce & lut : 14.85ms vs filter & indexOf : 2836ms
- 在chrome v51中,reduce和lut:23.90ms与filter和indexOf:1066ms
好吧,到目前为止,一切顺利。但是,这次让我们以 ES6 风格正确地做到这一点。它看起来很酷..!但截至目前,它将如何对抗强大的 lut 解决方案对我来说是一个谜。让我们先看一下代码,然后对它进行基准测试。
var myArray = [100, 200, 100, 200, 100, 100, 200, 200, 200, 200],
reduced = [...myArray.reduce((p,c) => p.set(c,true),new Map()).keys()];
console.log(reduced);
哇,太短了..!但是性能怎么样..?真漂亮。。。由于过滤器/索引的重担举过我们的肩膀,现在我可以测试一个数组 1M 个正整数的数组,范围为 0..99999,以获得 10 次连续测试的平均值。我可以说这一次是一场真正的比赛。亲眼看看结果:)
var ranar = [],
red1 = a => Object.keys(a.reduce((p,c) => (p[c] = true,p),{})),
red2 = a => reduced = [...a.reduce((p,c) => p.set(c,true),new Map()).keys()],
avg1 = [],
avg2 = [],
ts = 0,
te = 0,
res1 = [],
res2 = [],
count= 10;
for (var i = 0; i<count; i++){
ranar = (new Array(1000000).fill(true)).map(e => Math.floor(Math.random()*100000));
ts = performance.now();
res1 = red1(ranar);
te = performance.now();
avg1.push(te-ts);
ts = performance.now();
res2 = red2(ranar);
te = performance.now();
avg2.push(te-ts);
}
avg1 = avg1.reduce((p,c) => p+c)/count;
avg2 = avg2.reduce((p,c) => p+c)/count;
console.log("reduce & lut took: " + avg1 + "msec");
console.log("map & spread took: " + avg2 + "msec");
你会用哪一个..?好吧,没那么快......!不要上当受骗。地图处于位移状态。现在看...在上述所有情况下,我们用范围 < n 的数组填充大小为 n 的数组。我的意思是我们有一个大小为 100 的数组,我们用随机数 0..9 填充,所以有明确的重复项,并且“几乎”肯定每个数字都有一个重复项。如果我们用随机数 100 填充大小为 0..9999 的数组怎么样。现在让我们看看地图在家玩。这次是 100K 项的数组,但随机数范围为 0..100M。我们将进行 100 次连续测试以平均结果。好吧,让我们看看赌注..!<-没有错别字
var ranar = [],
red1 = a => Object.keys(a.reduce((p,c) => (p[c] = true,p),{})),
red2 = a => reduced = [...a.reduce((p,c) => p.set(c,true),new Map()).keys()],
avg1 = [],
avg2 = [],
ts = 0,
te = 0,
res1 = [],
res2 = [],
count= 100;
for (var i = 0; i<count; i++){
ranar = (new Array(100000).fill(true)).map(e => Math.floor(Math.random()*100000000));
ts = performance.now();
res1 = red1(ranar);
te = performance.now();
avg1.push(te-ts);
ts = performance.now();
res2 = red2(ranar);
te = performance.now();
avg2.push(te-ts);
}
avg1 = avg1.reduce((p,c) => p+c)/count;
avg2 = avg2.reduce((p,c) => p+c)/count;
console.log("reduce & lut took: " + avg1 + "msec");
console.log("map & spread took: " + avg2 + "msec");
现在这是Map()的壮观回归..!也许现在,当您想要删除重复时,您可以做出更好的决定。
好吧,我们现在都很开心。但主角总是排在最后,赢得一些掌声。我相信你们中的一些人想知道 Set 对象会做什么。既然我们对 ES6 持开放态度,并且我们知道 Map 是前几款游戏的赢家,那么让我们将 Map 与 Set 作为最终游戏进行比较。这次是典型的皇家马德里对阵巴塞罗那的比赛......或者是吗?让我们看看谁将赢得 el classico :)
var ranar = [],
red1 = a => reduced = [...a.reduce((p,c) => p.set(c,true),new Map()).keys()],
red2 = a => Array.from(new Set(a)),
avg1 = [],
avg2 = [],
ts = 0,
te = 0,
res1 = [],
res2 = [],
count= 100;
for (var i = 0; i<count; i++){
ranar = (new Array(100000).fill(true)).map(e => Math.floor(Math.random()*10000000));
ts = performance.now();
res1 = red1(ranar);
te = performance.now();
avg1.push(te-ts);
ts = performance.now();
res2 = red2(ranar);
te = performance.now();
avg2.push(te-ts);
}
avg1 = avg1.reduce((p,c) => p+c)/count;
avg2 = avg2.reduce((p,c) => p+c)/count;
console.log("map & spread took: " + avg1 + "msec");
console.log("set & A.from took: " + avg2 + "msec");
哇。。男人。。!好吧,出乎意料的是,它根本不是经典的。更像巴塞罗那对阵奥萨苏纳:))
评论
arr.reduce(...).keys(...).slice is not a function
这只是另一种解决方案,但与其他解决方案不同。
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;
}
通用功能方法
以下是 ES2015 的通用且严格功能的方法:
// 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) );
我们可以很容易地从以下方面派生或使用更快的实现:unique
unqiueBy
Set
const unqiue = uniqueBy(eq);
// const unique = xs => Array.from(new Set(xs));
这种方法的优点:
- 使用单独的比较器函数的通用解决方案
- 声明式和简洁的实现
- 重用其他小型通用函数
性能注意事项
uniqueBy
不如带有循环的命令式实现快,但由于其通用性,它更具表现力。
如果您确定是导致应用中出现具体性能损失的原因,请将其替换为优化的代码。也就是说,首先以函数式、声明性的方式编写代码。之后,如果遇到性能问题,请尝试在导致问题的位置优化代码。uniqueBy
内存消耗和垃圾回收
uniqueBy
利用隐藏在其体内的突变 ()。它重用累加器,而不是在每次迭代后将其丢弃。这样可以减少内存消耗和 GC 压力。由于这种副作用被包裹在函数内部,因此外部的一切都保持纯净。push(x) (acc)
这里对于理解和在任何地方(甚至在 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"]
function removeDuplicates (array) {
var sorted = array.slice().sort()
var result = []
sorted.forEach((item, index) => {
if (sorted[index + 1] !== item) {
result.push(item)
}
})
return result
}
到目前为止,我遇到的最简单的一个。在 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 lines = ["Mike", "Matt", "Nancy", "Adam", "Jenny", "Nancy", "Carl"];
var uniqueNames = [];
for(var i = 0; i < lines.length; i++)
{
if(uniqueNames.indexOf(lines[i]) == -1)
uniqueNames.push(lines[i]);
}
if(uniqueNames.indexOf(uniqueNames[uniqueNames.length-1])!= -1)
for(var i = 0; i < uniqueNames.length; i++)
{
document.write(uniqueNames[i]);
document.write("<br/>");
}
评论
uniqueNames.pop()
使用 lodash 快速简便 -var array = ["12346","12347","12348","12349","12349"]; console.log(_.uniqWith(array,_.isEqual));
所以选项是:
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]
var uniqueCompnies = function(companyArray) {
var arrayUniqueCompnies = [],
found, x, y;
for (x = 0; x < companyArray.length; x++) {
found = undefined;
for (y = 0; y < arrayUniqueCompnies.length; y++) {
if (companyArray[x] === arrayUniqueCompnies[y]) {
found = true;
break;
}
}
if ( ! found) {
arrayUniqueCompnies.push(companyArray[x]);
}
}
return arrayUniqueCompnies;
}
var arr = [
"Adobe Systems Incorporated",
"IBX",
"IBX",
"BlackRock, Inc.",
"BlackRock, Inc.",
];
评论
aLinks 是一个简单的 JavaScript 数组对象。如果索引显示已删除重复记录的元素之前存在任何元素。我重复取消所有重复项。一个通道数组取消更多记录。
var srt_ = 0;
var pos_ = 0;
do {
var srt_ = 0;
for (var i in aLinks) {
pos_ = aLinks.indexOf(aLinks[i].valueOf(), 0);
if (pos_ < i) {
delete aLinks[i];
srt_++;
}
}
} while (srt_ != 0);
一种简单但有效的技术是将该方法与过滤器结合使用。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>';
另请参阅此小提琴。
评论
this.indexOf(value) == index
this.indexOf(value, index+1) > 0
filterData = data.filter((v, i) => (data.indexOf(v) == i) );
var JSON_dupCounter = {};
data.filter((testItem, index) => (data.indexOf(testItem, index + 1) > 0)).forEach((found_duplicated) => (JSON_dupCounter[found_duplicated] = (JSON_dupCounter [found_duplicated] || 1) + 1));
解决方案 1
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;
}
解决方案 2(使用 Set)
Array.prototype.unique = function() {
return Array.from(new Set(this));
}
测试
var x=[1,2,3,3,2,1];
x.unique() //[1,2,3]
性能
当我在 chrome 中测试这两种实现(有和没有 Set)的性能时,我发现带有 Set 的实现要快得多!
Array.prototype.unique1 = function() {
var a = [];
for (i = 0; i < this.length; i++) {
var current = this[i];
if (a.indexOf(current) < 0) a.push(current);
}
return a;
}
Array.prototype.unique2 = function() {
return Array.from(new Set(this));
}
var x=[];
for(var i=0;i<10000;i++){
x.push("x"+i);x.push("x"+(i+1));
}
console.time("unique1");
console.log(x.unique1());
console.timeEnd("unique1");
console.time("unique2");
console.log(x.unique2());
console.timeEnd("unique2");
评论
此解决方案使用一个新数组和函数内部的对象映射。它所做的只是遍历原始数组,并将每个整数添加到对象映射中。如果在遍历原始数组时遇到重复,则
`if (!unique[int])`
捕获此属性是因为对象上已经存在具有相同编号的键属性。因此,跳过该数字并且不允许将其推送到新数组中。
function removeRepeats(ints) {
var unique = {}
var newInts = []
for (var i = 0; i < ints.length; i++) {
var int = ints[i]
if (!unique[int]) {
unique[int] = 1
newInts.push(int)
}
}
return newInts
}
var example = [100, 100, 100, 100, 500]
console.log(removeRepeats(example)) // prints [100, 500]
您可以简单地在 JavaScript 中完成,借助该方法的第二个 - index - 参数: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)
评论
a.indexOf(v)==i
a.indexOf(v) === a.lastIndexOf(v)
a
filter
[1/0, 2,1/0,2,3].filter((v,i,a) => a.indexOf(v) === i)
Infinity
.filter((v,i, array) => array.indexOf(v) == i)
使用reduce和find的方式。
const numbers = [1, 1, 2, 3, 4, 4];
function unique(array) {
return array.reduce((a, b) => {
let isIn = a.find(element => {
return element === b;
});
if (!isIn) {
a.push(b);
}
return a;
}, []);
}
let ret = unique(numbers); // [1, 2, 3, 4]
console.log(ret);
尽管 ES6 解决方案是最好的,但我对没有人展示以下解决方案感到困惑:
function removeDuplicates(arr){
o={}
arr.forEach(function(e){
o[e]=true
})
return Object.keys(o)
}
这里要记住的是,对象必须具有唯一的键。我们正在利用这一点来删除所有重复项。我本来以为这将是最快的解决方案(在 ES6 之前)。
但请记住,这也对数组进行了排序。
一行:
let names = ['Mike','Matt','Nancy','Adam','Jenny','Nancy','Carl', 'Nancy'];
let dup = [...new Set(names)];
console.log(dup);
评论
这里是简单的方法,没有任何特殊库的特殊功能,
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)
像这样使用
Array.filter()
var actualArr = ['Apple', 'Apple', 'Banana', 'Mango', 'Strawberry', 'Banana'];
console.log('Actual Array: ' + actualArr);
var filteredArr = actualArr.filter(function(item, index) {
if (actualArr.indexOf(item) == index)
return item;
});
console.log('Filtered Array: ' + filteredArr);
在 ES6 中可以将其缩短为
actualArr.filter((item,index,self) => self.indexOf(item)==index);
这是很好的解释Array.filter()
评论
ES2015,1 行,与 链接良好,但仅适用于整数:map
[1, 4, 1].sort().filter((current, next) => current !== next)
[1, 4]
评论
[1,1,2,2,3,3]
[1,2,3]
[1,2,3,1,2,3]
-> [1,2,3,1,2,3]
filter
(value, index)
(current, next)
[1,4,1]
[2,4,2]
["1", "4", "1"].sort().filter((value, index, array) => value !== array[index + 1])
对于希望将具有重复元素的数组展平为一个唯一数组的任何人:
function flattenUniq(arrays) {
var args = Array.prototype.slice.call(arguments);
var array = [].concat.apply([], args)
var result = array.reduce(function(prev, curr){
if (prev.indexOf(curr) < 0) prev.push(curr);
return prev;
},[]);
return result;
}
评论
for (i=0; i<originalArray.length; i++) {
if (!newArray.includes(originalArray[i])) {
newArray.push(originalArray[i]);
}
}
评论
_.uniq(peoplenames)
解决了这个 lodash.com/docs#uniqconst names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"]; let unique = [...new Set(names)]; console.log(unique); // 'Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Carl'