如何获取数组中的唯一值[重复]
Posted
技术标签:
【中文标题】如何获取数组中的唯一值[重复]【英文标题】:How to get unique values in an array [duplicate] 【发布时间】:2012-06-30 02:21:14 【问题描述】:如何获取数组中唯一值的列表?我总是必须使用第二个数组还是在 javascript 中有类似于 java 的 hashmap 的东西?
我将只使用 JavaScript 和 jQuery。不能使用其他库。
【问题讨论】:
***.com/questions/5381621/… - 准确描述你想要的我的想法? 您愿意使用underscore.js
库吗?
java hashmap 与 javascript 对象基本相同。语法是 "key": "value", "key2": "value2"
JavaScript/TypeScript 集合 API 与 Scala 相比很糟糕,list.toSet
这取决于数组中的内容、您如何定义“唯一性”以及您的数据有多大。如果它们是对象,则代码不同于数字或字符串,并且如果数据很大,您将需要线性解决方案而不是二次解决方案。请提供更多详细信息。
【参考方案1】:
在 Javascript 中不是原生的,但很多库都有这种方法。
Underscore.js 的 _.uniq(array)
(link) 效果很好 (source)。
【讨论】:
感谢分享!此函数将迭代器和上下文以及数组作为 v1.4.3 中的参数列表。【参考方案2】:使用 jQuery,这是我制作的 Array 唯一函数:
Array.prototype.unique = function ()
var arr = this;
return $.grep(arr, function (v, i)
return $.inArray(v, arr) === i;
);
console.log([1,2,3,1,2,3].unique()); // [1,2,3]
【讨论】:
如果你打算在核心javascript对象的原型中使用jQuery,写一个jQuery函数会不会更好,比如$.uniqueArray(arr)
?在 Array
的原型中嵌入对 jQuery 的引用似乎有问题
@jackwanders:这有什么问题?如果页面上有 jQuery,让我们使用它。
只是你写的新的唯一函数现在依赖于jQuery;如果不确保 jQuery 正在那里使用,则不能将其移动到新站点或应用程序。
那是我的观点;如果你打算使用 jQuery,那么让函数本身成为 jQuery 的一部分。如果我要扩展核心对象的原型,我会坚持使用核心 javascript,只是为了保持可重用性。如果其他人正在查看您的代码,很明显$.uniqueArray
依赖于 jQuery; Array.prototype.unique
也不太明显。
@jackwanders:我猜。我在我的代码中使用它,因为我总是使用 jQuery,我只是喜欢扩展 prototype
s。但是,我现在明白你的意思了。无论如何我都会把它留在这里。【参考方案3】:
如果你想保持原数组不变,
你需要第二个数组来包含第一个的唯一元素-
大多数浏览器都有Array.prototype.filter
:
var unique= array1.filter(function(itm, i)
return array1.indexOf(itm)== i;
// returns true for only the first instance of itm
);
//if you need a 'shim':
Array.prototype.filter= Array.prototype.filter || function(fun, scope)
var T= this, A= [], i= 0, itm, L= T.length;
if(typeof fun== 'function')
while(i<L)
if(i in T)
itm= T[i];
if(fun.call(scope, itm, i, T)) A[A.length]= itm;
++i;
return A;
Array.prototype.indexOf= Array.prototype.indexOf || function(what, i)
if(!i || typeof i!= 'number') i= 0;
var L= this.length;
while(i<L)
if(this[i]=== what) return i;
++i;
return -1;
【讨论】:
【参考方案4】:由于我在 cmets 中继续讨论 @Rocket 的答案,我不妨提供一个不使用库的示例。这需要两个新的原型函数,contains
和 unique
Array.prototype.contains = function(v)
for (var i = 0; i < this.length; i++)
if (this[i] === v) return true;
return false;
;
Array.prototype.unique = function()
var arr = [];
for (var i = 0; i < this.length; i++)
if (!arr.contains(this[i]))
arr.push(this[i]);
return arr;
var duplicates = [1, 3, 4, 2, 1, 2, 3, 8];
var uniques = duplicates.unique(); // result = [1,3,4,2,8]
console.log(uniques);
为了更可靠,您可以用 MDN 的 indexOf
shim 替换 contains
并检查每个元素的 indexOf
是否等于 -1:documentation
【讨论】:
感谢您的示例。我将使用它们来过滤选择框的选项。这应该很好用。 这具有很高的运行时间复杂度(最坏情况:O(n^2)) 这是一个非常低效的实现。检查结果数组以查看它是否已经包含一个项目是可怕的。更好的方法是使用跟踪计数的对象,或者如果您不想使用辅助存储,请先在 O(n log n) 中对其进行排序,然后进行线性扫描并并排比较元素 我们真的需要“包含”功能吗?Array.from(new Set(arr))
非常 更快:jsperf.com/unique-func-vs-set/1 - 公平地说,这可能是一个很好的答案,但你不应该 立即使用。【参考方案5】:
我只是在想我们是否可以使用线性搜索来消除重复项:
JavaScript:
function getUniqueRadios()
var x=document.getElementById("QnA");
var ansArray = new Array();
var prev;
for (var i=0;i<x.length;i++)
// Check for unique radio button group
if (x.elements[i].type == "radio")
// For the first element prev will be null, hence push it into array and set the prev var.
if (prev == null)
prev = x.elements[i].name;
ansArray.push(x.elements[i].name);
else
// We will only push the next radio element if its not identical to previous.
if (prev != x.elements[i].name)
prev = x.elements[i].name;
ansArray.push(x.elements[i].name);
alert(ansArray);
html:
<body>
<form name="QnA" action="" method='post' ">
<input type="radio" name="g1" value="ANSTYPE1"> good </input>
<input type="radio" name="g1" value="ANSTYPE2"> avg </input>
<input type="radio" name="g2" value="ANSTYPE3"> Type1 </input>
<input type="radio" name="g2" value="ANSTYPE2"> Type2 </input>
<input type="submit" value='SUBMIT' onClick="javascript:getUniqueRadios()"></input>
</form>
</body>
【讨论】:
【参考方案6】:或者对于那些寻找单线(简单且实用)与当前浏览器兼容的人:
let a = ["1", "1", "2", "3", "3", "1"];
let unique = a.filter((item, i, ar) => ar.indexOf(item) === i);
console.log(unique);
2021 年更新 我建议查看Charles Clayton's answer,由于最近对 JS 的更改,有更简洁的方法可以做到这一点。
2017 年 4 月 18 日更新
“Array.prototype.includes”现在似乎在最新版本的主流浏览器中得到了广泛支持 (compatibility)
2015 年 7 月 29 日更新:
浏览器计划支持标准化的 'Array.prototype.includes' 方法,虽然没有直接回答这个问题;通常是相关的。
用法:
["1", "1", "2", "3", "3", "1"].includes("2"); // true
Pollyfill (browser support, source from mozilla):
// https://tc39.github.io/ecma262/#sec-array.prototype.includes
if (!Array.prototype.includes)
Object.defineProperty(Array.prototype, 'includes',
value: function(searchElement, fromIndex)
// 1. Let O be ? ToObject(this value).
if (this == null)
throw new TypeError('"this" is null or not defined');
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// 3. If len is 0, return false.
if (len === 0)
return false;
// 4. Let n be ? ToInteger(fromIndex).
// (If fromIndex is undefined, this step produces the value 0.)
var n = fromIndex | 0;
// 5. If n ≥ 0, then
// a. Let k be n.
// 6. Else n < 0,
// a. Let k be len + n.
// b. If k < 0, let k be 0.
var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
// 7. Repeat, while k < len
while (k < len)
// a. Let elementK be the result of ? Get(O, ! ToString(k)).
// b. If SameValueZero(searchElement, elementK) is true, return true.
// c. Increase k by 1.
// NOTE: === provides the correct "SameValueZero" comparison needed here.
if (o[k] === searchElement)
return true;
k++;
// 8. Return false
return false;
);
【讨论】:
这几乎是 kennebec 的复制粘贴,但不可否认的是,将数组作为参数传递而不是使用闭包可能会提高性能。 - 必须说我没有连接点,只是扫描了一个衬里,看起来像一个大帖子,所以被跳过了,去寻找另一个来源并重新发布以供其他人快速找到。也就是说,你的权利;和肯尼贝克差不多。 很好——没有意识到过滤器作为参数在数组中发送,并且不想在外部对象上工作。这正是我所需要的——我的 javascript 版本(旧的 xerces 版本)暂时不会有新的好东西。 @GerardONeill 是的,在某些情况下它非常重要,例如,如果它在功能上被链接并且您想要访问尚未分配变量的数组,例如 .map(...).filter (...) 糟糕的答案。 O(N^2) 复杂度。不要使用这个。【参考方案7】:使用第二个数组的短而甜的解决方案;
var axes2=[1,4,5,2,3,1,2,3,4,5,1,3,4];
var distinct_axes2=[];
for(var i=0;i<axes2.length;i++)
var str=axes2[i];
if(distinct_axes2.indexOf(str)==-1)
distinct_axes2.push(str);
console.log("distinct_axes2 : "+distinct_axes2); // distinct_axes2 : 1,4,5,2,3
【讨论】:
短?甜美?您是否查看过最佳解决方案?【参考方案8】:您只需要 vanilla JS 即可使用 Array.some 和 Array.reduce 查找唯一性。使用 ES2015 语法,它只有 62 个字符。
a.reduce((c, v) => b.some(w => w === v) ? c : c.concat(v)), b)
IE9+ 和其他浏览器支持Array.some 和Array.reduce。只需将常规函数的粗箭头函数更改为支持不支持 ES2015 语法的浏览器即可。
var a = [1,2,3];
var b = [4,5,6];
// .reduce can return a subset or superset
var uniques = a.reduce(function(c, v)
// .some stops on the first time the function returns true
return (b.some(function(w) return w === v; ) ?
// if there's a match, return the array "c"
c :
// if there's no match, then add to the end and return the entire array
c.concat(v)),
// the second param in .reduce is the starting variable. This is will be "c" the first time it runs.
b);
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
【讨论】:
【参考方案9】:又想到了这个问题。这是我用更少的代码实现这一目标的方法。
var distinctMap = ;
var testArray = ['John', 'John', 'Jason', 'Jason'];
for (var i = 0; i < testArray.length; i++)
var value = testArray[i];
distinctMap[value] = '';
;
var unique_values = Object.keys(distinctMap);
console.log(unique_values);
【讨论】:
【参考方案10】:Array.prototype.unique = function ()
var dictionary = ;
var uniqueValues = [];
for (var i = 0; i < this.length; i++)
if (dictionary[this[i]] == undefined)
dictionary[this[i]] = i;
uniqueValues.push(this[i]);
return uniqueValues;
【讨论】:
【参考方案11】:如今,您可以使用 ES6 的 Set 数据类型将您的数组转换为唯一的 Set。然后,如果你需要使用数组方法,你可以把它转回一个数组:
var arr = ["a", "a", "b"];
var uniqueSet = new Set(arr); // "a", "b"
var uniqueArr = Array.from(uniqueSet); // ["a", "b"]
//Then continue to use array methods:
uniqueArr.join(", "); // "a, b"
【讨论】:
如果您正在使用转译器或处于支持它的环境中,则可以更简洁地执行相同的操作:var uniqueArr = [...new Set(arr)]; // ["a", "b"]
【参考方案12】:
单行,纯 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")
采用 ES5 语法
list = list.filter(function (x, i, a)
return a.indexOf(x) === i;
);
浏览器兼容性:IE9+
【讨论】:
不知道为什么这被否决了。起初它可能有点晦涩,也许被归类为“聪明”且不实用,但它是陈述性的、非破坏性的和简洁的,而大多数其他答案都缺乏。跨度> @Larry 这被否决了,因为在此之前几年提供了完全相同的答案。 @AlexOkrushko 很公平 - 因为它的格式方式而错过了这个答案 如果你把所有东西都放在一行上,一切都是单行的:-) 加强等号a.indexOf(x) === i
可能会很好,注意三个等号。【参考方案13】:
这是一个更清洁的 ES6 解决方案,我看到这里不包括在内。它使用Set 和spread operator:...
var a = [1, 1, 2];
[... new Set(a)]
返回[1, 2]
【讨论】:
太聪明了! 现在,THIS 是单行的! 在 Typescript 中,您必须使用Array.from(new Set(a))
,因为 Set 不能隐式转换为数组类型。请注意!【参考方案14】:
我已经在纯 JS 中尝试过这个问题。 我遵循以下步骤:1. 对给定数组进行排序,2. 遍历排序后的数组,3. 使用当前值验证上一个值和下一个值
// JS
var inpArr = [1, 5, 5, 4, 3, 3, 2, 2, 2,2, 100, 100, -1];
//sort the given array
inpArr.sort(function(a, b)
return a-b;
);
var finalArr = [];
//loop through the inpArr
for(var i=0; i<inpArr.length; i++)
//check previous and next value
if(inpArr[i-1]!=inpArr[i] && inpArr[i] != inpArr[i+1])
finalArr.push(inpArr[i]);
console.log(finalArr);
Demo
【讨论】:
【参考方案15】:上述大多数解决方案的运行时间复杂度都很高。
这是使用reduce
的解决方案,可以在 O(n) 时间内完成这项工作。
Array.prototype.unique = Array.prototype.unique || function()
var arr = [];
this.reduce(function (hash, num)
if(typeof hash[num] === 'undefined')
hash[num] = 1;
arr.push(num);
return hash;
, );
return arr;
var myArr = [3,1,2,3,3,3];
console.log(myArr.unique()); //[3,1,2];
注意:
这个解决方案不依赖于reduce。这个想法是创建一个对象映射并将唯一的对象映射到数组中。
【讨论】:
【参考方案16】:快速、紧凑、无嵌套循环,适用于任何对象,而不仅仅是字符串和数字,采用谓词,并且只需 5 行代码!!
function findUnique(arr, predicate)
var found = ;
arr.forEach(d =>
found[predicate(d)] = d;
);
return Object.keys(found).map(key => found[key]);
示例:按类型查找唯一项目:
var things = [
name: 'charm', type: 'quark',
name: 'strange', type: 'quark',
name: 'proton', type: 'boson',
];
var result = findUnique(things, d => d.type);
// [
// name: 'charm', type: 'quark',
// name: 'proton', type: 'boson'
// ]
如果您希望它找到第一个唯一项目而不是最后一个,请在此处添加 found.hasOwnPropery() 签入。
【讨论】:
【参考方案17】:使用 EcmaScript 2016,您可以像这样简单地做到这一点。
var arr = ["a", "a", "b"];
var uniqueArray = Array.from(new Set(arr)); // Unique Array ['a', 'b'];
集合总是唯一的,使用Array.from()
您可以将集合转换为数组。作为参考,请查看文档。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
【讨论】:
这是您应该使用的答案。indexOf()
答案很糟糕,因为它们是 O(N^2)。传播答案还可以,但不适用于大型数组。这是最好的方法。
@Timmmm:哪些传播答案不适用于大型数组?
其实我觉得我错了。我在想人们什么时候做Math.max(...foo)
之类的事情,但在数组中没关系。 indexOf
仍然是个糟糕的主意!【参考方案18】:
您可以输入具有重复元素的数组,下面的方法将返回具有唯一元素的数组。
function getUniqueArray(array)
var uniqueArray = [];
if (array.length > 0)
uniqueArray[0] = array[0];
for(var i = 0; i < array.length; i++)
var isExist = false;
for(var j = 0; j < uniqueArray.length; j++)
if(array[i] == uniqueArray[j])
isExist = true;
break;
else
isExist = false;
if(isExist == false)
uniqueArray[uniqueArray.length] = array[i];
return uniqueArray;
【讨论】:
这不应该被考虑,因为它增加了很多复杂性和可变性,这是不推荐的。请看其他答案。 如果使用 'let' 而不是 'var' 会怎样?这会解决可变性问题吗?【参考方案19】:如果您不需要太担心旧版浏览器,这正是 Set 的设计目的。
Set 对象允许您存储任何类型的唯一值,无论是 原始值或对象引用。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
const set1 = new Set([1, 2, 3, 4, 5, 1]);
// returns Set(5) 1, 2, 3, 4, 5
【讨论】:
【参考方案20】:这是一种具有可自定义equals
函数的方法,可用于基元以及自定义对象:
Array.prototype.pushUnique = function(element, equalsPredicate = (l, r) => l == r)
let res = !this.find(item => equalsPredicate(item, element))
if(res)
this.push(element)
return res
用法:
//with custom equals for objects
myArrayWithObjects.pushUnique(myObject, (left, right) => left.id == right.id)
//with default equals for primitives
myArrayWithPrimitives.pushUnique(somePrimitive)
【讨论】:
以上是关于如何获取数组中的唯一值[重复]的主要内容,如果未能解决你的问题,请参考以下文章