关于javascript:从JS数组中删除重复值

Remove duplicate values from JS array

本问题已经有最佳答案,请猛点这里访问。

我有一个非常简单的javascript数组,可以包含也可以不包含重复项。

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

我需要删除重复项并将唯一值放入新数组中。

我可以指出我尝试过的所有代码,但我认为这是无用的,因为它们不起作用。我也接受jquery解决方案。

类似问题:

  • 获取数组中的所有非唯一值(即重复/多次出现)


DR

使用set构造函数和spread语法:好的。

1
uniq = [...new Set(array)];

"聪明"但NA?VE方式

1
2
3
uniqueArray = a.filter(function(item, pos) {
    return a.indexOf(item) == pos;
})

基本上,我们迭代数组,并检查每个元素在数组中的第一个位置是否等于当前位置。显然,这两个位置对于重复元素是不同的。好的。

使用filter回调的第三个("this array")参数可以避免数组变量的关闭:好的。

1
2
3
uniqueArray = a.filter(function(item, pos, self) {
    return self.indexOf(item) == pos;
})

虽然算法简洁,但对于大型阵列(二次时间)来说,该算法并不是特别有效。好的。救援的哈希表

1
2
3
4
5
6
function uniq(a) {
    var seen = {};
    return a.filter(function(item) {
        return seen.hasOwnProperty(item) ? false : (seen[item] = true);
    });
}

这就是通常的做法。其思想是将每个元素放在哈希表中,然后立即检查其是否存在。这给了我们线性时间,但至少有两个缺点:好的。

  • 因为哈希键只能是javascript中的字符串,所以此代码不区分数字和"数字字符串"。也就是说,uniq([1,"1"])只会返回[1]
  • 出于同样的原因,所有对象都被认为是相等的:uniq([{foo:1},{foo:2}])只返回[{foo:1}]

也就是说,如果您的数组只包含基元,而您不关心类型(例如,它总是数字),那么这个解决方案是最佳的。好的。两全其美

一个通用的解决方案结合了这两种方法:它使用散列查找原语和线性搜索对象。好的。

1
2
3
4
5
6
7
8
9
10
11
function uniq(a) {
    var prims = {"boolean":{},"number":{},"string":{}}, objs = [];

    return a.filter(function(item) {
        var type = typeof item;
        if(type in prims)
            return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true);
        else
            return objs.indexOf(item) >= 0 ? false : objs.push(item);
    });
}

排序:UNIQ

另一个选项是先对数组进行排序,然后删除与前一个元素相等的每个元素:好的。

1
2
3
4
5
function uniq(a) {
    return a.sort().filter(function(item, pos, ary) {
        return !pos || item != ary[pos - 1];
    })
}

同样,这不适用于对象(因为所有对象对于sort)都是相等的)。另外,我们悄悄地改变了原来的阵列作为副作用-不好!但是,如果您的输入已经排序,这是一种方法(只需从上面删除sort)。好的。独特的…

有时需要基于一些条件(而不仅仅是相等条件)统一列表,例如筛选出不同的对象,但共享某些属性。这可以通过传递回调优雅地完成。此"key"回调应用于每个元素,并且删除具有相同"keys"的元素。由于key将返回原语,因此哈希表在这里可以正常工作:好的。

1
2
3
4
5
6
7
function uniqBy(a, key) {
    var seen = {};
    return a.filter(function(item) {
        var k = key(item);
        return seen.hasOwnProperty(k) ? false : (seen[k] = true);
    })
}

一个特别有用的key()JSON.stringify,它将删除物理上不同但"看起来"相同的对象:好的。

1
2
3
a = [[1,2,3], [4,5,6], [1,2,3]]
b = uniqBy(a, JSON.stringify)
console.log(b) // [[1,2,3], [4,5,6]]

如果key不是原始的,则必须采用线性搜索:好的。

1
2
3
4
5
6
7
function uniqBy(a, key) {
    var index = [];
    return a.filter(function (item) {
        var k = key(item);
        return index.indexOf(k) >= 0 ? false : index.push(k);
    });
}

在ES6中,您可以使用Set:好的。

1
2
3
4
5
6
7
function uniqBy(a, key) {
    let seen = new Set();
    return a.filter(item => {
        let k = key(item);
        return seen.has(k) ? false : seen.add(k);
    });
}

或者一个Map:好的。

1
2
3
4
5
6
7
function uniqBy(a, key) {
    return [
        ...new Map(
            a.map(x => [key(x), x])
        ).values()
    ]
}

这两种方法都适用于非基元键。好的。第一个还是最后一个?

通过键删除对象时,您可能希望保留"相等"对象中的第一个或最后一个对象。好的。

使用上面的Set变体保留第一个,使用Map变体保留最后一个:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function uniqByKeepFirst(a, key) {
    let seen = new Set();
    return a.filter(item => {
        let k = key(item);
        return seen.has(k) ? false : seen.add(k);
    });
}


function uniqByKeepLast(a, key) {
    return [
        ...new Map(
            a.map(x => [key(x), x])
        ).values()
    ]
}

//

data = [
    {a:1, u:1},
    {a:2, u:2},
    {a:3, u:3},
    {a:4, u:1},
    {a:5, u:2},
    {a:6, u:3},
];

console.log(uniqByKeepFirst(data, it => it.u))
console.log(uniqByKeepLast(data, it => it.u))

好的。图书馆

下划线和长划线都提供了uniq方法。他们的算法基本上类似于上面的第一个片段,并归结为:好的。

