从数组中获取所有唯一元素的jQuery函数?

74

jQuery.unique 可以让你获取数组的唯一元素,但文档说该函数主要用于内部使用,并且只能操作DOM元素。另一个SO(Stack Overflow)回答说 unique() 函数可以处理数字,但这种用法未必具备未来性,因为它没有在官方文档中明确说明。

考虑到这些限制,是否有一种“标准”的 jQuery 函数可以访问数组中唯一的值 - 具体而言是原始类型,如整数?(显然,我们可以使用 each() 函数构建循环,但我们是 jQuery 的新手,想知道是否有专门的 jQuery 函数可以实现这个功能。)


2
@未来读者 - 在查看Rubyrider的答案时,请注意可能会出现意外的结果 - 这不是一个安全或可靠的解决方案。 - Yuck
14个回答

132
您可以使用array.filter来返回每个不同值的第一个项目-

var a = [ 1, 5, 1, 6, 4, 5, 2, 5, 4, 3, 1, 2, 6, 6, 3, 3, 2, 4 ];

var unique = a.filter(function(itm, i, a) {
    return i == a.indexOf(itm);
});

console.log(unique);

如果支持IE8及以下版本是主要考虑因素,则不要使用不支持的filter方法。
否则,
if (!Array.prototype.filter) {
    Array.prototype.filter = function(fun, scope) {
        var T = this, A = [], i = 0, itm, L = T.length;
        if (typeof fun == 'function') {
            while(i < L) {
                if (i in T) {
                    itm = T[i];
                    if (fun.call(scope, itm, i, T)) A[A.length] = itm;
                }
                ++i;
            }
        }
        return A;
    }
}

5
警告:上面的代码在IE8及以下版本中无法正常工作,因为这些浏览器不支持Array.filterArray.indexOf。请查看我对此答案的修改版,以获取在支持jquery的浏览器中不会出现问题的代码。 - Dennis
1
支持从IE9开始,在https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/filter上获取更多信息。 - Organiccat
1
或者使用许多 EcmaScript 5 shims 之一,比如 Kriskowal 的这个 - superjos
2
@cwolves的答案应该被接受。这个答案和几乎所有其他答案都以O(n^2)的时间运行,如果你在一个大数组上尝试它,它将非常非常慢。 - Alex D
简单的回答,太棒了。 - bntzio
jquery.unique已被JQuery 3.0弃用,请使用uniqueSort()代替:https://api.jquery.com/jQuery.unique/ - Jonny

54

只需将此代码用作简单 JQuery 插件的基础即可。

$.extend({
    distinct : function(anArray) {
       var result = [];
       $.each(anArray, function(i,v){
           if ($.inArray(v, result) == -1) result.push(v);
       });
       return result;
    }
});

使用方法如下:

$.distinct([0,1,2,2,3]);

14
基于 @kennebec 的回答,但通过在数组周围使用 jQuery 包装器来提供缺失的 Array 函数 filter 和 indexOf,修复了 IE8 及以下版本的问题: $.makeArray() 封装可能并非绝对必需,但如果省略此封装并以其他方式 JSON.stringify 结果,则会得到奇怪的结果。
var a = [1,5,1,6,4,5,2,5,4,3,1,2,6,6,3,3,2,4];

// note: jQuery's filter params are opposite of javascript's native implementation :(
var unique = $.makeArray($(a).filter(function(i,itm){ 
    // note: 'index', not 'indexOf'
    return i == $(a).index(itm);
}));

// unique: [1, 5, 6, 4, 2, 3]

1
似乎不再起作用了,请将过滤器更改为:return i == $.inArray(itm, a); - Shadaez

10

我会使用underscore.js,它提供了一个uniq方法来实现你需要的功能。


7
    // for numbers
    a = [1,3,2,4,5,6,7,8, 1,1,4,5,6]
    $.unique(a)
    [7, 6, 1, 8, 3, 2, 5, 4]

    // for string
    a = ["a", "a", "b"]
    $.unique(a)
    ["b", "a"]

对于DOM元素,我想这里不需要举例了,因为您已经知道了!

这是一个实时示例的jsfiddle链接: http://jsfiddle.net/3BtMc/4/


