获取JavaScript数组中的所有唯一值(删除重复项)


1481

我需要确定一个唯一的数字数组。我在互联网上找到了下面的代码片段,并且在数组中包含零之前,它都可以正常工作。我在Stack Overflow上的这里找到了另一个脚本,看起来几乎完全一样,但是它不会失败。

因此,为了帮助我学习,有人可以帮助我确定原型脚本出了什么问题吗?

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

来自重复问题的更多答案:

类似的问题:


3
@hippietrail这个较旧的问题是关于仅查找和返回重复项(我也感到困惑!)。我的问题更多是关于为什么数组中包含零时此函数失败的原因。
Mottie 2014年

您可能还想使问题标题变得不太模糊。
hippietrail

对于将来的读者,当开始发现您必须一直在算法上修改数据结构的内容(对它们进行排序,删除重复的元素等)或在每次迭代中搜索其中的元素时,可以肯定地假设您是首先使用错误的数据结构,并开始使用更适合手头任务的数据结构(在这种情况下,使用哈希集而不是数组)。
nurettin

我是从很久以前的其他地方复制代码的……但是似乎很简单:o= objecta= arrayi= indexe= umm,有些东西:P
Mottie 2015年

Answers:


2656

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

function onlyUnique(value, index, self) { 
    return self.indexOf(value) === index;
}

// usage example:
var a = ['a', 1, 'a', 2, '1'];
var unique = a.filter( onlyUnique ); // returns ['a', 1, 2, '1']

本机方法filter将遍历数组,仅保留那些通过给定回调函数的条目onlyUnique

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

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

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

对于旧的浏览器(<ie9),它们不支持本机方法filterindexOf您可以在MDN文档中找到关于filterindexOf的解决方法

如果要保留最后一次出现的值,请简单地替换indexOflastIndexOf

使用ES6可以简化为:

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

// unique is ['a', 1, 2, '1']

感谢Camilo Martin的评论提示。

ES6有一个本地对象Set来存储唯一值。要获得具有唯一值的数组,您现在可以执行以下操作:

var myArray = ['a', 1, 'a', 2, '1'];

let unique = [...new Set(myArray)]; 

// unique is ['a', 1, 2, '1']

的构造函数Set采用可迭代对象(例如Array),并且散布运算符...将集合转换回Array。感谢Lukas Liese的评论提示。


53
不幸的是,该解决方案将运行慢得多。您要循环两次,一次使用过滤器,一次使用索引
Jack Franzen 2013年

18
@JackFranzen比什么还慢?拉斐尔的解决方案?Rafaels解决方案不适用于混合类型的阵列。对于我的例子,['a', 1, 'a', 2, '1']您会得到的['a', 1, 2]。但这不是我所期望的。顺便说一句,慢得多是相对的。
TLindig

25
在现代JS中:(.filter((v,i,a)=>a.indexOf(v)==i)胖箭头符号)。
卡米洛·马丁

162
let unique_values = [...new Set(random_array)]; developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
卢卡斯Liesis

5
有关更详细的答案(包括许多可能性)(例如,首先进行排序以及处理各种数据类型),请参见stackoverflow.com/a/9229821/368896
Dan Nissenbaum

873

ES6 / ES2015的更新答案:使用Set,单行解决方案是:

var items = [4,5,4,6,3,4,5,2,23,1,4,4,4]
var uniqueItems = Array.from(new Set(items))

哪个返回

[4, 5, 6, 3, 2, 23, 1]

正如le_m所建议的那样,也可以使用传播算子来缩短它,例如

var uniqueItems = [...new Set(items)]

10
注意,内部数组不起作用Array.from(new Set([[1,2],[1,2],[1,2,3]]))
Alexander Goncharov

2
此解决方案的性能相比myArray.filter((v, i, a) => a.indexOf(v) === i);
Simoyw

42
请注意,如果使用Set和添加对象而不是原始值,它将包含对对象的唯一引用。因此,set sin let s = new Set([{Foo:"Bar"}, {Foo:"Bar"}]);将返回this:Set { { Foo: 'Bar' }, { Foo: 'Bar' } }这是一个Set具有唯一对象引用的对象,这些对象包含相同的值。如果您编写let o = {Foo:"Bar"};并使用两个引用创建一个集合,如下所示:let s2 = new Set([o,o]);,则s2将为Set { { Foo: 'Bar' } }
mortb

