我有一组数字,我需要确保它们是唯一的。我在互联网上找到了下面的代码片段,它工作得很好,直到数组中有一个零。我在 Stack Overflow 上发现 this other script 看起来几乎一模一样,但它并没有失败。
所以为了帮助我学习,谁能帮我确定原型脚本哪里出错了?
Array.prototype.getUnique = function() {
var o = {}, a = [], i, e;
for (i = 0; e = this[i]; i++) {o[e] = 1};
for (e in o) {a.push (e)};
return a;
}
来自重复问题的更多答案:
从 JS 数组中删除重复值
类似的问题:
获取数组中的所有非唯一值(即:重复/多次出现)
o
= object
,a
= array
,i
= index
和 e
=嗯,有些东西:P
使用 JavaScript 1.6 / ECMAScript 5,您可以通过以下方式使用数组的原生 filter
方法来获取具有唯一值的数组:
function onlyUnique(value, index, self) { return self.indexOf(value) === index; } // 用法示例: var a = ['a', 1, 'a', 2, '1']; var unique = a.filter(onlyUnique);控制台.log(唯一); // ['a', 1, 2, '1']
本机方法 filter
将遍历数组并只留下那些通过给定回调函数 onlyUnique
的条目。
onlyUnique
检查给定值是否是第一个出现的值。如果不是,它必须是重复的,不会被复制。
此解决方案无需任何额外的库(如 jQuery 或原型.js)即可工作。
它也适用于具有混合值类型的数组。
对于不支持本机方法 filter
和 indexOf
的旧浏览器 (
如果要保留最后一次出现的值,只需将 indexOf
替换为 lastIndexOf
。
使用 ES6,这可以缩短为:
// 使用示例: var myArray = ['a', 1, 'a', 2, '1']; var unique = myArray.filter((v, i, a) => a.indexOf(v) === i);控制台.log(唯一); // 唯一的是 ['a', 1, 2, '1']
感谢 Camilo Martin 的评论提示。
ES6 有一个原生对象 Set
来存储唯一值。要获取具有唯一值的数组,您现在可以这样做:
var myArray = ['a', 1, 'a', 2, '1']; let unique = [...new Set(myArray)];控制台.log(唯一); // 唯一的是 ['a', 1, 2, '1']
Set
的构造函数采用可迭代对象,如数组,扩展运算符 ...
将集合转换回数组。感谢 Lukas Liese 的评论提示。
ES6/ES2015 的更新答案:使用 Set 和 the spread operator(感谢 le-m),单行解决方案是:
let uniqueItems = [...new Set(items)]
哪个返回
[4, 5, 6, 3, 2, 23, 1]
Array.from(new Set([[1,2],[1,2],[1,2,3]]))
Set
并添加对象而不是原始值,它将包含对对象的唯一引用。因此,let s = new Set([{Foo:"Bar"}, {Foo:"Bar"}]);
中的集合 s
将返回:Set { { Foo: 'Bar' }, { Foo: 'Bar' } }
,它是一个具有对包含相同值的对象的唯一对象引用的 Set
。如果您编写 let o = {Foo:"Bar"};
,然后创建一个包含两个 references 的集合,如下所示:let s2 = new Set([o,o]);
,那么 s2 将是 Set { { Foo: 'Bar' } }
Array.from( new Set( items ) )
我将所有答案拆分为 4 种可能的解决方案:
使用 object { } 防止重复 使用辅助数组 [ ] 使用 filter + indexOf 奖励! ES6 设置方法。
以下是答案中的示例代码:
使用 object { } 防止重复
function uniqueArray1( ar ) {
var j = {};
ar.forEach( function(v) {
j[v+ '::' + typeof v] = v;
});
return Object.keys(j).map(function(v){
return j[v];
});
}
使用辅助数组 [ ]
function uniqueArray2(arr) {
var a = [];
for (var i=0, l=arr.length; i<l; i++)
if (a.indexOf(arr[i]) === -1 && arr[i] !== '')
a.push(arr[i]);
return a;
}
使用过滤器 + indexOf
function uniqueArray3(a) {
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
// usage
var unique = a.filter( onlyUnique ); // returns ['a', 1, 2, '1']
return unique;
}
使用 ES6 [...new Set(a)]
function uniqueArray4(a) {
return [...new Set(a)];
}
我想知道哪个更快。我制作了 sample Google Sheet 来测试功能。注意:ECMA 6 在 Google 表格中不可用,因此我无法对其进行测试。
https://i.stack.imgur.com/EqG6p.png
我希望看到使用对象 { }
的代码会获胜,因为它使用哈希。所以我很高兴测试显示了该算法在 Chrome 和 IE 中的最佳结果。感谢@rab the code。
2020 年更新
Google Script 启用了 ES6 引擎。现在我用 Sets
测试了最后一个代码,它看起来比对象方法更快。
uniqueItems = [...new Set(items)]
似乎是所有方法中最快和最简洁的?
您也可以使用 underscore.js。
console.log(_.uniq([1, 2, 1, 3, 1, 4]));
这将返回:
[1, 2, 3, 4]
array = [...new Set(array)]
完成的小工作添加额外的库
一个班轮,纯 JavaScript
使用 ES6 语法
list = list.filter((x, i, a) => a.indexOf(x) == i)
x --> item in array
i --> index of item
a --> array reference, (in this case "list")
https://i.stack.imgur.com/TfGaP.png
使用 ES5 语法
list = list.filter(function (x, i, a) {
return a.indexOf(x) == i;
});
浏览器兼容性:IE9+
这里的许多答案可能对初学者没有用处。如果对数组进行重复数据删除很困难,他们真的会知道原型链,甚至 jQuery 吗?
在现代浏览器中,一个干净且简单的解决方案是将数据存储在 Set 中,该 Set 被设计为唯一值的列表。
const cars = ['沃尔沃', '吉普车', '沃尔沃', '林肯', '林肯', '福特']; const uniqueCars = Array.from(new Set(cars)); console.log(uniqueCars);
Array.from
对于将 Set 转换回数组很有用,这样您就可以轻松访问数组拥有的所有很棒的方法(功能)。也有other ways做同样的事情。但是您可能根本不需要 Array.from
,因为 Set 有很多有用的功能,例如 forEach。
如果您需要支持旧的 Internet Explorer,因此无法使用 Set,那么一种简单的技术是将项目复制到新数组中,同时预先检查它们是否已经在新数组中。
// Create a list of cars, with duplicates.
var cars = ['Volvo', 'Jeep', 'Volvo', 'Lincoln', 'Lincoln', 'Ford'];
// Create a list of unique cars, to put a car in if we haven't already.
var uniqueCars = [];
// Go through each car, one at a time.
cars.forEach(function (car) {
// The code within the following block runs only if the
// current car does NOT exist in the uniqueCars list
// - a.k.a. prevent duplicates
if (uniqueCars.indexOf(car) === -1) {
// Since we now know we haven't seen this car before,
// copy it to the end of the uniqueCars list.
uniqueCars.push(car);
}
});
为了使它立即可重用,让我们把它放在一个函数中。
function deduplicate(data) {
if (data.length > 0) {
var result = [];
data.forEach(function (elem) {
if (result.indexOf(elem) === -1) {
result.push(elem);
}
});
return result;
}
}
所以为了摆脱重复,我们现在就这样做。
var uniqueCars = deduplicate(cars);
当函数完成时,deduplicate(cars)
部分变成我们命名为 result 的东西。
只需将您喜欢的任何数组的名称传递给它。
["volvo","lincoln"]
Map
来存储以前看到的项目和一个数组来存储重复的项目。然后遍历 cars
数组并检查 Map 是否有当前项,如果有则将其推送到重复数组,如果没有则将其添加到 Map。如果您创建一个新问题,我很乐意为您创建一个代码示例,我们可以在那里继续讨论。
使用 ES6 新设置
var 数组 = [3,7,5,3,2,5,2,7]; var unique_array = [...new Set(array)]; console.log(unique_array); // 输出 = [3,7,5,2]
使用 For 循环
var 数组 = [3,7,5,3,2,5,2,7]; for(var i=0;i
从那以后,我发现了一个使用 jQuery 的好方法
arr = $.grep(arr, function(v, k){
return $.inArray(v ,arr) === k;
});
注意:此代码是从 Paul Irish's duck punching post 中提取的 - 我忘了注明:P
使用 Set
删除重复项。
// Array with duplicates⤵️
const withDuplicates = [2, 2, 5, 5, 1, 1, 2, 2, 3, 3];
// Get new array without duplicates by using Set
// [2, 5, 1, 3]
const withoutDuplicates = Array.from(new Set(arrayWithDuplicates));
一个较短的版本,如下:
const withoutDuplicates = [...new Set(arrayWithDuplicates)];
最简单的 fastest(在 Chrome 中)执行此操作的方法:
Array.prototype.unique = function() {
var a = [];
for (var i=0, l=this.length; i<l; i++)
if (a.indexOf(this[i]) === -1)
a.push(this[i]);
return a;
}
只需遍历数组中的每个项目,测试该项目是否已经在列表中,如果不是,则推送到返回的数组。
根据 JSBench,这个函数是 the fastest of the ones I could find anywhere - 随意添加你自己的。
非原型版本:
function uniques(arr) {
var a = [];
for (var i=0, l=arr.length; i<l; i++)
if (a.indexOf(arr[i]) === -1 && arr[i] !== '')
a.push(arr[i]);
return a;
}
排序
当还需要对数组进行排序时,以下是最快的:
Array.prototype.sortUnique = function() {
this.sort();
var last_i;
for (var i=0;i<this.length;i++)
if ((last_i = this.lastIndexOf(this[i])) !== i)
this.splice(i+1, last_i-i);
return this;
}
或非原型:
function sortUnique(arr) {
arr.sort();
var last_i;
for (var i=0;i<arr.length;i++)
if ((last_i = arr.lastIndexOf(arr[i])) !== i)
arr.splice(i+1, last_i-i);
return arr;
}
这也是大多数非 Chrome 浏览器中的 faster than the above method。
unique
函数中实现的算法具有 O(n^2) 复杂度,而 getUnique
中的算法是 O(n)。第一个在小型数据集上可能更快,但你怎么能与数学争论:) 如果你在一组 1e5 个独特项目上运行它,你可以确保后者更快
input_array.length < 200
,否则使用 [...new Set(input_array)]
方法。表示为减速器:input_array.reduce((c, v) => {if (!c.includes(v)) c.push(v); return c;}, [])
我们可以使用 ES6 集合来做到这一点:
var duplicatesArray = [1, 2, 3, 4, 5, 1, 1, 1, 2, 3, 4]; var uniqueArray = [...new Set(duplicatesArray)]; console.log(uniqueArray); // [1,2,3,4,5]
魔法
a.filter(e=>!(t[e]=e in t))
O(n) 性能(比 new Set
快);我们假设您的数组位于 a
和 t={}
中。说明 here(+Jeppe 次展示)
让 t, unique= a=> ( t={}, a.filter(e=>!(t[e]=e in t)) ); // 使用全局 t 的“独立”版本: // a1.filter((t={},e=>!(t[e]=e in t))); // 测试数据 let a1 = [5,6,0,4,9,2,3,5,0,3,4,1,5,4,9];令 a2 = [[2, 17], [2, 17], [2, 17], [1, 12], [5, 9], [1, 12], [6, 2], [1, 12 ]];让a3 = ['迈克','亚当','马特','南希','亚当','珍妮','南希','卡尔']; // 结果 console.log(JSON.stringify( unique(a1) )) console.log(JSON.stringify( unique(a2) )) console.log(JSON.stringify( unique(a3) ))
["Defects", "Total", "Days", "City", "Defects"].reduce(function(prev, cur) {
return (prev.indexOf(cur) < 0) ? prev.concat([cur]) : prev;
}, []);
[0,1,2,0,3,2,1,5].reduce(function(prev, cur) {
return (prev.indexOf(cur) < 0) ? prev.concat([cur]) : prev;
}, []);
这个原型 getUnique
并不完全正确,因为如果我有一个像 ["1",1,2,3,4,1,"foo"]
这样的数组,它将返回 ["1","2","3","4"]
并且 "1"
是字符串,而 1
是整数;它们不一样。
这是一个正确的解决方案:
Array.prototype.unique = function(a){
return function(){ return this.filter(a) }
}(function(a,b,c){ return c.indexOf(a,b+1) < 0 });
使用:
var foo;
foo = ["1",1,2,3,4,1,"foo"];
foo.unique();
以上将产生 ["1",2,3,4,1,"foo"]
。
$foo = 'bar'
是声明变量的 PHP 方式。它可以在 javascript 中工作,但会创建一个隐式全局,通常不应该这样做。
$foo
是在 javascript 中声明变量的方式,而实际上 var foo
是。
您可以简单地使用内置函数 Array.prototype.filter()
和 Array.prototype.indexOf()
array.filter((x, y) => array.indexOf(x) == y)
var arr = [1, 2, 3, 3, 4, 5, 5, 5, 6, 7, 8, 9, 6, 9]; var newarr = arr.filter((x, y) => arr.indexOf(x) == y);控制台.log(newarr);
在这里查看了所有 90 多个答案后,我发现还有一个空间:
Array.includes 有一个非常方便的第二个参数:"fromIndex",因此通过使用它,filter
回调方法的每次迭代都会搜索 数组,从[current index] + 1
保证不在查找中包含当前过滤的项目,同时也节省了时间。
注意 - 此解决方案不保留顺序,因为它从左到右删除了重复的项目,但如果 Array 是对象的集合,它会赢得 Set 技巧。
// 🚩 🚩 🚩 var list = [0,1,2,2,3,'a','b',4,5,2,'a'] console.log( list.filter((v,i) => !list.includes(v,i+1)) ) // [0,1,3,"b",4,5,2,"a"]
解释:
例如,假设 filter
函数当前在索引 2
处迭代,并且该索引处的值恰好是 2
。然后扫描重复项(includes
方法)的数组部分是 索引 2 (i+1
) 之后的所有内容:
👇 👇
[0, 1, 2, 2 ,3 ,'a', 'b', 4, 5, 2, 'a']
👆 |---------------------------|
并且由于当前过滤项的值 2
包含在数组的其余部分中,因此它将被过滤掉,因为前导感叹号否定了过滤规则。
如果顺序很重要,请使用以下方法:
// 🚩 🚩 🚩 var list = [0,1,2,2,3,'a','b',4,5,2,'a'] console.log( // 用空数组初始化并填充非重复 list.reduce((acc, v) => (!acc.includes(v) && acc.push(v), acc), []) ) // [0,1,2,3,"a ","b",4,5]
[...new Set(duplicates)]
这是最简单的一种,引用自 MDN Web Docs。
const numbers = [2,3,4,4,2,3,3,4,4,5,5,6,6,7,5,32,3,4,5]
console.log([...new Set(numbers)]) // [2, 3, 4, 5, 6, 7, 32]
这已经得到了很多回答,但它并没有解决我的特殊需求。
很多答案是这样的:
a.filter((item, pos, self) => self.indexOf(item) === pos);
但这不适用于复杂对象的数组。
假设我们有一个这样的数组:
const a = [
{ age: 4, name: 'fluffy' },
{ age: 5, name: 'spot' },
{ age: 2, name: 'fluffy' },
{ age: 3, name: 'toby' },
];
如果我们想要具有唯一名称的对象,我们应该使用 array.prototype.findIndex
而不是 array.prototype.indexOf
:
a.filter((item, pos, self) => self.findIndex(v => v.name === item.name) === pos);
indexOf
解决方案,数组 [ { a: 2 }, { a: 2 } ] 不会像许多人期望的那样工作,但是 { 2} 解决方案可能有用
Array.prototype.getUnique = function() {
var o = {}, a = []
for (var i = 0; i < this.length; i++) o[this[i]] = 1
for (var e in o) a.push(e)
return a
}
o
而不是仅 1
中来解决,尽管相等比较仍然是按字符串进行的(尽管在所有可能的 Javascript 相等中,它似乎并不太不合理)。
在不扩展 Array.prototype(据说这是一种不好的做法)或使用 jquery/underscore 的情况下,您可以简单地 filter
数组。
通过保留最后一次出现:
function arrayLastUnique(array) {
return array.filter(function (a, b, c) {
// keeps last occurrence
return c.indexOf(a, b + 1) < 0;
});
},
或第一次出现:
function arrayFirstUnique(array) {
return array.filter(function (a, b, c) {
// keeps first occurrence
return c.indexOf(a) === b;
});
},
好吧,它只是 javascript ECMAScript 5+,这意味着只有 IE9+,但它非常适合原生 HTML/JS(Windows Store App、Firefox OS、Sencha、Phonegap、Titanium 等)的开发。
filter
。在 MDN page 他们有一个 Internet Explorer 的实现,我的意思是,旧浏览器。另外:JS 1.6 仅指 Firefox 的 js 引擎,但正确的说法是它是 ECMAScript 5。
这是因为 0
在 JavaScript 中是一个虚假值。
如果数组的值为 0 或任何其他虚假值,this[i]
将是虚假的。
如果您使用的是 Prototype 框架,则无需执行“for”循环,您可以像这样使用 http://prototypejs.org/doc/latest/language/Array/prototype/uniq/:
var a = Array.uniq();
这将产生一个没有重复的重复数组。我遇到了您的问题,搜索了一种计算不同数组记录的方法,因此在 uniq()
之后我使用了 size()
并且得到了简单的结果。 ps对不起,如果我打错了什么
编辑:如果您想转义未定义的记录,您可能需要在 compact()
之前添加,如下所示:
var a = Array.compact().uniq();
现在使用集合,您可以删除重复项并将它们转换回数组。
var 名称 = ["迈克","马特","南希","马特","亚当","珍妮","南希","卡尔"]; console.log([...new Set(names)])
另一种解决方案是使用排序和过滤
var 名称 = ["迈克","马特","南希","马特","亚当","珍妮","南希","卡尔"]; var namesSorted = names.sort();常量结果 = namesSorted.filter((e, i) => namesSorted[i] != namesSorted[i+1]);控制台.log(结果);
我有一个稍微不同的问题,我需要从数组中删除具有重复 id 属性的对象。这行得通。
让 objArr = [{ id: '123' }, { id: '123' }, { id: '456' }]; objArr = objArr.reduce((acc, cur) => [ ...acc.filter((obj) => obj.id !== cur.id), cur ], []);控制台.log(objArr);
如果您对额外的依赖项没有问题,或者您的代码库中已经有一个库,您可以使用 LoDash(或下划线)从适当的数组中删除重复项。
用法
如果您的代码库中还没有它,请使用 npm 安装它:
npm install lodash
然后按如下方式使用它:
import _ from 'lodash';
let idArray = _.uniq ([
1,
2,
3,
3,
3
]);
console.dir(idArray);
出去:
[ 1, 2, 3 ]
_.uniqWith(objectArray, _.isEqual)
。
我不确定为什么 Gabriel Silveira 以这种方式编写函数,但一种更简单的形式也适用于我并且没有缩小:
Array.prototype.unique = function() {
return this.filter(function(value, index, array) {
return array.indexOf(value, index + 1) < 0;
});
};
或在 CoffeeScript 中:
Array.prototype.unique = ->
this.filter( (value, index, array) ->
array.indexOf(value, index + 1) < 0
)
以简单的方法查找唯一的数组值
function arrUnique(a){
var t = [];
for(var x = 0; x < a.length; x++){
if(t.indexOf(a[x]) == -1)t.push(a[x]);
}
return t;
}
arrUnique([1,4,2,7,1,5,9,2,4,7,2]) // [1, 4, 2, 7, 5, 9]
看来我们已经失去了 Rafael's answer,这是几年来公认的答案。这是(至少在 2017 年)表现最好的解决方案 if you don't have a mixed-type array:
Array.prototype.getUnique = function(){
var u = {}, a = [];
for (var i = 0, l = this.length; i < l; ++i) {
if (u.hasOwnProperty(this[i])) {
continue;
}
a.push(this[i]);
u[this[i]] = 1;
}
return a;
}
如果你确实有一个混合类型的数组,你可以序列化散列键:
Array.prototype.getUnique = function() {
var hash = {}, result = [], key;
for ( var i = 0, l = this.length; i < l; ++i ) {
key = JSON.stringify(this[i]);
if ( !hash.hasOwnProperty(key) ) {
hash[key] = true;
result.push(this[i]);
}
}
return result;
}
奇怪的是,这之前没有被建议过.. 要通过数组中的对象键(下面的 id
)删除重复项,您可以执行以下操作:
const uniqArray = array.filter((obj, idx, arr) => (
arr.findIndex((o) => o.id === obj.id) === idx
))
filter()
和 findIndex()
不是都必须遍历数组吗?这将使这成为一个双循环,因此运行成本是此处任何其他答案的两倍。
new Set()
或类似于 Grozz 答案的查找对象。
对于具有一些唯一 id 的基于对象的数组,我有一个简单的解决方案,您可以通过它对线性复杂度进行排序
function getUniqueArr(arr){
const mapObj = {};
arr.forEach(a => {
mapObj[a.id] = a
})
return Object.values(mapObj);
}
.filter((v,i,a)=>a.indexOf(v)==i)
(粗箭头符号)。let unique_values = [...new Set(random_array)];
developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…[...
...]
?new Set(myArray)
本身似乎具有相同的行为