1
2
3
4
5
6
var result = [];
a.forEach(function(item) {
     if(result.indexOf(item) < 0) {
         result.push(item);
     }
});

这是二次的,但是还有一些不错的附加功能,比如包装本机indexOf,能够通过键进行统一(iteratee),以及对已经排序的数组进行优化。好的。

如果你用的是jquery,没有一块钱你什么都受不了,它是这样的:好的。

1
2
3
4
5
  $.uniqArray = function(a) {
        return $.grep(a, function(item, pos) {
            return $.inArray(item, a) === pos;
        });
  }

这也是第一个片段的变体。好的。性能

函数调用在JavaScript中是昂贵的,因此上述解决方案虽然简洁,但效率不高。为了获得最佳性能,请用循环替换filter,并去掉其他函数调用:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function uniq_fast(a) {
    var seen = {};
    var out = [];
    var len = a.length;
    var j = 0;
    for(var i = 0; i < len; i++) {
         var item = a[i];
         if(seen[item] !== 1) {
               seen[item] = 1;
               out[j++] = item;
         }
    }
    return out;
}

这段难看的代码与上面的代码片段3相同,但是速度快了一个数量级(截至2017年,它的速度只有两倍-JS核心人员做得很好!)好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
function uniq(a) {
    var seen = {};
    return a.filter(function(item) {
        return seen.hasOwnProperty(item) ? false : (seen[item] = true);
    });
}

function uniq_fast(a) {
    var seen = {};
    var out = [];
    var len = a.length;
    var j = 0;
    for(var i = 0; i < len; i++) {
         var item = a[i];
         if(seen[item] !== 1) {
               seen[item] = 1;
               out[j++] = item;
         }
    }
    return out;
}

/////

var r = [0,1,2,3,4,5,6,7,8,9],
    a = [],
    LEN = 1000,
    LOOPS = 1000;

while(LEN--)
    a = a.concat(r);

var d = new Date();
for(var i = 0; i < LOOPS; i++)
    uniq(a);
document.write('uniq, ms/loop: ' + (new Date() - d)/LOOPS)

var d = new Date();
for(var i = 0; i < LOOPS; i++)
    uniq_fast(a);
document.write('uniq_fast, ms/loop: ' + (new Date() - d)/LOOPS)

好的。ES6

ES6提供了set对象,这使得事情变得更加简单:好的。

1
2
3
function uniq(a) {
   return Array.from(new Set(a));
}

或好的。

1
let uniq = a => [...new Set(a)];

请注意,与Python不同,ES6集是按插入顺序迭代的,因此此代码保留了原始数组的顺序。好的。

但是,如果您需要一个具有唯一元素的数组,为什么不从一开始就使用集合呢?好的。发电机

基于发电机的"懒惰"版本uniq可以在相同的基础上构建:好的。

  • 从参数中获取下一个值
  • 如果已经看到了,跳过它
  • 否则,生成它并将其添加到已经看到的值集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function* uniqIter(a) {
    let seen = new Set();

    for (let x of a) {
        if (!seen.has(x)) {
            seen.add(x);
            yield x;
        }
    }
}

// example:

function* randomsBelow(limit) {
    while (1)
        yield Math.floor(Math.random() * limit);
}

// note that randomsBelow is endless

count = 20;
limit = 30;

for (let r of uniqIter(randomsBelow(limit))) {
    console.log(r);
    if (--count === 0)
        break
}

// exercise for the reader: what happens if we set `limit` less than `count` and why

好的。好啊。


使用jquery快速而脏:

1
2
3
4
5
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var uniqueNames = [];
$.each(names, function(i, el){
    if($.inArray(el, uniqueNames) === -1) uniqueNames.push(el);
});


厌倦了看到for循环或jquery中的所有坏例子。Javascript现在有了完美的工具:排序、映射和减少。

在保持现有秩序的同时减少uniq

1
2
3
4
5
6
7
8
9
10
11
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];

var uniq = names.reduce(function(a,b){
    if (a.indexOf(b) < 0 ) a.push(b);
    return a;
  },[]);

console.log(uniq, names) // [ 'Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Carl' ]

// one liner
return names.reduce(function(a,b){if(a.indexOf(b)<0)a.push(b);return a;},[]);

更快的uniq和排序

可能有更快的方法,但这个方法相当不错。

1
2
3
4
5
6
7
8
9
10
11
var uniq = names.slice() // slice makes copy of array before sorting it
  .sort(function(a,b){
    return a > b;
  })
  .reduce(function(a,b){
    if (a.slice(-1)[0] !== b) a.push(b); // slice(-1)[0] means last item in array without removing it (like .pop())
    return a;
  },[]); // this empty array becomes the starting value for a

// one liner
return names.slice().sort(function(a,b){return a > b}).reduce(function(a,b){if (a.slice(-1)[0] !== b) a.push(b);return a;},[]);

更新2015:ES6版本:

在ES6中,您有集合和排列,这使得删除所有重复项非常容易和容易:

1
var uniq = [ ...new Set(names) ]; // [ 'Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Carl' ]

根据事件排序:

有人问到根据有多少个唯一的名称来排序结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var names = ['Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Nancy', 'Carl']

var uniq = names
  .map((name) => {
    return {count: 1, name: name}
  })
  .reduce((a, b) => {
    a[b.name] = (a[b.name] || 0) + b.count
    return a
  }, {})

var sorted = Object.keys(uniq).sort((a, b) => uniq[a] < uniq[b])

console.log(sorted)


Vanilla JS:使用集合之类的对象删除重复项