2
即使存在polyfill.io层,这两个选项在IE10上都存在问题
Thymine

2
新的测试用例jsperf.com/array-filter-unique-vs-new-set/1看起来像new Set奖杯
shuk

167

我意识到这个问题已经有30多个答案。但是我首先阅读了所有现有答案,并进行了自己的研究。

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

  1. 使用新的ES6功能: [...new Set( [1, 1, 2] )];
  2. 使用对象{ }防止重复
  3. 使用辅助数组 [ ]
  4. 采用 filter + indexOf

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

使用新的ES6功能: [...new Set( [1, 1, 2] )];

function uniqueArray0(array) {
  var result = Array.from(new Set(array));
  return result    
}

使用对象{ }防止重复

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

我想知道哪一个更快。我已经制作了示例Google表格来测试功能。注意:ECMA 6在Google表格中不可用,因此我无法对其进行测试。

测试结果如下: 在此处输入图片说明

我希望看到使用object的代码{ }将获胜,因为它使用哈希。因此,我很高兴测试在Chrome和IE中针对该算法显示了最佳结果。感谢@rab提供的代码


在超过100.000个项目的数组上,“ filter + indexOf”选项非常慢。我不得不使用“对象映射”方法,但是它破坏了原始排序。
liberborn

更高的数字更慢,不是更快?
fletchsod

3
@ fletchsod,数字是运行代码的时间(以毫秒为单位)。
Max Makhrov

1
数不胜数!Google Apps脚本在其服务器上运行,而不是在客户端的浏览器上运行-因此,Chrome / IE(或几乎所有浏览器)的性能都将相同!
Jay Dadhania

1
虽然有可能到(无论是通过从运行谷歌脚本客户端JS google.script.hostgoogle.script.run -肯定不是哪个),答案明确指出,“ECMA 6是不是在谷歌表avaliable” -因此,我们可以放心地认为,发布者为此使用了服务器端Google脚本-因此答案显示的是Google Server的性能,而不是浏览器的性能!
Jay Dadhania

134

您还可以使用underscore.js

console.log(_.uniq([1, 2, 1, 3, 1, 4]));
<script src="http://underscorejs.org/underscore-min.js"></script>

它将返回:

[1, 2, 3, 4]

22
请这个人。不要将任何东西拖到Array原型上。请。
Jacob Dalton

5
@JacobDalton-这不是扩展Array原型。它在_对象中命名空间。
superluminary

24
@superluminary我知道这就是为什么我说请这样做的原因。公认的解决方案建议修改Array原型。不要那样做。
Jacob Dalton

20
@JacobDalton请不要这样做。无需为可能需要完成的小工作添加额外的库array = [...new Set(array)]

3
这打败了使用ES功能并添加更多库的目的,这只会使网站更加肿。
fletchsod

68

一线纯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 +


13
@Spets它的成本是二次方,所以这不是最佳答案
Oriol

@Spets像所有唯一性/非重复性一样……是的,要聪明,使用基数优先排序,在大多数情况下要比0(n2)快速搜索慢。
doker

谢谢你们!当我第一次查看代码时并没有意识到它,但是现在它变得更加明显了。
Spets

51

从那以后我发现了一个使用jQuery的不错的方法

arr = $.grep(arr, function(v, k){
    return $.inArray(v ,arr) === k;
});

注意:此代码是从Paul Irish的鸭打孔帖子中提取 -我忘了给您以谢意了:P


8
一个简洁的解决方案,但是调用inArray的效率比调用hasOwnProperty的效率低。
史密斯先生2013年

这也是O(N ^ 2),对吗?而字典或hasOwnProperty方法可能是O(N * logN)。
Speedplane

这对我有用,Internet Explorer不支持所有其他解决方案。
凯尔

51

ES6最短的解决方案: [...new Set( [1, 1, 2] )];

或者,如果您想修改Array原型(如原始问题中所示):

Array.prototype.getUnique = function() {
    return [...new Set( [this] )];
};

