如何获取数组中的唯一值[重复]

Posted

技术标签:

【中文标题】如何获取数组中的唯一值[重复]【英文标题】:How to get unique values in an array [duplicate] 【发布时间】:2012-06-30 02:21:14 【问题描述】:

如何获取数组中唯一值的列表?我总是必须使用第二个数组还是在 javascript 中有类似于 java 的 hashmap 的东西?

我将只使用 JavaScriptjQuery。不能使用其他库。

【问题讨论】:

***.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,我只是喜欢扩展 prototypes。但是,我现在明白你的意思了。无论如何我都会把它留在这里。【参考方案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 的答案,我不妨提供一个不使用库的示例。这需要两个新的原型函数,containsunique

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) =&gt; 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)

【讨论】:

以上是关于如何获取数组中的唯一值[重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何从包含 JavaScript 中重复项的数组中获取唯一值数组? [复制]

获取嵌套数组/对象的数组中的所有唯一值(删除重复项)

从数组数组中获取唯一值[重复]

从jquery中的数组中获取唯一元素和元素的数量[重复]

如何获取字典中的所有不同值[重复]

如何从 Python 中的列表中获取具有相应出现次数的唯一值?