您可以尝试将其放入对象中,然后遍历其键:

1
2
3
4
5
6
7
8
9
10
11
function remove_duplicates(arr) {
    var obj = {};
    var ret_arr = [];
    for (var i = 0; i < arr.length; i++) {
        obj[arr[i]] = true;
    }
    for (var key in obj) {
        ret_arr.push(key);
    }
    return ret_arr;
}

Vanilla JS:通过跟踪已经看到的值来删除重复项(订单安全)

或者,对于订单安全版本,使用对象存储所有以前看到的值,并在添加到数组之前对照该值检查值。

1
2
3
4
5
6
7
8
9
10
11
12
function remove_duplicates_safe(arr) {
    var seen = {};
    var ret_arr = [];
    for (var i = 0; i < arr.length; i++) {
        if (!(arr[i] in seen)) {
            ret_arr.push(arr[i]);
            seen[arr[i]] = true;
        }
    }
    return ret_arr;

}

ECMAScript 6:使用新的集合数据结构(订单安全)

ecmascript 6添加了新的Set数据结构,它允许您存储任何类型的值。Set.values按插入顺序返回元素。

1
2
3
4
5
function remove_duplicates_es6(arr) {
    let s = new Set(arr);
    let it = s.values();
    return Array.from(it);
}

示例用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
a = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];

b = remove_duplicates(a);
// b:
// ["Adam","Carl","Jenny","Matt","Mike","Nancy"]

c = remove_duplicates_safe(a);
// c:
// ["Mike","Matt","Nancy","Adam","Jenny","Carl"]

d = remove_duplicates_es6(a);
// d:
// ["Mike","Matt","Nancy","Adam","Jenny","Carl"]


使用下划线.js

它是一个库,有许多用于操作数组的函数。

It's the tie to go along with jQuery's tux, and Backbone.js's
suspenders.

IUQ

_.uniq(array, [isSorted], [iterator]) Alias: unique
Produces a duplicate-free version of the array, using === to test object
equality. If you know in advance that the array is sorted, passing
true for isSorted will run a much faster algorithm. If you want to
compute unique items based on a transformation, pass an iterator
function.

例子

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

alert(_.uniq(names, false));

注:lo dash(一个下划线的竞争对手)也提供了一个类似的.uniq实现。


使用数组过滤器和函数索引的单行版本:

1
2
3
arr = arr.filter (function (value, index, array) {
    return array.indexOf (value) == index;
});


通过filter方法的第二个索引参数的帮助,您可以简单地在javascript中完成:

1
2
var a = [2,3,4,5,5,4];
a.filter(function(value, index){ return a.indexOf(value) == index });

或是用速记法

1
a.filter((v,i) => a.indexOf(v) == i)


一行:

1
2
3
let names = ['Mike','Matt','Nancy','Adam','Jenny','Nancy','Carl', 'Nancy'];
let dup = [...new Set(names)];
console.log(dup);


使用本机JavaScript函数从数组中删除重复项的最简单方法是使用如下序列:

1
vals.sort().reduce(function(a, b){ if (b != a[0]) a.unshift(b); return a }, [])

在reduce函数中不需要sliceindexOf,就像我在其他示例中看到的那样!但是,与过滤功能一起使用是有意义的:

1
vals.filter(function(v, i, a){ return i == a.indexOf(v) })

另一种ES6(2015年)的方法是:

1
Array.from(new Set(vals))

甚至使用排列运算符:

1
[...new Set(vals)]

干杯!


use Array.filter() like this

1
2
3
4
5
6
7
8
9
10
var actualArr = ['Apple', 'Apple', 'Banana', 'Mango', 'Strawberry', 'Banana'];

console.log('Actual Array: ' + actualArr);

var filteredArr = actualArr.filter(function(item, index) {
  if (actualArr.indexOf(item) == index)
    return item;
});

console.log('Filtered Array: ' + filteredArr);

这可以在ES6中缩短到

1
actualArr.filter((item,index,self) => self.indexOf(item)==index);

这是对Array.filter()的很好的解释。


去做这个:

1
2
3
var uniqueArray = duplicateArray.filter(function(elem, pos) {
    return duplicateArray.indexOf(elem) == pos;
});

现在uniquearray不包含重复项。


我遇到的最简单的一个。在ES6中。

1
2
3
 var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl","Mike","Nancy"]

 var noDupe = Array.from(new Set(names))

https://developer.mozilla.org/en-us/docs/web/javascript/reference/global_objects/set


我在另一个问题上做了一个关于消除重复的详细比较,但是我注意到这是我想要在这里分享的真实的地方。

我相信这是最好的方法

1
2
3
var myArray = [100, 200, 100, 200, 100, 100, 200, 200, 200, 200],
    reduced = Object.keys(myArray.reduce((p,c) => (p[c] = true,p),{}));
console.log(reduced);

