【问题标题】:Get all unique values in a JavaScript array (remove duplicates)获取 JavaScript 数组中的所有唯一值(删除重复项)
【发布时间】:2010-12-29 22:47:00
【问题描述】:

我有一组数字,我需要确保它们是唯一的。我在互联网上找到了下面的代码 sn-p 并且它工作得很好,直到数组中的零。我在 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;
}

重复问题的更多答案:

类似问题:

【问题讨论】:

  • @hippietrail 那个较老的问题是关于只查找和返回重复项(我也很困惑!)。我的问题更多是关于为什么当数组中有一个零时这个函数会失败。
  • 对于未来的读者,当您开始发现您必须始终通过算法修改数据结构的内容时,(对它们进行排序、删除重复元素等)或搜索其中的元素迭代,可以安全地假设您首先使用了错误的数据结构并开始使用更适合手头任务的数据结构(在这种情况下是哈希集而不是数组)。
  • 很久以前我从其他地方复制了代码...但看起来很简单:o = object, a = array, i = indexe = 嗯,有些东西:P
  • 只是想指出,很多人建议使用 JavaScript Set 作为解决方案,请谨慎使用,因为 Internet Explorer 不支持它。如果您必须支持 IE,请使用 polyfill。

标签: javascript unique arrays


【解决方案1】:

使用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));

【讨论】:

  • 虽然这段代码 sn-p 可以解决问题,但它没有解释为什么或如何回答这个问题。请include an explanation for your code,因为这确实有助于提高您的帖子质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。
【解决方案2】:

使用 ES6 新建集

var array = [3,7,5,3,2,5,2,7];
var unique_array = [...new Set(array)];
console.log(unique_array);    // output = [3,7,5,2]

使用 For 循环

var array = [3,7,5,3,2,5,2,7];

for(var i=0;i<array.length;i++)
{
for(var j=i+1;j<array.length;j++)
{
if(array[i]===array[j])
{
array.splice(j,1);
}
}
}
console.log(array); // output = [3,7,5,2]

