从 JS 数组中删除重复值 [重复]
Posted
技术标签:
【中文标题】从 JS 数组中删除重复值 [重复]【英文标题】:Remove duplicate values from JS array [duplicate] 【发布时间】:2012-03-03 00:46:37 【问题描述】:我有一个非常简单的 javascript 数组,它可能包含也可能不包含重复项。
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
我需要删除重复项并将唯一值放入一个新数组中。
我可以指出我尝试过的所有代码,但我认为它没有用,因为它们不起作用。我也接受 jQuery 解决方案。
类似问题:
Get all non-unique values (i.e.: duplicate/more than one occurrence) in an array【问题讨论】:
_.uniq(peoplenames)
解决了这个问题lodash.com/docs#uniq
@ConnorLeech 使用 lodash 很容易,但没有经过优化
最简单的方法(在我看来)是使用 Set 对象,它可以让您存储任何类型的唯一值。换句话说,Set 会自动为我们移除重复项。 const names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"]; let unique = [...new Set(names)]; console.log(unique); // 'Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Carl'
世界上有太多的麦克 - 为什么不删除它们?南希对此有所了解。
在我的解决方案中,我在过滤之前对数据进行排序:` const result = data.sort().filter((v, idx, t) => idx==0 || v != t[ idx-1]);
【参考方案1】:
TL;DR
使用Set 构造函数和spread syntax:
uniq = [...new Set(array)];
“聪明”但幼稚的方式
uniqueArray = a.filter(function(item, pos)
return a.indexOf(item) == pos;
)
基本上,我们遍历数组,并且对于每个元素,检查该元素在数组中的第一个位置是否等于当前位置。显然,这两个位置对于重复元素是不同的。
使用过滤器回调的第三个(“这个数组”)参数,我们可以避免数组变量的闭包:
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))
库
underscore 和 Lo-Dash 都提供了 uniq
方法。他们的算法与上面的第一个sn-p基本相似,归结为:
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;
);
这也是第一个 sn-p 的变体。
性能
函数调用在 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;
这段丑陋的代码与上面的 sn-p #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
【讨论】:
filter 和 indexOf 已在 ECMAScript 5 中引入,因此这在旧 IE 版本 ( @RoderickObrist 如果您希望您的页面在旧版浏览器中工作,您可以这样做 这是O(n^2)
解决方案,在大型阵列中运行速度可能非常慢...
试试这个数组:["toString", "valueOf", "failed"]
。 toString
和 valueOf
被完全剥离。使用Object.create(null)
而不是
。
有谁知道 Set 转换解决方案与其他解决方案相比有多快?【参考方案2】:
使用 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);
);
【讨论】:
不介意为那些不使用它的人提供非 jquery 答案 由于这已被一位知名人士恢复为原始的inArray
解决方案,所以我要再次提及:此解决方案是 O(n^2),因此效率低下。
我真的希望在 2020 年我们可以开始贬低 jQuery 和其他更过时的答案...... *** 开始在这里显示一些年龄......
我同意@NickSteele,但如果您查看选票而不是接受的答案,我发现它确实会随着时间的推移自然发生。随着旧的弃用答案被否决,最佳答案将被排在首位
让 uniqueNames = names.filter((item, pos ,self) => self.indexOf(item) == pos);【参考方案3】:
厌倦了看到所有使用 for 循环或 jQuery 的坏例子。如今,Javascript 拥有完美的工具:排序、映射和归约。
Uniq 减少同时保持现有订单
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"
就移动到了修改后的数组的前面(或后面)?
@ALx - 我更新了一个基于出现排序的示例。
sort() 在您的第二个示例中似乎被错误地调用:如果 a 是 .sort(function(a,b) return a > b ? 1 : a < b ? -1 : 0; )
如果数据只是一个名称数组,除了消除重复项之外没有其他要求,为什么还要使用排序、映射和归约呢?只需使用一组在 O(n) 时间内完成的工作。 -- msdn.microsoft.com/en-us/library/dn251547
@Dave 是的-请参阅上面[...new Set(names)]
上的示例【参考方案4】:
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 添加了新的Set
Data-Structure,它允许您存储任何类型的值。 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)
。应该注意的是,这种方法仅适用于字符串,但没关系,这就是原始问题所要求的。
还应注意,您可能会丢失数组的顺序,因为对象不会按顺序保持其属性。
@JuanMendes 我创建了一个订单安全版本,如果之前没有看到过该值,它会简单地复制到新数组。
obj[arr[i]] = true;
这一行发生了什么??
@kittu,即获取数组的i
th 元素,并将其放入对象中(用作集合)。键是元素,值是true
,这完全是任意的,因为我们只关心对象的键。【参考方案5】:
使用数组过滤器和 indexOf 函数的单行版本:
arr = arr.filter(function (value, index, array)
return array.indexOf(value) === index;
);
【讨论】:
想解释一下它是如何消除欺骗的? @web_dev:它没有!我已经更正了之前破坏代码的编辑。希望它现在更有意义。感谢您的提问! 不幸的是,如果这是一个大数组,则性能很差—— arr.indexOf 为 O(n),这使得该算法为 O(n^2) 正如@CaseyKuball 建议的那样,这个解决方案实际上非常慢 - 请参阅***.com/questions/67424599/… 伟大的 SVG 图标 :) 你给了我灵感,也是一个很好的答案。【参考方案6】:使用Underscore.js
它是一个包含大量操作数组函数的库。
这是搭配 jQuery 的 tux 和 Backbone.js 的领带 吊带。
_.uniq
_.uniq(array, [isSorted], [iterator])
别名: 独特 生成 array 的无重复版本,使用 === 测试对象 平等。如果您事先知道 array 已排序,则通过 isSorted 的 true 将运行更快的算法。如果你想 根据转换计算唯一项,传递 迭代器 功能。
Example
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
alert(_.uniq(names, false));
注意:Lo-Dash(underscore 的竞争对手)也提供了类似的 .uniq 实现。
【讨论】:
不幸的是,下划线不提供定义自定义相等函数的能力。他们确实允许的回调是针对“迭代”函数的,例如带参数(项目、值、数组)。[...new Set(Array)]
是绰绰有余的伙伴
@norbekoff - 绝对,哈哈。 ~10 年后!【参考方案7】:
一行:
let names = ['Mike','Matt','Nancy','Adam','Jenny','Nancy','Carl', 'Nancy'];
let dup = [...new Set(names)];
console.log(dup);
【讨论】:
最佳答案,如果您使用的是 ES6 这三个点是什么意思? @Vitalicus,这是 ES6 中的扩展运算符。阅读更多here【参考方案8】:借助filter
方法的第二个 - 索引 - 参数,您可以在 JavaScript 中简单地做到这一点:
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)
【讨论】:
这只适用于包含原语的数组? 是的,你是对的@frozen 这个a.indexOf(v)==i
应该是a.indexOf(v) === a.lastIndexOf(v)
@Hitmands 你从右边比较,我从左边比较。没有别的。
在不需要a
变量的情况下也可以工作,因为数组是filter
的第三个参数:[1/0, 2,1/0,2,3].filter((v,i,a) => a.indexOf(v) === i)
(请注意,它也适用于Infinity
☺)【参考方案9】:
像这样使用
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);
Here 很好地解释了Array.filter()
【讨论】:
你能详细说明你在这里做了什么吗? :-) 太棒了!如果您将其添加到您的答案中,它将对用户有所帮助。 当数组是数组数组时不起作用 不适用于区分大小写的数组【参考方案10】:使用原生 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))
甚至使用spread operator:
[...new Set(vals)]
干杯!
【讨论】:
Set 对于那些习惯使用 python 的人来说非常棒并且非常直观。太糟糕了,他们没有那些很棒的(联合、相交、差异)方法。 我使用了使用set
机制的简单的一行代码。这是针对自定义自动化任务的,因此我对在最新版本的 Chrome(在 jsfiddle 内)中使用它并不持怀疑态度。但是,我仍然想知道对数组进行重复数据删除的最短所有浏览器兼容方法。
集合是新规范的一部分,你应该使用 sort/reduce 组合来确保跨浏览器的兼容性@AlexanderDixon
.reduce()
不是跨浏览器兼容的,因为我必须应用 poly-fill。不过,我很感谢你的回应。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…【参考方案11】:
最佳答案的复杂度为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)];
实现如此简单,不再需要定义函数。
【讨论】:
您是否考虑过您的方法对性能的影响? @Tushar - 您的要点给出了 404。没有排序算法具有O(n)
复杂性。排序不会更快。
@Tushar - 该数组中没有实际的重复项。如果要从数组中删除与数组中其他对象具有完全相同属性和值的对象,则需要编写自定义相等检查函数来支持它。
@Tushar - 此页面上的任何答案都不会从your gist 中的此类数组中删除任何重复项。
请注意,IE 参加 Set 聚会迟到了【参考方案12】:
迄今为止我遇到的最简单的一个。在 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
【讨论】:
对于 Mac 用户,即使这是一个 ES6 函数,它也可以在 macOS 10.11.6 El Capitan 中使用,使用脚本编辑器。 这是一个非常简单干净的解决方案!【参考方案13】:在 ECMAScript 6(又名 ECMAScript 2015)中,Set
可用于过滤掉重复项。然后可以使用spread operator 将其转换回数组。
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"],
unique = [...new Set(names)];
【讨论】:
Set的构造函数实际上需要new
关键字
@Ivo 谢谢。以前 Firefox 的实现不需要new
,不知道 ES6 草案是否改变了这种行为。
一些构造函数可能确实被称为函数,尽管这种行为取决于浏览器对规范的实现;)
完美答案!【参考方案14】:
解决方案 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");
【讨论】:
赞成使用 Set。虽然我不知道性能比较 我在某处读到 Array 比 Set 快(整体性能),但是当我在 chrome 中测试时,使用 Set 的实现要快得多!查看编辑后的答案:) 更好的做法是使用 Object.defineProperty(Array.prototype,"unique".. 而不是 Array.prototype.unique = ... 在此处查看更多信息***.com/questions/10105824/… Set 方法似乎不适用于 Node.js 中的我。 new Set([5,5]) 在某些情况下似乎返回 [5,5]。我和你一样困惑。编辑:我发现发生了什么。 new Set([new Number(5), new Number(5)]) 返回 [5,5]。显然,如果我用 new 实例化这两个数字 5,Node 会认为它们是不同的......老实说,这是我见过的最愚蠢的事情。 @Demonblack 这是一个有效的问题。 x=new Number(5) 和另一个 y=new Number(5) 将是两个不同的对象,与 var x=5 和 var y=5 相反。 new 关键字将创建一个新对象。我知道这个解释很明显,但我只知道这些:)【参考方案15】:去这个:
var uniqueArray = duplicateArray.filter(function(elem, pos)
return duplicateArray.indexOf(elem) == pos;
);
现在 uniqueArray 不包含重复项。
【讨论】:
我认为,最好的!【参考方案16】:我在其他一些问题上对删除欺骗进行了详细比较,但注意到这是我只想在这里分享的真实地方。
我相信这是最好的方法
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) 我很想看看这个减少/查找表和 filter/indexOf 组合之间的基准比较(我选择 Jeetendras 非常好实现https://***.com/a/37441144/4543207)。我准备了一个 100K 项目数组,其中填充了 0-9999 范围内的随机正整数,并删除了重复项。我重复测试了 10 次,结果的平均值表明它们在性能上是不匹配的。
在 firefox v47 中,reduce & lut:14.85ms vs filter & indexOf:2836ms 在 chrome v51 中,reduce & lut:23.90ms vs 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);
哇,太短了..!但是性能怎么样..?太漂亮了...由于过滤器/ indexOf 的重量已经举到我们的肩上,现在我可以测试一个数组 0..99999 范围内的 1M 个正整数随机项,以获得 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");
你会用哪一个..?好吧,没那么快……!不要被欺骗。地图处于位移状态。现在看……在上述所有情况下,我们用范围
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");
哇.. 伙计..!出乎意料的是,它根本就不是经典之作。更像是巴塞罗那足球俱乐部对阵 CA 奥萨苏纳 :))
【讨论】:
顺便说一句,我在 Typescript 中得到arr.reduce(...).keys(...).slice is not a function
试图使用你的 ES6 方法【参考方案17】:
以下方法比列出的 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
【讨论】:
我在修订版 4 中添加了更快的版本。请查看! 测试好像没有使用数组???我添加了(又一个)一个在不同浏览器上似乎始终快速的(参见jsperf.com/remove-duplicate-array-tests/10):for (var n = array.length, result = [array[n--]], i; n-- ;) i = 数组[n]; if (!(i in result)) result.push(i); 返回结果;【参考方案18】:这里是问题的简单答案。
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]);
【讨论】:
为===
+1。如果我们不检查它的类型,它将不适用于具有混合类型的数组。简单而有效的答案【参考方案19】:
一个简单但有效的技术,就是将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 Fiddle。
【讨论】:
天才!而且,例如,如果您想要重复的,(而不是删除它们)您所要做的就是将this.indexOf(value) == index
替换为 this.indexOf(value, index+1) > 0
谢谢!
您甚至可以将其恢复到单个“过滤器”行:filterData = data.filter((v, i) => (data.indexOf(v) == i) );
上次打扰了!抱歉...拿起我的第一个答案,在 2 行中,您可以得到一个 JSON 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));
这只适用于基元数组?
@frozen :如果适用于所有==
可用于确定相等性的事物。所以,如果你正在处理例如。数组、对象或函数,过滤器仅适用于引用相同数组、对象或函数的不同条目 (see demo)。如果您想根据 different criteria 确定相等性,则需要在过滤器中包含这些条件。【参考方案20】:
所以选项是:
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]
【讨论】:
【参考方案21】:这里很容易理解和在任何地方工作(甚至在 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"]
【讨论】:
【参考方案22】:这里是简单的方法,没有任何特殊库的特殊功能,
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)
【讨论】:
【参考方案23】:除了比当前答案更简单、更简洁的解决方案(减去面向未来的 ES6 解决方案)之外,我还对其进行了测试,而且速度也快得多:
var uniqueArray = dupeArray.filter(function(item, i, self)
return self.lastIndexOf(item) == i;
);
一个警告:Array.lastIndexOf() 是在 IE9 中添加的,因此如果您需要低于此值,则需要查看其他地方。
【讨论】:
【参考方案24】:通用函数方法
这是 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) );
我们可以很容易地从unqiueBy
派生unique
,或者使用更快的实现,利用Set
s:
const unqiue = uniqueBy(eq);
// const unique = xs => Array.from(new Set(xs));
这种方法的好处:
使用单独的比较器函数的通用解决方案 声明式和简洁的实现 重用其他小型通用函数性能注意事项
uniqueBy
不如使用循环的命令式实现快,但由于其通用性,它更具表现力。
如果您将 uniqueBy
确定为应用中具体性能下降的原因,请将其替换为优化代码。也就是说,首先以功能性、声明性的方式编写代码。之后,如果您遇到性能问题,请尝试在导致问题的位置优化代码。
内存消耗和垃圾回收
uniqueBy
利用隐藏在其体内的突变 (push(x) (acc)
)。它重用累加器,而不是在每次迭代后将其丢弃。这减少了内存消耗和 GC 压力。由于这个副作用被包裹在函数内部,所以外部的一切都保持纯净。
【讨论】:
【参考方案25】:for (i=0; i<originalArray.length; i++)
if (!newArray.includes(originalArray[i]))
newArray.push(originalArray[i]);
【讨论】:
喜欢香草js。谢谢【参考方案26】:如果你有任何机会使用
D3.js
你可以的
d3.set(["foo", "bar", "foo", "baz"]).values() ==> ["foo", "bar", "baz"]
https://github.com/mbostock/d3/wiki/Arrays#set_values
【讨论】:
很漂亮,但是加载功能强大的完整可视化库以仅过滤重复项似乎有点过头了。幸运的是,我出于某种目的需要该库,我将使用它。非常感谢。【参考方案27】:$(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);
);
【讨论】:
【参考方案28】:以下脚本返回一个仅包含唯一值的新数组。它适用于字符串和数字。不需要额外的库,只有 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);
【讨论】:
【参考方案29】:对 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);
);
【讨论】:
【参考方案30】:虽然 ES6 解决方案是最好的,但我对没有人展示以下解决方案感到困惑:
function removeDuplicates(arr)
o=
arr.forEach(function(e)
o[e]=true
)
return Object.keys(o)
这里要记住的是对象必须有唯一的键。我们正在利用它来删除所有重复项。我原以为这将是最快的解决方案(在 ES6 之前)。
请记住,这也会对数组进行排序。
【讨论】:
以上是关于从 JS 数组中删除重复值 [重复]的主要内容,如果未能解决你的问题,请参考以下文章