好啊。。尽管这一个是O(n),其他的是O(n^2),我还是很想看到这个reduce/look up table和filter/indexof combo之间的基准比较(我选择了jeetendras非常好的实现https://stackoverflow.com/a/3744144/4543207)。我准备了一个100K的项目数组,其中填充了0-9999范围内的随机正整数,它删除了重复项。我重复测试10次,结果的平均值表明它们在性能上不匹配。

  • 在Firefox v47中,reduce&lut:14.85ms与filter&indexof:2836ms之比
  • 在Chrome v51中,减少lut:23.90ms,而过滤器和索引:1066ms

好吧,到目前为止还不错。但这次让我们以ES6的方式来做正确的事情。看起来很酷。但到目前为止,它如何对抗强大的LUT解决方案对我来说是个谜。让我们先看看代码,然后对其进行基准测试。

1
2
3
var myArray = [100, 200, 100, 200, 100, 100, 200, 200, 200, 200],
    reduced = [...myArray.reduce((p,c) => p.set(c,true),new Map()).keys()];
console.log(reduced);

哇,真是太短了!但是表演呢?很漂亮…由于过滤器/索引的重量超过了我们的肩膀,现在我可以测试一个数组1米范围内的正整数随机项,从10个连续测试中得到平均值。我可以说这次是真正的比赛。自己看看结果:)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var ranar = [],
     red1 = a => Object.keys(a.reduce((p,c) => (p[c] = true,p),{})),
     red2 = a => reduced = [...a.reduce((p,c) => p.set(c,true),new Map()).keys()],
     avg1 = [],
     avg2 = [],
       ts = 0,
       te = 0,
     res1 = [],
     res2 = [],
     count= 10;
for (var i = 0; i<count; i++){
  ranar = (new Array(1000000).fill(true)).map(e => Math.floor(Math.random()*100000));
  ts = performance.now();
  res1 = red1(ranar);
  te = performance.now();
  avg1.push(te-ts);
  ts = performance.now();
  res2 = red2(ranar);
  te = performance.now();
  avg2.push(te-ts);
}

avg1 = avg1.reduce((p,c) => p+c)/count;
avg2 = avg2.reduce((p,c) => p+c)/count;

console.log("reduce & lut took:" + avg1 +"msec");
console.log("map & spread took:" + avg2 +"msec");

你会用哪一个……?好吧,别那么快…!不要被欺骗。地图处于位移位置。现在看…在上述所有情况下,我们用范围小于n的数字填充一个大小为n的数组。我的意思是我们有一个大小为100的数组,我们用随机数0..9填充,所以有明确的重复,"几乎"肯定每个数字都有一个重复。如果我们用0到9999的随机数填充100大小的数组,怎么样?现在让我们看看地图在家里玩。这次是一个由100K项组成的数组,但随机数范围是0..100M。我们将进行100次连续测试来平均结果。好吧,让我们看看下注的内容。<没有打印错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var ranar = [],
     red1 = a => Object.keys(a.reduce((p,c) => (p[c] = true,p),{})),
     red2 = a => reduced = [...a.reduce((p,c) => p.set(c,true),new Map()).keys()],
     avg1 = [],
     avg2 = [],
       ts = 0,
       te = 0,
     res1 = [],
     res2 = [],
     count= 100;
for (var i = 0; i<count; i++){
  ranar = (new Array(100000).fill(true)).map(e => Math.floor(Math.random()*100000000));
  ts = performance.now();
  res1 = red1(ranar);
  te = performance.now();
  avg1.push(te-ts);
  ts = performance.now();
  res2 = red2(ranar);
  te = performance.now();
  avg2.push(te-ts);
}

avg1 = avg1.reduce((p,c) => p+c)/count;
avg2 = avg2.reduce((p,c) => p+c)/count;

console.log("reduce & lut took:" + avg1 +"msec");
console.log("map & spread took:" + avg2 +"msec");

现在这是地图()的壮观回归!也许现在你可以做一个更好的决定,当你想删除重复。

好吧,我们现在都很高兴。但是领头的角色总是以掌声排在最后。我相信你们中的一些人想知道这个设定物体会做什么。既然我们对ES6开放,而且我们知道MAP是前几场比赛的胜利者,那么让我们将MAP与SET作为决赛进行比较。一场典型的皇马对巴塞罗那的比赛…或者是?让我们看看谁会赢得El Classico:)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var ranar = [],
     red1 = a => reduced = [...a.reduce((p,c) => p.set(c,true),new Map()).keys()],
     red2 = a => Array.from(new Set(a)),
     avg1 = [],
     avg2 = [],
       ts = 0,
       te = 0,
     res1 = [],
     res2 = [],
     count= 100;
for (var i = 0; i<count; i++){
  ranar = (new Array(100000).fill(true)).map(e => Math.floor(Math.random()*10000000));
  ts = performance.now();
  res1 = red1(ranar);
  te = performance.now();
  avg1.push(te-ts);
  ts = performance.now();
  res2 = red2(ranar);
  te = performance.now();
  avg2.push(te-ts);
}

avg1 = avg1.reduce((p,c) => p+c)/count;
avg2 = avg2.reduce((p,c) => p+c)/count;

console.log("map & spread took:" + avg1 +"msec");
console.log("set & A.from took:" + avg2 +"msec");

真的。。男人!好吧,出乎意料的是,它根本不是一个El Classico。更像巴塞罗纳对卡奥萨苏纳的比赛。)


以下比列出的jquery方法快80%以上(请参阅下面的测试)。这是几年前一个类似问题的答案。如果我遇到最初提出申请的人,我会寄信用卡。纯JS。

1
2
3
4
5
6
7
var temp = {};
for (var i = 0; i < array.length; i++)
  temp[array[i]] = true;
var r = [];
for (var k in temp)
  r.push(k);
return r;

我的测试用例比较:http://jspef.com/remove-duplicate-array-tests


在ecmascript 6(aka ecmascript 2015)中,可以使用Set过滤重复项。然后,可以使用spread运算符将其转换回数组。

1
2
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"],
    unique = [...new Set(names)];


下面是这个问题的简单答案。

