Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
306 views
in Technique[技术] by (71.8m points)

javascript - 从JS数组中删除重复的值[duplicate](Remove duplicate values from JS array [duplicate])

I have a very simple JavaScript array that may or may not contain duplicates.

(我有一个非常简单的JavaScript数组,其中可能包含重复项,也可能不包含重复项。)

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

I need to remove the duplicates and put the unique values in a new array.

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

I could point to all the codes that I've tried but I think it's useless because they don't work.

(我可以指出我尝试过的所有代码,但是我认为这没用,因为它们不起作用。)

I accept jQuery solutions too.

(我也接受jQuery解决方案。)

Similar question:(类似的问题:)

  ask by kramden88 translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

TL;DR(TL; DR)

Using the Set constructor and the spread syntax :

(使用Set构造函数和spread语法 :)

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

"Smart" but na?ve way(“聪明”但幼稚的方式)

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

Basically, we iterate over the array and, for each element, check if the first position of this element in the array is equal to the current position.

(基本上,我们遍历数组,并针对每个元素检查此元素在数组中的第一个位置是否等于当前位置。)

Obviously, these two positions are different for duplicate elements.

(显然,对于重复元素,这两个位置是不同的。)

Using the 3rd ("this array") parameter of the filter callback we can avoid a closure of the array variable:

(使用过滤器回调的第3个(“此数组”)参数,我们可以避免关闭数组变量:)

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

Although concise, this algorithm is not particularly efficient for large arrays (quadratic time).

(尽管简洁,但是该算法对于大型数组(二次时间)并不是特别有效。)

Hashtables to the rescue(救援的哈希表)

function uniq(a) {
    var seen = {};
    return a.filter(function(item) {
        return seen.hasOwnProperty(item) ? false : (seen[item] = true);
    });
}

This is how it's usually done.

(通常是这样的。)

The idea is to place each element in a hashtable and then check for its presence instantly.

(想法是将每个元素放在哈希表中,然后立即检查其是否存在。)

This gives us linear time, but has at least two drawbacks:

(这给了我们线性的时间,但是至少有两个缺点:)

  • since hash keys can only be strings in JavaScript, this code doesn't distinguish numbers and "numeric strings".

    (由于哈希键只能是JavaScript中的字符串,因此此代码无法区分数字和“数字字符串”。)

    That is, uniq([1,"1"]) will return just [1]

    (也就是说, uniq([1,"1"])将只返回[1])

  • for the same reason, all objects will be considered equal: uniq([{foo:1},{foo:2}]) will return just [{foo:1}] .

    (出于相同的原因,所有对象都将被视为相等: uniq([{foo:1},{foo:2}])仅返回[{foo:1}] 。)

That said, if your arrays contain only primitives and you don't care about types (eg it's always numbers), this solution is optimal.

(就是说,如果您的数组仅包含基元并且您不关心类型(例如,它始终是数字),则此解决方案是最佳的。)

The best from two worlds(来自两个世界的最好)

A universal solution combines both approaches: it uses hash lookups for primitives and linear search for objects.

(通用解决方案结合了这两种方法:它使用哈希查找原始图元和线性搜索对象。)

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

sort |(排序) uniq(优衣库)

Another option is to sort the array first, and then remove each element equal to the preceding one:

(另一种选择是先对数组排序,然后删除等于前一个元素的每个元素:)

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

Again, this doesn't work with objects (because all objects are equal for sort ).

(同样,这不适用于对象(因为所有对象的sort相等)。)

Additionally, we silently change the original array as a side effect - not good!

(另外,我们无声地更改了原始数组作为副作用-不好!)

However, if your input is already sorted, this is the way to go (just remove sort from the above).

(但是,如果您的输入已经排序,这就是方法(只需从上面删除sort )。)

Unique by...(独一无二...)

Sometimes it's desired to uniquify a list based on some criteria other than just equality, for example, to filter out objects that are different, but share some property.

