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

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

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

【问题讨论】:

  • stackoverflow.com/questions/5381621/… - 准确描述你想要的我的想法?
  • 您愿意使用underscore.js 库吗?
  • java hashmap 与 javascript 对象基本相同。语法是 {"key": "value", "key2": "value2"}
  • JavaScript/TypeScript 集合 API 与 Scala 相比很糟糕,list.toSet
  • 这取决于数组中的内容、您如何定义“唯一性”以及您的数据有多大。如果它们是对象,则代码不同于数字或字符串,并且如果数据很大,您将需要线性解决方案而不是二次解决方案。请提供更多详细信息。

标签: javascript jquery


【解决方案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 解决方案,我看到这里不包括在内。它使用Setspread 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)
                    

                    【讨论】:

                      猜你喜欢
                      • 2023-03-21
                      • 1970-01-01
                      • 2012-11-09
                      • 1970-01-01
                      • 1970-01-01
                      • 2022-12-22
                      • 2013-05-29
                      • 2013-10-04
                      相关资源
                      最近更新 更多