1
2
3
4
5
6
7
8
var names = ["Alex","Tony","James","Suzane","Marie","Laurence","Alex","Suzane","Marie","Marie","James","Tony","Alex"];
var uniqueNames = [];

    for(var i in names){
        if(uniqueNames.indexOf(names[i]) === -1){
            uniqueNames.push(names[i]);
        }
    }


解决方案1

1
2
3
4
5
6
7
8
Array.prototype.unique = function() {
    var a = [];
    for (i = 0; i < this.length; i++) {
        var current = this[i];
        if (a.indexOf(current) < 0) a.push(current);
    }
    return a;
}

解决方案2(使用套装)

1
2
3
Array.prototype.unique = function() {
    return Array.from(new Set(this));
}

试验

1
2
var x=[1,2,3,3,2,1];
x.unique() //[1,2,3]

性能

当我在chrome中测试两种实现(有设置和无设置)的性能时,我发现有设置的实现要快得多!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Array.prototype.unique1 = function() {
    var a = [];
    for (i = 0; i < this.length; i++) {
        var current = this[i];
        if (a.indexOf(current) < 0) a.push(current);
    }
    return a;
}


Array.prototype.unique2 = function() {
    return Array.from(new Set(this));
}

var x=[];
for(var i=0;i<10000;i++){
    x.push("x"+i);x.push("x"+(i+1));
}

console.time("unique1");
console.log(x.unique1());
console.timeEnd("unique1");



console.time("unique2");
console.log(x.unique2());
console.timeEnd("unique2");


一种简单而有效的技术是将filter方法与滤波器function(value, index){ return this.indexOf(value) == index }结合使用。

代码示例:

1
2
3
4
5
var data = [2,3,4,5,5,4];
var filter = function(value, index){ return this.indexOf(value) == index };
var filteredData = data.filter(filter, data );

document.body.innerHTML = '[cc lang="javascript"]' + JSON.stringify(filteredData, null, '\t') +  '

';.

1
2
3
4
5
6
</P><P>另见这把小提琴。</P><div class="suo-content">[collapse title=""]<ul><li>天才!举例来说,如果你想要重复的(而不是删除它们),你所要做的就是用<wyn>this.indexOf(value, index+1) > 0 </wyn>代替<wyn>this.indexOf(value) == index </wyn>,谢谢!</li><li>你甚至可以把它恢复到一个"过滤器"行:<wyn>filterData = data.filter((v, i) => (data.indexOf(v) == i) ); </wyn></li><li>上次我麻烦了!对不起的。。。拿起我的第一个答案,在两行中,你可以得到一个重复的json <wyn>var JSON_dupCounter = {};</wyn>,以及重复的次数:<wyn>data.filter((testItem, index) => (data.indexOf(testItem, index + 1) > 0)).forEach((found_duplicated) => (JSON_dupCounter[found_duplicated] = (JSON_dupCounter [found_duplicated] || 1) + 1));</wyn></li><li>这只适用于基元数组吗?</li><li>@冻结:如果与所有可以使用<wyn>==</wyn>来确定相等性的东西一起工作。因此,如果您处理的是数组、对象或函数,那么过滤器将只适用于引用同一数组、对象或函数的不同条目(参见演示)。如果要根据不同的条件确定相等性,则需要在筛选器中包含这些条件。</li></ul>[/collapse]</div><hr><P>这里有一个简单的方法,没有任何特殊的库都是特殊的函数,</P><P>[cc lang="javascript"]name_list = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
get_uniq = name_list.filter(function(val,ind) { return name_list.indexOf(val) == ind; })

console.log("Original name list:"+name_list.length, name_list)
console.log("
 Unique name list:"
+get_uniq.length, get_uniq)

enter image description here


最重要的答案有O(n2)的复杂性,但这可以通过使用对象作为散列来实现,只需O(n)

1
2
3
4
5
6
7
8
9
function getDistinctArray(arr) {
    var dups = {};
    return arr.filter(function(el) {
        var hash = el.valueOf();
        var isDup = dups[hash];
        dups[hash] = true;
        return !isDup;
    });
}

这对字符串、数字和日期有效。如果您的数组包含复杂对象(即,必须将它们与===进行比较),那么上述解决方案将无法工作。通过在对象本身设置一个标志,可以获得对象的O(n)实现:

1
2
3
4
5
6
7
8
9
10
11
function getDistinctObjArray(arr) {
    var distinctArr = arr.filter(function(el) {
        var isDup = el.inArray;
        el.inArray = true;
        return !isDup;
    });
    distinctArr.forEach(function(el) {
        delete el.inArray;
    });
    return distinctArr;
}


除了是一个比当前答案更简单、更简洁的解决方案(减去未来的ES6答案),我还进行了性能测试,测试速度也快得多:

1
2
3
var uniqueArray = dupeArray.filter(function(item, i, self){
  return self.lastIndexOf(item) == i;
});

有一个警告:array.lastindexof()是在ie9中添加的,所以如果需要低于这个值,就需要在其他地方查找。


所以选项是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let a = [11,22,11,22];
let b = []


b = [ ...new Set(a) ];    
// b = [11, 22]

b = Array.from( new Set(a))  
// b = [11, 22]

b = a.filter((val,i)=>{
  return a.indexOf(val)==i
})                        
// b = [11, 22]

通用函数方法

以下是ES2015的通用和严格功能方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// small, reusable auxiliary functions

const apply = f => a => f(a);

const flip = f => b => a => f(a) (b);

const uncurry = f => (a, b) => f(a) (b);

const push = x => xs => (xs.push(x), xs);

