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

111

给出一个 ['0','1','1','2','3','3','3'] 数组,结果应为 ['0','1','2','3']


6
是的,jQuery解决了所有问题。 - Michael J. Calkins
1
你可以使用像underscore.js这样的实用库http://underscorejs.org/#uniq来执行这些“简单”的操作。 - Daan
36
重复提出相同问题的讽刺啊。 - Lucio Paiva
@LucioPaiva 这个重复问题的数组在哪里??? - dewd
17个回答

257

编辑过的

ES6解决方案:

[...new Set(a)];

替代选择:

Array.from(new Set(a));

旧响应。O(n^2)(不要在大型数组上使用它!)

var arrayUnique = function(a) {
    return a.reduce(function(p, c) {
        if (p.indexOf(c) < 0) p.push(c);
        return p;
    }, []);
};

31
胡说八道!你确实需要使用分号!http://jsfiddle.net/bTNc2/ - jack
107
现在是2014年,所以我们需要再次使用分号。 - tacone
56
现在是2015年,所以我们不再需要使用分号了。 - Mark Knol
49
2016年我们是否需要使用分号? - Dumbo
28
现在已经是2016年的中间时期了,总的来说,这一年可以说是“分号是可选的,但强烈建议使用”的一年。 - trex005
显示剩余23条评论

52

如果你想保持秩序:

arr = arr.reverse().filter(function (e, i, arr) {
    return arr.indexOf(e, i+1) === -1;
}).reverse();

由于没有内置的反向indexOf,我将数组反转,过滤掉重复项,然后重新反转它。

过滤函数查找当前索引之后的任何元素出现(在原始数组中之前)。如果找到一个,就会丢弃此元素。

编辑:

或者,您可以使用lastIndexOf(如果您不关心顺序):

arr = arr.filter(function (e, i, arr) {
    return arr.lastIndexOf(e) === i;
});

这将保留唯一的元素,但只保留最后一次出现的元素。这意味着 ['0','1','0'] 变成了 ['1','0'],而不是 ['0','1']。


2
+1 for golfing。不幸的是,直接使用for循环似乎表现更好JSPerf。该死的函数调用太昂贵了。 - merv
@merv - OP没有提到性能问题,所以我就发挥了一下创意。代码很简单,对吧? - beatgammit
1
不错!我更喜欢[1,2,3,1,1].filter(function(elem,idx,arr){ return arr.indexOf(elem) >= idx; });,因为它更加直观易懂。 - Benjamin Gruenbaum
太棒了,@tjameson! - inorganik
可以从顶部答案的评论中看出,没有维护顺序。 - Aaron Cicali
显示剩余5条评论

26

这是一个数组原型函数:

Array.prototype.unique = function() {
    var unique = [];
    for (var i = 0; i < this.length; i++) {
        if (unique.indexOf(this[i]) == -1) {
            unique.push(this[i]);
        }
    }
    return unique;
};

2
这是最容易阅读的代码 XD。 - Huei Tan

14

使用 underscorejs

_.uniq([1, 2, 1, 3, 1, 4]); //=> [1, 2, 3, 4]

这对于数组的数组可以工作吗? - vini

13

现在是2014年,时间复杂度仍然很重要!

array.filter(function() {
  var seen = {};
  return function(element, index, array) {
    return !(element in seen) && (seen[element] = 1);
  };
}());

http://jsperf.com/array-filter-unique/13


9
function array_unique(arr) {
    var result = [];
    for (var i = 0; i < arr.length; i++) {
        if (result.indexOf(arr[i]) == -1) {
            result.push(arr[i]);
        }
    }
    return result;
}

这不是一个内置函数。如果产品列表中不包含该项,则将其添加到唯一列表中并返回唯一列表。


1
可能需要提醒一下,这在IE8或以下版本中无法正常工作。 - Stephen
为什么在IE8中不能正常工作? - n s
1
可能是因为缺少了一个右括号,应该这样写: result.push(arr[i]); - Jake Rowsell

6

好的!不用谢!

Array.prototype.unique = function()
{
    var tmp = {}, out = [];
    for(var i = 0, n = this.length; i < n; ++i)
    {
        if(!tmp[this[i]]) { tmp[this[i]] = true; out.push(this[i]); }
    }
    return out;
}

var a = [1,2,2,7,4,1,'a',0,6,9,'a'];
var b = a.unique();
alert(a);
alert(b);

3

你可以在这里找到各种不同的数组去重实现方式:

http://jsperf.com/distinct-hash-vs-comparison/12

http://jsperf.com/array-unique-functional

我更喜欢函数式编程风格,例如:

var arr = ['lol', 1, 'fdgdfg', 'lol', 'dfgfg', 'car', 1, 'car', 'a', 'blah', 'b', 'c', 'd', '0', '1', '1', '2', '3', '3', '3', 'crazy', 'moot', 'car', 'lol', 1, 'fdgdfg', 'lol', 'dfgfg', 'car', 1, 'car', 'a', 'blah', 'b', 'c', 'd', '0', '1', '1', '2', '3', '3', '3', 'crazy', 'moot', 'car', 'lol', 1, 'fdgdfg'];

var newarr = arr.reduce(function (prev, cur) {
    //console.log(prev, cur);
    if (prev.indexOf(cur) < 0) prev.push(cur);
    return prev;
}, []);

var secarr = arr.filter(function(element, index, array){
    //console.log(element, array.indexOf(element), index);
    return array.indexOf(element) >= index;
});

//reverses the order
var thirdarr = arr.filter(function (e, i, arr) {
    //console.log(e, arr.lastIndexOf(e), i);
    return arr.lastIndexOf(e) === i;
});

console.log(newarr);
console.log(secarr);
console.log(thirdarr);

我认为大部分提出的解决方案都存在一个潜在问题,即它们需要计算密集型操作。它们至少需要O(n^2)的操作(由于每次迭代调用indexOf),因此在使用小数组时很好,但对于大数组则不适用。我在这里发表此评论是因为有一个性能测试链接,而且我认为由于数据太小而具有误导性。 - terrinecold
这里是一篇更好的性能研究:http://www.shamasis.net/2009/09/fast-algorithm-to-find-unique-items-in-javascript-array/ - terrinecold
@terrinecold 很棒,你应该发布一个参考答案。如果JavaScript编译器/解释器可以自动优化它就太好了。 - CMCDragonkai
@terrinecold 等等,你链接中的方法与我提供的比较中的方法是一样的,并不总是更快。我猜可能对于更大的数组会更快。 - CMCDragonkai

1

以下是从数组中去除重复值的方法。

function ArrNoDupe(dupArray) {
   var temp = {};
    for (var i = 0; i < dupArray.length; i++) {
         temp[dupArray[i]] = true;
         var uniqueArray = [];
       for (var k in temp)
           uniqueArray.push(k);
 return uniqueArray;
    }
}

1
function array_unique(nav_array) {
    nav_array = nav_array.sort(function (a, b) { return a*1 - b*1; });      
    var ret = [nav_array[0]];       
    // Start loop at 1 as element 0 can never be a duplicate
    for (var i = 1; i < nav_array.length; i++) { 
        if (nav_array[i-1] !== nav_array[i]) {              
            ret.push(nav_array[i]);             
        }       
    }
    return ret;     
}

不是一个称为array_unique的好实现,因为你依赖于它是数值类型。即使对于数字数组去重,我认为parseInt会是更好的方式(但我可能错了)。 - Raekye

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接