【讨论】:

    【解决方案3】:

    let ar = [1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 2, 1];
    let unique = ar.filter((value, index) => {
            return ar.indexOf(value) == index;
          });
    console.log(unique);

    【讨论】:

      【解决方案4】:

      并不是对原始问题的直接字面回答,因为我更喜欢一开始就不在数组中包含重复值。所以这是我的 UniqueArray

      class UniqueArray extends Array {
          constructor(...args) {
              super(...new Set(args));
          }
          push(...args) {
              for (const a of args) if (!this.includes(a)) super.push(a);
              return this.length;
          }
          unshift(...args) {
              for (const a of args.reverse()) if (!this.includes(a)) super.unshift(a);
              return this.length;
          }
          concat(...args) {
              var r = new UniqueArray(...this);
              for (const a of args) r.push(...a);
              return r;
          }
      }
      
      > a = new UniqueArray(1,2,3,1,2,4,5,1)
      UniqueArray(5) [ 1, 2, 3, 4, 5 ]
      > a.push(1,4,6)
      6
      > a
      UniqueArray(6) [ 1, 2, 3, 4, 5, 6 ]
      > a.unshift(1)
      6
      > a
      UniqueArray(6) [ 1, 2, 3, 4, 5, 6 ]
      > a.unshift(0)
      7
      > a
      UniqueArray(7) [
        0, 1, 2, 3,
        4, 5, 6
      ]
      > a.concat(2,3,7)
      UniqueArray(8) [
        0, 1, 2, 3,
        4, 5, 6, 7
      ]
      

      【讨论】:

        【解决方案5】:

        使用 JavaScript 1.6 / ECMAScript 5 您可以通过以下方式使用数组的原生 filter 方法来获取具有唯一值的数组:

        function onlyUnique(value, index, self) {
          return self.indexOf(value) === index;
        }
        
        // usage example:
        var a = ['a', 1, 'a', 2, '1'];
        var unique = a.filter(onlyUnique);
        
        console.log(unique); // ['a', 1, 2, '1']

        本机方法filter 将遍历数组并只留下那些通过给定回调函数onlyUnique 的条目。

        onlyUnique 检查给定值是否是第一个出现的值。如果不是,则必须是重复的,不会被复制。

        此解决方案无需任何额外的库(如 jQuery 或prototype.js)即可工作。

        它也适用于具有混合值类型的数组。

        对于不支持本机方法 filterindexOf 的旧浏览器 (filter 和 indexOf 的解决方法。

        如果您想保留最后一次出现的值,只需将 indexOf 替换为 lastIndexOf

        在 ES6 中,这可以缩短为:

        // usage example:
        var myArray = ['a', 1, 'a', 2, '1'];
        var unique = myArray.filter((v, i, a) => a.indexOf(v) === i);
        
        console.log(unique); // unique is ['a', 1, 2, '1']

        感谢Camilo Martin 的评论提示。

        ES6 有一个原生对象Set 来存储唯一值。要获取具有唯一值的数组,您现在可以这样做:

        var myArray = ['a', 1, 'a', 2, '1'];
        
        let unique = [...new Set(myArray)];
        
        console.log(unique); // unique is ['a', 1, 2, '1']

        Set 的构造函数接受一个可迭代对象,如数组,扩展运算符... 将集合转换回数组。感谢Lukas Liese 的评论提示。

        【讨论】:

        • 不幸的是,此解决方案的运行速度会慢得多。您循环了两次,一次使用过滤器,一次使用索引
        • 在现代 JS 中:.filter((v,i,a)=&gt;a.indexOf(v)==i)(粗箭头符号)。
        • let unique_values = [...new Set(random_array)]; developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
        • 性能明智,设置是要走的路medium.com/@jakubsynowiec/… IMO 唯一的缺点是它不会使程序员的意图明确。我的意思是,一旦你知道它的作用,你就明白了,但它远不如 myArray.unique() 这样清晰
        • 为什么需要[... ... ]new Set(myArray) 本身似乎具有相同的行为
        【解决方案6】:

        使用iter-ops 库的现代方法,可扩展、快速、高效且易于阅读:

        import {pipe, distinct} from 'iter-ops';
        
        const input = [1, 1, 2, 2, 2, 3]; // our data
        
        const i = pipe(input, distinct()); // distinct iterable
        
        console.log([...i]); //=> [1, 2, 3]
        

        如果您的输入是一个对象数组,您只需为distinct 运算符提供一个键选择器。

        【讨论】:

          【解决方案7】:

          这是使用比较器的另一种方法(我更关心代码是否简洁而不是性能):

          const list = [
              {name: "Meier"},
              {name: "Hans"},
              {name: "Meier"},
          ]
          const compare = (a, b) => a.name.localeCompare(b.name);
          const uniqueNames = list.makeUnique(compare);
          uniqueNames.pushIfAbsent({name: "Hans"}, compare);
          

          原型声明:

          declare global {
              interface Array<T>  {
                  pushIfAbsent(item: T, compare:(a:T, b:T)=>number): number;
              }
              interface Array<T>  {
                  makeUnique(compare:(a:T, b:T)=>number): Array<T>;
              }
          }
          Array.prototype.pushIfAbsent = function <T>(this:T[], item:T, compare:(a:T, b:T)=>number) {
              if (!this.find(existing => compare(existing, item)===0)) {
                  return this.push(item)
              } else {
                  return this.length;
              }
          }
          Array.prototype.makeUnique = function <T>(this:T[], compare:(a:T, b:T)=>number) {
              return this.filter((existing, index, self) => self.findIndex(item => compare(existing, item) == 0) == index);
          }
          

          【讨论】:

            【解决方案8】:

            如前所述,[...new Set(values)] 是最好的选择,如果您可以使用的话。

            否则,这是一个不会为每个索引迭代数组的单行:

            values.sort().filter((val, index, arr) => index === 0 ? true : val !== arr[index - 1]);
            

            这只是将每个值与之前的值进行比较。结果将被排序。

            示例:

            let values = [ 1, 2, 3, 3, 4, 5, 5, 5, 4, 4, 4, 5, 1, 1, 1, 3, 3 ];
            let unique = values.sort().filter((val, index, arr) => index === 0 ? true : val !== arr[index - 1]);
            console.log(unique);

            【讨论】:

            • 行中有多个相同值时不起作用
            • 添加了代码 sn-p,似乎可以工作。
            【解决方案9】:

            如果你想删除重复项,返回整个对象并想使用 ES6 Set 和 Map 语法,并且只运行一个循环,你可以试试这个,以获得唯一的 id:

            const collection = [{id:3, name: "A"}, {id:3, name: "B"}, {id:4, name: "C"}, {id:5, name: "D"}]
            
            function returnUnique(itemsCollection){
              const itemsMap = new Map();
            
              itemsCollection.forEach(item => {
                if(itemsMap.size === 0){
                  itemsMap.set(item.id, item)       
                }else if(!itemsMap.has(item.id)){
                  itemsMap.set(item.id, item)
                }
              });
              
                return [...new Set(itemsMap.values())];
             }
            
            console.log(returnUnique(collection));

            【讨论】:

              【解决方案10】:

                var myArray = ["a",2, "a", 2, "b", "1"];
                const uniques = [];
                myArray.forEach((t) => !uniques.includes(t) && uniques.push(t));
                console.log(uniques);

              【讨论】:

                【解决方案11】:

                您可以简单地使用内置函数Array.prototype.filter()Array.prototype.indexOf()

                array.filter((x, y) =&gt; 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);
                
                console.log(newarr);

                【讨论】:

                  【解决方案12】:

                  如果您使用 Prototype 框架,则无需执行“for”循环,您可以像这样使用http://prototypejs.org/doc/latest/language/Array/prototype/uniq/

                  var a = Array.uniq();  
                  

                  这将产生一个没有重复的重复数组。我遇到了您搜索计算不同数组记录的方法的问题,所以在uniq() 之后我使用了size(),这是我的简单结果。 p.s.对不起,如果我打错了东西

                  编辑:如果您想转义未定义的记录,您可能需要在之前添加compact(),如下所示:

                  var a = Array.compact().uniq();  
                  

                  【讨论】:

                  • 因为我找到了更好的答案,所以我认为主题是针对所有人的,而不仅仅是针对提出问题的人
                  • 感谢时间机器,但 iirc 大约在 15 年前 JS 社区进行了辩论,结果是 - 不要扩展原型导致副作用并导致您以这种方式污染所有 JS 数组。
                  【解决方案13】:

                  最简单的 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

                  【讨论】:

                  • 在 Linux 上,Chrome 55.0.2883 更喜欢您的 arr.unique() 而 swilliams 的 arrclone2.sortFilter() 最慢(慢 78%)。然而,Firefox 51.0.0(有很多插件)的 swilliams 最快(但仍然比任何其他 Chrome 结果慢),而 mottie 的 jQuery $.grep(arr, jqFilter) 最慢(慢 46%)。你的 arr.uniq() 慢了 30%。我将每个测试运行了两次并得到了一致的结果。 Rafael 的arr.getUnique() 在两个浏览器中均获得第二名。
                  • jsPerf 目前是 buggy,所以我对此测试的编辑并没有提交所有内容,但它确实导致添加了两个测试:Cocco 的 toUnique() 在两个浏览器上都击败了 Vamsi 的 ES6 list.filter() ,在 FF 上击败了 swilliams 的 sortFilter()(sortFilter 慢了 16%),在 Chrome 上击败了 #3 的排序测试(慢了 2%)。
                  • 啊,我没有发现这些测试非常小而且并不重要。对已接受答案describes that problem 的评论并在revision 中对测试进行更正,其中 Rafael 的代码很容易最快,而 Joetje50 的 arr.unique 代码慢 98%。如this comment 所述,我还进行了另一次修订。
                  • 嗯,实际上您在unique 函数中实现的算法具有 O(n^2) 复杂度,而 getUnique 中的算法是 O(n)。第一个在小型数据集上可能更快,但你怎么能与数学争论:) 如果你在一组 1e5 个独特项目上运行它,你可以确保后者更快
                  • 也被lodash.uniq 用于input_array.length &lt; 200,否则使用[...new Set(input_array)] 方法。表示为reducer:input_array.reduce((c, v) =&gt; {if (!c.includes(v)) c.push(v); return c;}, [])
                  【解决方案14】:

                  我会对数组进行排序,然后所有重复项都是邻居。 然后遍历数组并消除所有重复项。

                  function getUniques(array) {
                    var l = array.length
                    if(l > 1) {
                      // get a cloned copy and sort it
                      array = [...array].sort();
                      var i = 1, j = 0;
                      while(i < l) {
                        if(array[i] != array[j]) {
                          array[++j] = array[i];
                        }
                        i++;
                      }
                      array.length = j + 1;
                    }
                    return array;
                  }
                  

                  【讨论】:

                    【解决方案15】:

                    使用 mongoose 我有一组 ObjectIds 可以使用。

                    我有一个要使用的对象 ID 的数组/列表,首先需要将其设置为字符串,然后在唯一集之后修改回对象 ID。

                    var mongoose = require('mongoose')
                    
                    var ids = [ObjectId("1"), ObjectId("2"), ObjectId("3")]
                    
                    var toStringIds = ids.map(e => '' + e)
                    let uniqueIds = [...new Set(toStringIds)]
                    uniqueIds = uniqueIds.map(b => mongoose.Types.ObjectId(b))
                    
                    
                    console.log("uniqueIds :", uniqueIds)

                    【讨论】:

                      【解决方案16】:

                      就我而言,这是最简单的解决方案

                      // A way to check if the arrays are equal
                      const a = ['A', 'B', 'C'].sort().toString()
                      const b = ['A', 'C', 'B'].sort().toString()
                      
                      console.log(a === b); // true
                      
                      
                      // Test Case
                      const data = [
                        { group: 'A', name: 'SD' },
                        { group: 'B', name: 'FI' },
                        { group: 'A', name: 'SD' },
                        { group: 'B', name: 'CO' }
                      ];
                      
                      // Return a new Array without dublocates
                      function unique(data) {
                        return data.reduce(function (accumulator, currentValue) {
                          // Convert to string in order to check if they are the same value.
                          const currentKeys = Object.keys(currentValue).sort().toString();
                          const currentValues = Object.values(currentValue).sort().toString();
                      
                          let hasObject = false
                          
                          for (const obj of accumulator) {
                            // Convert keys and values into strings so we can
                            // see if they are equal with the currentValue
                            const keys = Object.keys(obj).sort().toString();
                            const values = Object.values(obj).sort().toString();
                            // Check if keys and values are equal
                            if (keys === currentKeys && values === currentValues) {
                              hasObject = true
                            }
                          }
                      
                          // Push the object if it does not exist already.
                          if (!hasObject) {
                            accumulator.push(currentValue)
                          }
                      
                          return accumulator
                        }, []);
                      }
                      
                      // Run Test Case
                      console.log(unique(data)); // [ { group: 'A', name: 'SD' }, { group: 'B', name: 'FI' }, { group: 'B', name: 'CO' } ]

                      【讨论】:

                        【解决方案17】:

                        任务是从由任意类型(原始和非原始)组成的数组中获取唯一数组。

                        基于使用new Set(...) 的方法并不新鲜。在这里,JSON.stringify(...)JSON.parse(...)[].map 方法利用了它。优点是通用性(适用于任何类型的数组)、简短的 ES6 表示法以及对于这种情况可能性能

                        const dedupExample = [
                            { a: 1 },
                            { a: 1 },
                            [ 1, 2 ],
                            [ 1, 2 ],
                            1,
                            1,
                            '1',
                            '1'
                        ]
                        
                        const getUniqArrDeep = arr => {
                            const arrStr = arr.map(item => JSON.stringify(item))
                            return [...new Set(arrStr)]
                                .map(item => JSON.parse(item))
                        }
                        
                        console.info(getUniqArrDeep(dedupExample))
                           /* [ {a: 1}, [1, 2], 1, '1' ] */

                        【讨论】:

                          【解决方案18】:

                          要删除重复项,可能有两种情况。 首先,所有的数据都不是对象,其次,所有的数据都是对象。

                          如果所有数据都是任何一种原始数据类型,如 int、float、string 等,那么你可以按照这个

                          const uniqueArray = [...new Set(oldArray)]
                          

                          但是假设你的数组包含像下面这样的 JS 对象

                          {
                              id: 1,
                              name: 'rony',
                              email: 'rony@example.com'
                          }
                          

                          然后获取所有可以关注的唯一对象

                          let uniqueIds = [];
                          const uniqueUsers = oldArray.filter(item => {
                              if(uniqueIds.includes(item.id)){
                                  return false;
                              }else{
                                  uniqueIds.push(item.id);
                                  return true;
                              }
                          })
                          

                          您也可以使用此方法制作任何类型的数组以使其唯一。只需将跟踪密钥保留在 uniqueIds 数组中即可。

                          【讨论】:

                            【解决方案19】:

                            这是一个几乎是 O(n) 的单行代码,保留第一个元素,并且您可以将唯一的字段分开。

                            这是函数式编程中非常常见的技术 - 您使用 reduce 构建一个返回的数组。由于我们像这样构建数组,因此我们保证得到稳定的排序,这与[...new Set(array)] 方法不同。我们仍然使用Set 来确保我们没有重复,因此我们的累加器包含Set 和我们正在构建的数组。

                            const removeDuplicates = (arr) =>
                              arr.reduce(
                                ([set, acc], item) => set.has(item) ? [set, acc] : [set.add(item), (acc.push(item), acc)],
                                [new Set(), []]
                              )[1]

                            以上内容适用于简单值,但不适用于对象,类似于[...new Set(array)] 的分解方式。如果项目是包含 id 属性的对象,您会这样做:

                            const removeDuplicates = (arr) =>
                              arr.reduce(
                                ([set, acc], item) => set.has(item.id) ? [set, acc] : [set.add(item.id), (acc.push(item), acc)],
                                [new Set(), []]
                              )[1]

                            【讨论】:

                              【解决方案20】:

                              这里的许多答案可能对初学者没有用处。如果对数组进行重复数据删除很困难,他们真的会知道原型链,甚至 jQuery 吗?

                              在现代浏览器中,一个干净且简单的解决方案是将数据存储在 Set 中,它被设计为唯一值列表。

                              const cars = ['Volvo', 'Jeep', 'Volvo', 'Lincoln', 'Lincoln', 'Ford'];
                              const uniqueCars = Array.from(new Set(cars));
                              console.log(uniqueCars);

                              Array.from 可用于将 Set 转换回数组,这样您就可以轻松访问数组拥有的所有很棒的方法(功能)。也有other ways 做同样的事情。但是你可能根本不需要Array.from,因为集合有很多有用的功能,比如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"]
                              • @Jason 我可能会创建一个 Map 来存储以前看到的项目和一个数组来存储重复的项目。然后循环遍历cars 数组并检查 Map 是否有当前项,如果有则将其推送到重复数组,如果没有则将其添加到 Map。如果您创建一个新问题,我很乐意为您创建一个代码示例,我们可以在那里继续讨论。
                              【解决方案21】:

                              魔法

                              a.filter(e=>!(t[e]=e in t)) 
                              

                              O(n) 性能(比new Set 快);我们假设您的数组位于at={} 中。解释here (+Jeppe impr.)

                              let t, unique= a=> ( t={}, a.filter(e=>!(t[e]=e in t)) );
                              
                              // "stand-alone" version working with global t:
                              // a1.filter((t={},e=>!(t[e]=e in t)));
                              
                              // Test data
                              let a1 = [5,6,0,4,9,2,3,5,0,3,4,1,5,4,9];
                              let a2 = [[2, 17], [2, 17], [2, 17], [1, 12], [5, 9], [1, 12], [6, 2], [1, 12]];
                              let a3 = ['Mike', 'Adam','Matt', 'Nancy', 'Adam', 'Jenny', 'Nancy', 'Carl'];
                              
                              // Results
                              console.log(JSON.stringify( unique(a1) ))
                              console.log(JSON.stringify( unique(a2) ))
                              console.log(JSON.stringify( unique(a3) ))

                              【讨论】:

                              • 这看起来太酷了,如果没有可靠的解释,我就认为当我运行它时你会挖比特币
                              • 我的意思是你应该用一些解释来扩展你的答案并评论它的解构。不要指望人们会找到这样有用的答案。 (虽然它看起来很酷,但可能有效)
                              • 不是魔术,但很像“Set”答案,在字典中使用 O(1) 键查找。你需要增加计数器吗? “e=>!(t[e]=e in t)”怎么样。不错的答案。
                              • @Jeppe 当我运行您的改进时,我会体验到aha effect(之前我不知道我可以在for 循环之外的其他构造之外使用in 运算符:P)-谢谢你 - 我很感激,并且会给你其他好的答案+2。
                              【解决方案22】:

                              你根本不需要 .indexOf();你可以这样做 O(n):

                              function SelectDistinct(array) {
                                  const seenIt = new Set();
                              
                                  return array.filter(function (val) {
                                      if (seenIt.has(val)) { 
                                          return false;
                                      }
                              
                                      seenIt.add(val);
                              
                                      return true;
                                  });
                              }
                              
                              var hasDuplicates = [1,2,3,4,5,5,6,7,7];
                              console.log(SelectDistinct(hasDuplicates)) //[1,2,3,4,5,6,7]
                              

                              如果你不想使用 .filter():

                              function SelectDistinct(array) {
                                  const seenIt = new Set();
                                  const distinct = [];
                              
                                  for (let i = 0; i < array.length; i++) {
                                      const value = array[i];
                              
                                      if (!seenIt.has(value)) {
                                          seenIt.add(value);
                                          distinct.push(value);
                                      }
                                  }
                                  
                                  return distinct; 
                                  /* you could also drop the 'distinct' array and return 'Array.from(seenIt)', which converts the set object to an array */
                              }
                              

                              【讨论】:

                                【解决方案23】:

                                在这里查看所有 90+ 个答案后,我发现还有一个空间:

                                Array.includes 有一个非常方便的第二个参数:"fromIndex",因此通过使用它,filter 回调方法的每次迭代都会搜索 数组 ,从[current index] + 1 开始,保证不会在查找中包含当前过滤的项目,并且还可以节省时间。

                                //                ?              ? ?
                                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包含在数组的其余部分中,它将被过滤掉,因为前导感叹号否定过滤规则。

                                【讨论】:

                                • 不幸的是,这会保留每个值的最后一个实例,而不是第一个。 (这可能没问题,但我认为保留第一个通常是预期的)
                                【解决方案24】:

                                ES6/ES2015 的更新答案:使用Setthe 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' } }
                                • 如果有人想知道,这也适用于字符串,例如[...new Set(["apple","apple","orange"])] 结果为 ['apple', 'orange'] 。太好了!
                                【解决方案25】:

                                使用 One Liner 在对象数组中查找唯一性

                                const uniqueBy = (x,f)=>Object.values(x.reduce((a,b)=>((a[f(b)]=b),a),{}));
                                // f -> should must return string because it will be use as key
                                
                                const data = [
                                  { comment: "abc", forItem: 1, inModule: 1 },
                                  { comment: "abc", forItem: 1, inModule: 1 },
                                  { comment: "xyz", forItem: 1, inModule: 2 },
                                  { comment: "xyz", forItem: 1, inModule: 2 },
                                ];
                                
                                uniqueBy(data, (x) => x.forItem +'-'+ x.inModule); // find unique by item with module
                                // output
                                // [
                                //   { comment: "abc", forItem: 1, inModule: 1 },
                                //   { comment: "xyz", forItem: 1, inModule: 2 },
                                // ];
                                
                                // can also use for strings and number or other primitive values
                                
                                uniqueBy([1, 2, 2, 1], (v) => v); // [1, 2]
                                uniqueBy(["a", "b", "a"], (v) => v); // ['a', 'b']
                                
                                uniqueBy(
                                  [
                                    { id: 1, name: "abc" },
                                    { id: 2, name: "xyz" },
                                    { id: 1, name: "abc" },
                                  ],
                                  (v) => v.id
                                );
                                // output
                                // [
                                //   { id: 1, name: "abc" },
                                //   { id: 2, name: "xyz" },
                                // ];
                                

                                【讨论】:

                                • 使用也可以使用 uniqBy 代替 uniqueBy
                                【解决方案26】:

                                如果您只想获取唯一元素并删除重复一次的元素,您可以这样做:

                                let array = [2, 3, 4, 1, 2, 8, 1, 1, 2, 9, 3, 5, 3, 4, 8, 4];
                                
                                function removeDuplicates(inputArray) {
                                  let output = [];
                                  let countObject = {};
                                
                                  for (value of array) {
                                    countObject[value] = (countObject[value] || 0) + 1;
                                  }
                                
                                  for (key in countObject) {
                                    if (countObject[key] === 1) {
                                      output.push(key);
                                    }
                                  }
                                
                                  return output;
                                }
                                
                                console.log(removeDuplicates(array));

                                【讨论】:

                                  【解决方案27】:

                                  在我的解决方案中,我在过滤之前对数据进行排序:

                                  const uniqSortedArray = dataArray.sort().filter((v, idx, t) => idx==0 || v != t[idx-1]); 
                                  

                                  【讨论】:

                                    【解决方案28】:

                                    最简单的方法是将值转换为字符串以过滤嵌套对象值。

                                    const uniq = (arg = []) => {
                                      const stringifyedArg = arg.map(value => JSON.stringify(value))
                                      return arg.filter((value, index, self) => {
                                        if (typeof value === 'object')
                                          return stringifyedArg.indexOf(JSON.stringify(value)) === index
                                        return self.indexOf(value) === index
                                      })
                                    }
                                    
                                        console.log(uniq([21, 'twenty one', 21])) // [21, 'twenty one']
                                        console.log(uniq([{ a: 21 }, { a: 'twenty one' }, { a: 21 }])) // [{a: 21}, {a: 'twenty one'}]
                                    

                                    【讨论】:

                                      【解决方案29】:

                                      我将所有答案分成 4 种可能的解决方案:

                                      1. 使用对象{ } 防止重复
                                      2. 使用辅助数组[ ]
                                      3. 使用filter + indexOf
                                      4. 奖金! ES6 Sets 方法。

                                      以下是答案中的示例代码:

                                      使用对象{ } 防止重复

                                      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;
                                      }
                                      

                                      使用filter + 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 表格中不可用,因此我无法对其进行测试。

                                      以下是测试结果:

                                      我希望看到使用对象{ } 的代码会获胜,因为它使用哈希。所以我很高兴测试显示了该算法在 Chrome 和 IE 中的最佳结果。感谢@rab the code

                                      2020 年更新

                                      支持 Google 脚本的 ES6 引擎。现在我用Sets 测试了最后一个代码,它看起来比对象方法更快。

                                      【讨论】:

                                      • Makrov,所以uniqueItems = [...new Set(items)] 似乎是所有方法中最快和最简洁的?
                                      【解决方案30】:

                                      对于具有一些唯一 id 的基于对象的数组,我有一个简单的解决方案,您可以通过它对线性复杂度进行排序

                                      function getUniqueArr(arr){
                                          const mapObj = {};
                                          arr.forEach(a => { 
                                             mapObj[a.id] = a
                                          })
                                          return Object.values(mapObj);
                                      }
                                      

                                      【讨论】:

                                        猜你喜欢
                                        相关资源
                                        最近更新 更多