const foldl = f => acc => xs => xs.reduce(uncurry(f), acc);

const some = f => xs => xs.some(apply(f));


// the actual de-duplicate function

const uniqueBy = f => foldl(
   acc => x => some(f(x)) (acc)
    ? acc
    : push(x) (acc)
 ) ([]);


// comparators

const eq = y => x => x === y;

// string equality case insensitive :D
const seqCI = y => x => x.toLowerCase() === y.toLowerCase();


// mock data

const xs = [1,2,3,1,2,3,4];

const ys = ["a","b","c","A","B","C","D"];


console.log( uniqueBy(eq) (xs) );

console.log( uniqueBy(seqCI) (ys) );

我们可以很容易地从unqiueBy中派生unique或使用Set的更快的实现:

1
2
3
const unqiue = uniqueBy(eq);

// const unique = xs => Array.from(new Set(xs));

这种方法的好处:

  • 使用单独的比较器函数的通用解决方案
  • 声明性和简洁的实现
  • 重用其他小型通用函数

性能注意事项

uniqueBy没有带循环的命令式实现快,但由于它的通用性,它更具表现力。

如果您在应用程序中确定uniqueBy是造成具体性能损失的原因,请将其替换为优化代码。也就是说,首先以一种功能性的、声明性的方式编写代码。之后,如果您遇到性能问题,请尝试在导致问题的位置优化代码。

内存消耗和垃圾收集

uniqueBy利用隐藏在体内的突变(push(x) (acc))。它重用了累加器,而不是在每次迭代之后丢弃它。这减少了内存消耗和GC压力。由于这个副作用被包装在函数内部,所以外部的一切都保持纯净。


这里非常容易理解和处理任何地方(甚至在photoshopscript中)的代码。检查一下!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var peoplenames = new Array("Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl");

peoplenames = unique(peoplenames);
alert(peoplenames);

function unique(array){
    var len = array.length;
    for(var i = 0; i < len; i++) for(var j = i + 1; j < len; j++)
        if(array[j] == array[i]){
            array.splice(j,1);
            j--;
            len--;
        }
    return array;
}

//*result* peoplenames == ["Mike","Matt","Nancy","Adam","Jenny","Carl"]