EcmaScript 6 目前(2015年8月)仅在现代浏览器中部分实现,但是Babel在将ES6(甚至是ES7)转换回ES5方面变得非常流行。这样,您就可以立即编写ES6代码!

如果您想知道这是什么...意思,那就叫价差算子。在MDN中:«扩展运算符允许在需要多个参数(用于函数调用)或多个元素(用于数组文字)的地方扩展表达式”。因为Set是可迭代的(并且只能具有唯一值),所以散布运算符将扩展Set来填充数组。

学习ES6的资源:


3
您可以使用来做得更短,a = [...Set(a)]但是无论如何,目前暂时仅是Firefox。
2014年

@ c69,对,不会比这短。SpiderMonkey用户也将不胜感激。
Torsten Becker 2014年

与Babel合作。您只需要包括Array.from垫片即可:require ( "core-js/fn/array/from" );
Vladislav Rastrusny15年

应该是Array.prototype.getUnique = function(){return [... new Set([this])]; }; 或者,Array.prototype.getUnique = function(){return [... new Set(this)]; }; 我将其应用于以下-$('body')。html()。toLowerCase()。match(/([[a-zA-Z0-9 ._ +-] + @ [a-zA-Z0-9。 _-] + \。[a-zA-Z0-9 ._-] +)/ gi).getUnique(); 第一个仅在第二个重新调整了所有重复项的情况下才给我一个唯一的值。
ashique

[...Set(['a', 1, 'a', 2, '1'])]将抛出TypeError,因此保留以下内容仍然是明智的new[...new Set(['a', 1, 'a', 2, '1'])]
Adam Katz

36

最简单的解决方案:

var arr = [1, 3, 4, 1, 2, 1, 3, 3, 4, 1];
console.log([...new Set(arr)]);

要么:

var arr = [1, 3, 4, 1, 2, 1, 3, 3, 4, 1];
console.log(Array.from(new Set(arr)));


4
请注意,IE10及更低版本不支持此功能。需要注意的是IE11 +和Safari提供有限的支持(甚至不知道那上面的例子作品)developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
oriadam

32

最简单,最快的方法(在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;
}

只需遍历数组中的每个项目,测试该项目是否已在列表中,如果不是,则将其推入要返回的数组。

根据jsPerf的说法,此功能是我在任何地方都能找到的最快的功能-随时添加自己的功能。

非原型版本:

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浏览器中,这也比上述方法快


在Linux上,Chrome 55.0.2883更喜欢使用arr.unique(),而swilliams的arrclone2.sortFilter()最慢(慢78%)。但是,Firefox 51.0.0(具有大量附加组件)具有最快的速度(但Ops / sec仍然比其他任何Chrome结果慢),而mottie的jQuery $ .grep(arr,jqFilter)最慢(慢46%)。您的arr.uniq()慢了30%。我对每个测试运行了两次,并获得了一致的结果。拉斐尔的arr.getUnique()在两种浏览器中均排名第二。
亚当·卡兹

jsPerf是越野车的那一刻,让我的编辑到本次测试没有犯的一切,但它增加了两个测试中表现的结果:COCCO的toUnique()击败Vamsi的ES6 list.filter()在这两种浏览器,击败swilliams' sortFilter()在FF上排名第一(排序过滤器慢了16%),并在Chrome上击败了排名第三的排序测试(慢了2%)。
亚当·卡兹

啊,我没发现那些测试很小,并不重要。对已接受答案的注释描述了该问题,并在测试的修订版中进行了更正,其中拉斐尔的代码很容易最快,而Joetje50的arr.unique代码要慢98%。如评论中所述,我还进行了另一次修订。
亚当·卡兹

1
好吧,实际上,您在unique函数中实现的算法具有O(n ^ 2)复杂度,而in getUnique是O(n)。在小型数据集上,第一个可能更快,但是如何与数学争论:)如果您在一系列1e5个独特项目上运行它,可以确保后者更快
Mikhail Dudin

31

仅性能!此代码可能比此处的所有代码快10倍 *在所有浏览器上均适用,并且对内存的影响最小。

如果您不需要重用旧数组,请先执行其他必要的操作,然后再将其转换为唯一数组,这可能是最快的方法,而且非常简短。