7
但是文档中指出 $.unique() 只能用在 DOM 元素上!"对一个有重复元素的 DOM 元素数组进行排序,并去除重复元素。请注意,这仅适用于 DOM 元素数组,而非字符串或数字。" -- http://api.jquery.com/jquery.unique/ - Kat

6

遍历数组并在遇到它们时将项目推入哈希表中。对于每个新元素,交叉引用哈希表。

请注意,这仅适用于基元(字符串,数字,null,undefined,NaN)和一些序列化为相同内容的对象(函数,字符串,日期,可能是数组取决于内容)。此方法中的哈希表会发生碰撞,因为它们都序列化为相同的内容,例如 "[object Object]"。

Array.prototype.distinct = function(){
   var map = {}, out = [];

   for(var i=0, l=this.length; i<l; i++){
      if(map[this[i]]){ continue; }

      out.push(this[i]);
      map[this[i]] = 1;
   }

   return out;
}

您也可以使用jQuery.unique,没有任何理由不这样做。我唯一不喜欢的是它会破坏数组的顺序。如果您感兴趣,以下是精确的代码:

Sizzle.uniqueSort = function(results){
    if ( sortOrder ) {
        hasDuplicate = baseHasDuplicate;
        results.sort(sortOrder);

        if ( hasDuplicate ) {
            for ( var i = 1; i < results.length; i++ ) {
                if ( results[i] === results[i-1] ) {
                    results.splice(i--, 1);
                }
            }
        }
    }

    return results;
};

6

这是js1568的解决方案,经过修改可以适用于一些通用对象数组,比如:

 var genericObject=[
        {genProp:'this is a string',randomInt:10,isBoolean:false},
        {genProp:'this is another string',randomInt:20,isBoolean:false},
        {genProp:'this is a string',randomInt:10,isBoolean:true},
        {genProp:'this is another string',randomInt:30,isBoolean:false},
        {genProp:'this is a string',randomInt:40,isBoolean:true},
        {genProp:'i like strings',randomInt:60,isBoolean:true},
        {genProp:'this is a string',randomInt:70,isBoolean:true},
        {genProp:'this string is unique',randomInt:50,isBoolean:true},
        {genProp:'this is a string',randomInt:50,isBoolean:false},
        {genProp:'i like strings',randomInt:70,isBoolean:false}
    ]

它接受一个称为propertyName的参数,猜猜看! :)
  $.extend({
        distinctObj:function(obj,propertyName) {
            var result = [];
            $.each(obj,function(i,v){
                var prop=eval("v."+propertyName);
                if ($.inArray(prop, result) == -1) result.push(prop);
            });
            return result;
        }
    });

因此,如果您需要提取给定属性的唯一值列表,例如用于randomInt属性的值,请使用以下方法:

$.distinctObj(genericObject,'genProp');

它返回一个像这样的数组:

["this is a string", "this is another string", "i like strings", "this string is unique"] 

我真的很想知道我写了什么东西才会得到-1分。 - Dariozzo
1
我没有给你的回答点踩(我只是看到了这个回答),但我建议你将 eval("v."+propertyName) 改为 v[propertyName]。每次调用 eval 都会启动另一个编译器,对于循环来说,这是相当昂贵的。 - Shaz

6
保罗·爱尔兰(Paul Irish)有一种“鸭子打击”方法(参见示例2),可以修改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);

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

4

如果您不需要IE支持(Array.from在IE中不受支持),则可以使用纯JavaScript的现代解决方案。

您可以使用SetArray.from的组合。

const arr = [1, 1, 11, 2, 4, 2, 5, 3, 1];
const set = new Set(arr);
const uniqueArr = Array.from(set);

console.log(uniqueArr);

Set 对象可以存储任何类型的唯一值,包括原始值或对象引用。

Array.from() 方法可以从类似数组或可迭代对象创建一个新的数组实例。

同时,Array.from() 可以被扩展运算符替换。

const arr = [1, 1, 11, 2, 4, 2, 5, 3, 1];
const set = new Set(arr);
const uniqueArr = [...set];

console.log(uniqueArr);


1
变量 arr = [1, 1, 11, 2, 4, 2, 5, 3, 1]; [... new Set(arr)]; - Ashish Singh Rawat
@AshishSinghRawat 谢谢,已添加使用扩展运算符语法的选项。 - Vadim Ovchinnikov

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