1
2
3
4
5
for (i=0; i<originalArray.length; i++) {  
    if (!newArray.includes(originalArray[i])) {
        newArray.push(originalArray[i]);
    }
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
$(document).ready(function() {

    var arr1=["dog","dog","fish","cat","cat","fish","apple","orange"]

    var arr2=["cat","fish","mango","apple"]

    var uniquevalue=[];
    var seconduniquevalue=[];
    var finalarray=[];

    $.each(arr1,function(key,value){

       if($.inArray (value,uniquevalue) === -1)
       {
           uniquevalue.push(value)

       }

    });

     $.each(arr2,function(key,value){

       if($.inArray (value,seconduniquevalue) === -1)
       {
           seconduniquevalue.push(value)

       }

    });

    $.each(uniquevalue,function(ikey,ivalue){

        $.each(seconduniquevalue,function(ukey,uvalue){

            if( ivalue == uvalue)

            {
                finalarray.push(ivalue);
            }  

        });

    });
    alert(finalarray);
});

这可能是从数组中永久删除重复项的最快方法之一。比这里的大多数功能快10倍,比Safari快78倍

1
2
3
function toUnique(a,b,c){               //array,placeholder,placeholder
 b=a.length;while(c=--b)while(c--)a[b]!==a[c]||a.splice(c,1)
}
  • 测试:http://jspef.com/wgu
  • 演示:http://jsfiddle.net/46s7g/
  • 更多:https://stackoverflow.com/a/25082874/2450730
  • 如果你不能阅读上面的代码,请阅读一本javascript书,或者这里有一些关于较短代码的解释。https://stackoverflow.com/a/21353032/2450730


    如果你有机会

    D3.js

    你可以做到

    1
    d3.set(["foo","bar","foo","baz"]).values() ==> ["foo","bar","baz"]

    https://github.com/mbostock/d3/wiki/arrays设置值


    稍微修改一下THG435使用自定义比较器的最佳答案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    function contains(array, obj) {
        for (var i = 0; i < array.length; i++) {
            if (isEqual(array[i], obj)) return true;
        }
        return false;
    }
    //comparator
    function isEqual(obj1, obj2) {
        if (obj1.name == obj2.name) return true;
        return false;
    }
    function removeDuplicates(ary) {
        var arr = [];
        return ary.filter(function(x) {
            return !contains(arr, x) && arr.push(x);
        });
    }

    https://jsfiddle.net/2w0K5TZ8/

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    function remove_duplicates(array_){
        var ret_array = new Array();
        for (var a = array_.length - 1; a >= 0; a--) {
            for (var b = array_.length - 1; b >= 0; b--) {
                if(array_[a] == array_[b] && a != b){
                    delete array_[b];
                }
            };
            if(array_[a] != undefined)
                ret_array.push(array_[a]);
        };
        return ret_array;
    }

    console.log(remove_duplicates(Array(1,1,1,2,2,2,3,3,3)));

    循环访问、删除重复项并创建一个克隆数组占位符,因为数组索引将不会更新。

    向后循环以获得更好的性能(循环不需要一直检查数组的长度)


    虽然ES6解决方案是最好的,但我对没有人给出以下解决方案感到困惑:

    1
    2
    3
    4
    5
    6
    7
    function removeDuplicates(arr){
        o={}
        arr.forEach(function(e){
            o[e]=true
        })
        return Object.keys(o)
    }

    这里要记住的是,对象必须有唯一的键。我们正利用这一点来删除所有的副本。我本以为这是最快的解决方案(ES6之前)。

    但请记住,这也对数组进行排序。


    这只是另一个解决方案,但与其他方案不同。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function diffArray(arr1, arr2) {
      var newArr = arr1.concat(arr2);
      newArr.sort();
      var finalArr = [];
      for(var i = 0;i<newArr.length;i++) {
       if(!(newArr[i] === newArr[i+1] || newArr[i] === newArr[i-1])) {
         finalArr.push(newArr[i]);
       }
      }
      return finalArr;
    }

    对于希望将具有重复元素的数组展平为一个唯一数组的任何人:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function flattenUniq(arrays) {
      var args = Array.prototype.slice.call(arguments);

      var array = [].concat.apply([], args)

      var result = array.reduce(function(prev, curr){
        if (prev.indexOf(curr) < 0) prev.push(curr);
        return prev;
      },[]);

      return result;
    }

    以下脚本返回只包含唯一值的新数组。它适用于字符串和数字。不需要额外的库,只需要普通的JS。

    浏览器支持:

    1
    2
    Feature Chrome  Firefox (Gecko)     Internet Explorer   Opera   Safari
    Basic support   (Yes)   1.5 (1.8)   9                   (Yes)   (Yes)

    https://jsfiddle.net/fzmcgcxv/3/

    1
    2
    3
    4
    5
    var duplicates = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl","Mike","Mike","Nancy","Carl"];
    var unique = duplicates.filter(function(elem, pos) {
        return duplicates.indexOf(elem) == pos;
      });
    alert(unique);


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    function arrayDuplicateRemove(arr){
        var c = 0;
        var tempArray = [];
        console.log(arr);
        arr.sort();
        console.log(arr);
        for (var i = arr.length - 1; i >= 0; i--) {
            if(arr[i] != tempArray[c-1]){
                tempArray.push(arr[i])
                c++;
            }
        };
        console.log(tempArray);
        tempArray.sort();
        console.log(tempArray);
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    const numbers = [1, 1, 2, 3, 4, 4];

    function unique(array) {
      return array.reduce((a,b) => {
        let isIn = a.find(element => {
            return element === b;
        });
        if(!isIn){
          a.push(b);
        }
        return a;
      },[]);
    }

    let ret = unique(numbers); // [1, 2, 3, 4]

    使用reduce和find的方法。


    另一种不需要编写大量代码的方法是使用es5 Object.keys方法:

    1
    2
    3
    4
    5
    6
    var arrayWithDuplicates = ['a','b','c','d','a','c'],
        deduper = {};
    arrayWithDuplicates.forEach(function (item) {
        deduper[item] = null;
    });
    var dedupedArray = Object.keys(deduper); // ["a","b","c","d"]

    在函数中提取

    1
    2
    3
    4
    5
    6
    7
    function removeDuplicates (arr) {
        var deduper = {}
        arr.forEach(function (item) {
            deduper[item] = null;
        });
        return Object.keys(deduper);
    }


    嵌套循环方法,用于删除数组中的重复项并保留元素的原始顺序。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    var array = [1, 3, 2, 1, [5], 2, [4]]; // INPUT

    var element = 0;
    var decrement = array.length - 1;
    while(element < array.length) {
      while(element < decrement) {
        if (array[element] === array[decrement]) {
          array.splice(decrement, 1);
          decrement--;
        } else {
          decrement--;
        }
      }
      decrement = array.length - 1;
      element++;
    }

    console.log(array);// [1, 3, 2, [5], [4]]

    说明:内部循环将数组的第一个元素与以最高索引处的元素开头的所有其他元素进行比较。向第一个元素递减时,将从数组中拼接一个副本。

    当内部循环完成时,外部循环将增加到下一个元素进行比较,并重置数组的新长度。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    var lines = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
    var uniqueNames = [];

    for(var i=0;i<lines.length;i++)
    {
        if(uniqueNames.indexOf(lines[i]) == -1)
            uniqueNames.push(lines[i]);
    }
    if(uniqueNames.indexOf(uniqueNames[uniqueNames.length-1])!= -1)
        uniqueNames.pop();
    for(var i=0;i<uniqueNames.length;i++)
    {
        document.write(uniqueNames[i]);
          document.write("<br/>");
    }


    这里是另一种使用jquery的方法,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function uniqueArray(array){
      if ($.isArray(array)){
        var dupes = {}; var len, i;
        for (i=0,len=array.length;i<len;i++){
          var test = array[i].toString();
          if (dupes[test]) { array.splice(i,1); len--; i--; } else { dupes[test] = true; }
        }
      }
      else {
        if (window.console) console.log('Not passing an array to uniqueArray, returning whatever you sent it - not filtered!');
          return(array);
      }
      return(array);
    }

    作者:威廉·斯基德莫尔


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function removeDuplicates(inputArray) {
                var outputArray=new Array();

                if(inputArray.length>0){
                    jQuery.each(inputArray, function(index, value) {
                        if(jQuery.inArray(value, outputArray) == -1){
                            outputArray.push(value);
                        }
                    });
                }          
                return outputArray;
            }

    这个解决方案使用一个新的数组,并在函数内部使用一个对象映射。它所做的就是循环遍历原始数组,并将每个整数添加到对象映射中。

    1
    `if (!unique[int])`

    捕获此项,因为对象上已存在具有相同编号的键属性。因此,跳过该数字,不允许将其推入新数组。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
        function removeRepeats(ints) {
          var unique = {}
          var newInts = []

          for (var i = 0; i < ints.length; i++) {
            var int = ints[i]

            if (!unique[int]) {
              unique[int] = 1
              newInts.push(int)
            }
          }
          return newInts
        }

        var example = [100, 100, 100, 100, 500]
        console.log(removeRepeats(example)) // prints [100, 500]

    Alinks是一个简单的javascript数组对象。如果在元素之前存在任何元素,则索引显示删除了重复记录。我重复一遍取消所有的重复。一个通道数组取消更多记录。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var srt_ = 0;
    var pos_ = 0;
    do {
        var srt_ = 0;
        for (var i in aLinks) {
            pos_ = aLinks.indexOf(aLinks[i].valueOf(), 0);
            if (pos_ < i) {
                delete aLinks[i];
                srt_++;
            }
        }
    } while (srt_ != 0);

    如果不想包括整个库,可以使用此选项添加任何数组都可以使用的方法:

    1
    2
    3
    4
    5
    6
    7
    8
    Array.prototype.uniq = function uniq() {
      return this.reduce(function(accum, cur) {
        if (accum.indexOf(cur) === -1) accum.push(cur);
        return accum;
      }, [] );
    }

    ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"].uniq()


    如果您自己创建数组,您可以在插入数据时进行检查,从而为自己保存一个循环和额外的唯一过滤器;

    1
    2
    3
    4
    5
    6
    7
    var values = [];
    $.each(collection, function() {
        var x = $(this).value;
        if (!$.inArray(x, values)) {
            values.push(x);
        }
    });


    普通的JS解决方案具有O(n)的复杂性(对于这个问题来说,可能是最快的)。如果需要,修改hashfunction以区分对象(例如1和"1")。第一个解决方案避免隐藏循环(在数组提供的函数中很常见)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    var dedupe = function(a)
    {
        var hash={},ret=[];
        var hashFunction = function(v) { return""+v; };
        var collect = function(h)
        {
            if(hash.hasOwnProperty(hashFunction(h)) == false) // O(1)
            {
                hash[hashFunction(h)]=1;
                ret.push(h); // should be O(1) for Arrays
                return;
            }
        };

        for(var i=0; i<a.length; i++) // this is a loop: O(n)
            collect(a[i]);
        //OR: a.forEach(collect); // this is a loop: O(n)

        return ret;
    }

    var dedupe = function(a)
    {
        var hash={};
        var isdupe = function(h)
        {
            if(hash.hasOwnProperty(h) == false) // O(1)
            {
                hash[h]=1;
                return true;
            }

            return false;
        };

        return a.filter(isdupe); // this is a loop: O(n)
    }

    使用快捷方便


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function removeDuplicates (array) {
      var sorted = array.slice().sort()
      var result = []

      sorted.forEach((item, index) => {
        if (sorted[index + 1] !== item) {
          result.push(item)
        }
      })
      return result
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var duplicates = function(arr){
         var sorted = arr.sort();
       var dup = [];
       for(var i=0; i<sorted.length; i++){
            var rest  = sorted.slice(i+1); //slice the rest of array
           if(rest.indexOf(sorted[i]) > -1){//do indexOf
                if(dup.indexOf(sorted[i]) == -1)    
             dup.push(sorted[i]);//store it in another arr
          }
       }
       console.log(dup);
    }

    duplicates(["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"]);

    移除字符串重复项的最简单方法是使用关联数组,然后迭代关联数组,使列表/数组返回。

    如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    var toHash = [];
    var toList = [];

    // add from ur data list to hash
    $(data.pointsToList).each(function(index, Element) {
        toHash[Element.nameTo]= Element.nameTo;
    });

    // now convert hash to array
    // don't forget the"hasownproperty" else u will get random results
    for (var key in toHash)  {
        if (toHash.hasOwnProperty(key)) {
          toList.push(toHash[key]);
       }
    }

    喂,现在复制品不见了!


    删除重复项的最简单方法是执行for循环,比较不相同的元素并将它们推入新数组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     var array = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];

     var removeDublicate = function(arr){
     var result = []
     var sort_arr = arr.sort() //=> optional
     for (var i = 0; i < arr.length; i++) {
            if(arr[ i + 1] !== arr[i] ){
                result.push(arr[i])
            }
     };
      return result
    }  
    console.log(removeDublicate(array))
    ==>  ["Adam","Carl","Jenny","Matt","Mike","Nancy"]

    我知道我有点晚了,但这里有另一个选择使用jinqjs

    见小提琴

    1
    var result = jinqJs().from(["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"]).distinct().select();

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    var uniqueCompnies = function(companyArray) {
        var arrayUniqueCompnies = [],
            found, x, y;

        for (x = 0; x < companyArray.length; x++) {
            found = undefined;
            for (y = 0; y < arrayUniqueCompnies.length; y++) {
                if (companyArray[x] === arrayUniqueCompnies[y]) {
                    found = true;
                    break;
                }
            }

            if ( ! found) {
                arrayUniqueCompnies.push(companyArray[x]);
            }
        }

        return arrayUniqueCompnies;
    }

    var arr = [
       "Adobe Systems Incorporated",
       "IBX",
       "IBX",
       "BlackRock, Inc.",
       "BlackRock, Inc.",
    ];


    ES2015,1-liner,与map很好地连锁,但仅适用于整数:

    1
    [1, 4, 1].sort().filter((current, next) => current !== next)

    [1, 4]