var array=[1,2,3,4,5,6,7,8,9,0,1,2,1];

那你可以试试看

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 1];

function toUnique(a, b, c) { //array,placeholder,placeholder
  b = a.length;
  while (c = --b)
    while (c--) a[b] !== a[c] || a.splice(c, 1);
  return a // not needed ;)
}
console.log(toUnique(array));
//[3, 4, 5, 6, 7, 8, 9, 0, 2, 1]

我在阅读本文时想出了此功能...

http://www.shamasis.net/2009/09/fast-algorithm-to-find-unique-items-in-javascript-array/

我不喜欢for循环。它有很多参数。我喜欢while--循环。除了我们都非常喜欢的浏览器外,Chrome浏览器是所有浏览器中最快的循环。

无论如何,我编写了第一个使用while的函数,是的,比本文中找到的函数要快一点,但还不够。unique2()

下一步使用现代js。Object.keys 我用js1.7的Object.keys替换了另一个for循环...更快,更短(在chrome中快2倍);)。不够!。unique3()

在这一点上,我正在考虑我真正需要的功能。我不需要旧的数组,我想要一个快速的函数。所以我用了2个while循环+拼接。unique4()

没用说我印象深刻。

chrome:通常每秒150,000次操作跃升至每秒1,800,000次操作。

即: 80,000 op / s与3,500,000 op / s

ios: 18,000 op / s和170,000 op / s

野生动物园: 80,000 op / s与6,000,000 op / s

证明 http://jsperf.com/wgu或更好地使用console.time ... microtime ...等等

unique5() 只是为了告诉您如果要保留旧数组会发生什么。

Array.prototype如果您不知道您在做什么,请不要使用。我只是做了很多复制和过去。Object.defineProperty(Array.prototype,...,writable:false,enumerable:false})如果要创建本机原型,请使用此示例。例如:https : //stackoverflow.com/a/20463021/2450730

演示 http://jsfiddle.net/46S7g/

注意:此操作后,您的旧阵列将被破坏/变为唯一。

如果您看不懂上面的代码,请阅读一本javascript书籍,或以下有关较短代码的说明。https://stackoverflow.com/a/21353032/2450730

一些正在使用indexOf ...不要... http://jsperf.com/dgfgghfghfghghgfhgfhfghfhgfh

用于空数组

!array.length||toUnique(array); 

4
在node.js上进行了测试,并带有100k个Urls(字符串)数组。结果比underscore.js _.uniq慢2倍...尽管单独的jsperf同意您(jsperf.com/uniq-performance/5),但我很失望:(
xShirase 2014年

3
您没有在jsperf中正确测试...在您的示例中,每次都定义该函数...,但是underscore.js函数已经定义..这对我的函数不利。还要测试3和4。我应该说的另一件事是,如果您使用混合变量(字符串和数字),则应将a [b]!== a [c]替换为a [b]!= a [c]
cocco

2
我不确定jsPerf是否正确,但是似乎Reduce替代(对我来说更容易理解)比您的解决方案快94%。jsperf.com/reduce-for-distinct编辑:在chrome上,您的速度较慢;在Edge上,您的速度更快;在Firefox上,您的速度更快。
维克多·艾文斯

3
仅适用于小阵列/重复率低的情况。每次找到非唯一对象时,引擎将被迫通过以下方式将所有其余元素向左移动:1)遍历它们2)读取它们3)将它们写入新位置。然后,它创建了一个拼接的元素新的阵列,并返回它的结果...算法迅速用更大的阵列/更频繁的重复降解。对于大小为1000-> 10000-> 50000且重复次数约为40%的阵列,平均耗时将为2-> 775-> 19113 ms。(在Chrome中,大小更改为1-> x10-> x5,时间更改为1-> x10-> x25)。
ankhzet

1
谢谢。好的方法。但是正确的物品顺序呢?
liberborn

28

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

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

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

Array.from到重新设置为一个数组转换,这样你可以很方便地访问所有的真棒方法(特征),数组必须是有用的。还有其他做相同事情的方法。但是您可能根本不需要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的东西。