(有时,我们希望根据某些条件(不仅仅是相等性)来对列表进行唯一化,例如,过滤出不同但共享某些属性的对象。)

This can be done elegantly by passing a callback.

(可以通过传递回调来优雅地完成此操作。)

This "key" callback is applied to each element, and elements with equal "keys" are removed.

(此“键”回调将应用于每个元素,并且删除具有相等“键”的元素。)

Since key is expected to return a primitive, hash table will work fine here:

(由于预计key将返回原语,因此哈希表在这里可以正常工作:)

function uniqBy(a, key) {
    var seen = {};
    return a.filter(function(item) {
        var k = key(item);
        return seen.hasOwnProperty(k) ? false : (seen[k] = true);
    })
}

A particularly useful key() is JSON.stringify which will remove objects that are physically different, but "look" the same:

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

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

If the key is not primitive, you have to resort to the linear search:

(如果key不是原始key则必须诉诸线性搜索:)

function uniqBy(a, key) {
    var index = [];
    return a.filter(function (item) {
        var k = key(item);
        return index.indexOf(k) >= 0 ? false : index.push(k);
    });
}

In ES6 you can use a Set :

(在ES6中,您可以使用Set :)

function uniqBy(a, key) {
    let seen = new Set();
    return a.filter(item => {
        let k = key(item);
        return seen.has(k) ? false : seen.add(k);
    });
}

or a Map :

(或Map :)

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

which both also work with non-primitive keys.

(两者也都可以与非原始键一起使用。)

First or last?(首先还是最后?)

When removing objects by a key, you might to want to keep the first of "equal" objects or the last one.

(通过键删除对象时,您可能想保留“相等”对象中的第一个或最后一个。)

Use the Set variant above to keep the first, and the Map to keep the last:

(使用上面的Set变量保留第一个变量,使用Map保留最后一个变量:)

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

Libraries(图书馆)

Both underscore and Lo-Dash provide uniq methods.

(下划线Lo-Dash均提供uniq方法。)

Their algorithms are basically similar to the first snippet above and boil down to this:

(他们的算法基本上类似于上面的第一个代码片段,归结为:)

var result = [];
a.forEach(function(item) {
     if(result.indexOf(item) < 0) {
         result.push(item);
     }
});

This is quadratic, but there are nice additional goodies, like wrapping native indexOf , ability to uniqify by a key ( iteratee in their parlance), and optimizations for already sorted arrays.

(这是二次方的,但是还有很多其他好处,例如包装本机indexOf ,按键进行唯一化的能力(用术语iteratee表示)以及对已排序数组的优化。)

If you're using jQuery and can't stand anything without a dollar before it, it goes like this:

(如果您使用的是jQuery,但在没有美元之前不能忍受任何事情,它会像这样:)

  $.uniqArray = function(a) {
        return $.grep(a, function(item, pos) {
            return $.inArray(item, a) === pos;
        });
  }

which is, again, a variation of the first snippet.

(再次是第一个代码段的变体。)

Performance(性能)

Function calls are expensive in JavaScript, therefore the above solutions, as concise as they are, are not particularly efficient.

(函数调用在JavaScript中非常昂贵,因此上述解决方案尽管非常简洁,但并不是特别有效。)

For maximal performance, replace filter with a loop and get rid of other function calls:

(为了获得最佳性能,请用循环替换filter并摆脱其他函数调用:)

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

This chunk of ugly code does the same as the snippet #3 above, but an order of magnitude faster (as of 2017 it's only twice as fast - JS core folks are doing a great job!)

(这段丑陋的代码与上面的代码段#3一样, 但是速度提高了一个数量级 (截至2017年,它的速度仅是后者的两倍-JS核心人员做得很好!))

 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('<br>uniq, ms/loop: ' + (new Date() - d)/LOOPS) var d = new Date(); for(var i = 0; i < LOOPS; i++) u

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...