只需将您喜欢的任何数组的名称传递给它即可。


顺便说一句,我使用了一个充满字符串的数组来表明我的技术很灵活。它适用于数字。
塞斯·霍尔拉迪

20
["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;
 }, []);

您能解释一下为什么不push将元素简单地放入数组而不是使用数组concat吗?我尝试使用推,但失败了。我在寻找解释。
makenova

1
原因在于返回值。concat返回修改后的数组(正是reduce函数中需要返回的数组),而push返回一个索引,您可以在该索引处访问push值。这是否回答你的问题?
sergeyz 2015年

显然,此解决方案在chrome(和节点)中相当快。jsperf.com/reduce-for-distinct这是ES6版本: [0,1,2,0,3,2,1,5].reduce((prev, cur) => ~prev.indexOf(cur) ? prev : prev.concat([cur]), []);
Victor Ivens

旁注:此实现方式NaN不友好
-Alireza

19

我们可以使用ES6集来做到这一点:

var duplicatedArray = [1, 2, 3, 4, 5, 1, 1, 1, 2, 3, 4];
var uniqueArray = Array.from(new Set(duplicatedArray));

console.log(uniqueArray);

//输出将是

uniqueArray = [1,2,3,4,5];

17

这个原型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"]


1
请注意,这$foo = 'bar'是PHP声明变量的方法。它可以在javascript中工作,但会创建一个隐式全局,通常不应该这样做。
卡米洛·马丁

@CamiloMartin抱歉,但是您错了,$ foo是全局的,因为该示例不在闭包中,并且缺少var关键字。与美元无关jsfiddle.net/robaldred/L2MRb
Rob

8
@Rob就是我在说的,PHP人们会认为$foo这实际上var foo是在javascript中声明变量的方式。
卡米洛·马丁

13

无需扩展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等)的开发非常有用。


2
它是js 1.6的事实并不意味着您不能使用filter。在MDN页面上,它们具有Internet Explorer(我的意思是较旧的浏览器)的实现。另外:JS 1.6仅指Firefox的JS引擎,但正确的事情说出来的,这是ECMAScript的5
卡米洛·马丁

@CamiloMartin我将1.6更改为ECMAScript5。谢谢。
心教堂

13

魔法

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

O(n) 性能;我们假设您的数组位于a和中t={}此处说明(+ 杰普展示)


14
看起来非常酷,如果没有明确的解释,我会倒下,当我运行它时,您将挖到比特币
的OndrejŽelazko

3
我的意思是,您应该使用一些解释和解构评论来扩展您的答案。不要期望人们会找到有用的答案。(虽然它看起来真的冷却大概作品)
的OndrejŽelazko

1
@Jeppe当我进行改进时,我会体验到aha的效果(之前,我不知道可以in在除for循环之外的其他构造函数之外使用运算符:P)-谢谢-非常感谢,并会给您其他好的答案+2 。
卡米尔·基列夫斯基(KamilKiełczewski),

2
那不是创建一个全局变量t,该变量在过滤后仍然有效吗?
菲利普

1
这确实是一个棘手的解决方案。不幸的是,除非必须定义全局变量和/或依靠模块捆绑程序隐藏全局变量,否则必须在表达式外部声明t。看起来不错,但是请您自己(和您的开发团队)帮个忙,然后写两句划线:let t = {}; a.filter(e =>!(t [e] = e in t))
ford04

13
[...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]

尽管这段代码可以解决问题,但包括解释如何以及为什么解决该问题的说明,确实可以帮助提高您的帖子质量,并可能导致更多的投票。请记住,您将来会为读者回答问题,而不仅仅是现在问的人。请编辑您的答案以添加解释,并指出适用的限制和假设。
id.ot

11

如果您使用的是Prototype框架,则无需执行“ for”循环,可以使用http://www.prototypejs.org/api/array/uniq这样:

var a = Array.uniq();  

这将产生一个没有重复的重复数组。我遇到了您的问题,寻找一种方法来计算不同的数组记录,因此

uniq()

我用了

尺寸()

结果很简单。ps对不起,如果我输错了一些内容

编辑:如果要转义未定义的记录,则可能要添加

紧凑()

之前,像这样:

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

14
因为我找到了一个更好的答案,所以我认为主题适用于所有人,而不仅仅是针对那些问过的人
Decebal

11

现在,您可以使用集合删除重复项并将其转换回数组。

var names = ["Mike","Matt","Nancy", "Matt","Adam","Jenny","Nancy","Carl"];

console.log([...new Set(names)])

另一个解决方案是使用排序和过滤

var names = ["Mike","Matt","Nancy", "Matt","Adam","Jenny","Nancy","Carl"];
var namesSorted = names.sort();
const result = namesSorted.filter((e, i) => namesSorted[i] != namesSorted[i+1]);
console.log(result);


10

那是因为这0是JavaScript中的虚假值。

this[i] 如果数组的值为0或任何其他伪造的值,则将是伪造的。


嗯,好吧,我现在看到了……但是是否有一个简单的解决方案可以使其正常工作?
2009年

10
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相等中,它似乎不太合理)。
ephemient

只能使用不可枚举的方法来扩展Array.prototype .... Object.defineProperty(Array.prototype,“ getUnique”,{})...但是使用辅助对象的想法非常好
bortunac

10

我有一个稍微不同的问题,我需要从数组中删除具有重复id属性的对象。这工作。

let objArr = [{
  id: '123'
}, {
  id: '123'
}, {
  id: '456'
}];

objArr = objArr.reduce((acc, cur) => [
  ...acc.filter((obj) => obj.id !== cur.id), cur
], []);

console.log(objArr);


9

最简单的答案是:

const array = [1, 1, 2, 2, 3, 5, 5, 2];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // [1, 2, 3, 5]

很简单,但不适用于对象数组。
Thanwa Ch。

7

我不确定加布里埃尔·西尔维拉(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
  )

7

如果您对额外的依赖关系没问题,或者您的代码库中已经有一个库,则可以使用LoDash(或Underscore)从适当的阵列中删除重复项。

用法

如果您的代码库中还没有它,请使用npm进行安装:

npm install lodash

然后按以下方式使用它:

import _ from 'lodash';
let idArray = _.uniq ([
    1,
    2,
    3,
    3,
    3
]);
console.dir(idArray);

出:

[ 1, 2, 3 ]

7

这已经得到了很多回答,但是并没有解决我的特殊需求。

许多答案是这样的:

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

很棒的解决方案,请注意,新数组将从函数中返回。(它不会自行修改)
Thanwa Ch。

6

来自Shamasis Bhattacharya的博客(O(2n)时间复杂度):

Array.prototype.unique = function() {
    var o = {}, i, l = this.length, r = [];
    for(i=0; i<l;i+=1) o[this[i]] = this[i];
    for(i in o) r.push(o[i]);
    return r;
};

来自Paul Irish的博客:JQuery的改进.unique()

(function($){

    var _old = $.unique;

    $.unique = function(arr){

        // do the default behavior only if we got an array of elements
        if (!!arr[0].nodeType){
            return _old.apply(this,arguments);
        } else {
            // reduce the array to contain no dupes via grep/inArray
            return $.grep(arr,function(v,k){
                return $.inArray(v,arr) === k;
            });
        }
    };
})(jQuery);

// in use..
var arr = ['first',7,true,2,7,true,'last','last'];
$.unique(arr); // ["first", 7, true, 2, "last"]

var arr = [1,2,3,4,5,4,3,2,1];
$.unique(arr); // [1, 2, 3, 4, 5]

6

用简单的方法查找唯一的数组值

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]

6

看来我们已经失去了拉斐尔(Rafael)的答案,该答案已被接受为几年。如果您没有混合类型的数组,那么这(至少在2017年)是性能最佳的解决方案:

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

5

为了解决该问题,反之亦然,加载数组时不重复可能很有用,Set对象可以这样做,但是并非在所有浏览器中都可用。如果您需要多次查看其内容,它将节省内存并提高效率。

Array.prototype.add = function (elem) {
   if (this.indexOf(elem) == -1) {
      this.push(elem);
   }
}

样品:

set = [];
[1,3,4,1,2,1,3,3,4,1].forEach(function(x) { set.add(x); });

给你 set = [1,3,4,2]

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.