从JS数组中删除重复值

kra*_*n88 1225 javascript arrays unique duplicates

我有一个非常简单的JavaScript数组,可能包含也可能不包含重复项.

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
Run Code Online (Sandbox Code Playgroud)

我需要删除重复项并将唯一值放在一个新数组中.

我可以指出我尝试过的所有代码,但我认为它没用,因为它们不起作用.我也接受jQuery解决方案.

类似的问题:

geo*_*org 3187

tldr

uniq = [...new Set(array)];
Run Code Online (Sandbox Code Playgroud)

===

"聪明",但天真的方式

uniqueArray = a.filter(function(item, pos) {
    return a.indexOf(item) == pos;
})
Run Code Online (Sandbox Code Playgroud)

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

使用过滤器回调的第3个("this array")参数,我们可以避免数组变量的闭包:

uniqueArray = a.filter(function(item, pos, self) {
    return self.indexOf(item) == pos;
})
Run Code Online (Sandbox Code Playgroud)

虽然简洁,但该算法对于大型阵列(二次时间)并不是特别有效.

哈希表来救援

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

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

  • 由于散列键只能是Javascript中的字符串,因此该代码不区分数字和"数字字符串".也就是说,uniq([1,"1"])只会回归[1]
  • 出于同样的原因,所有对象都被认为是相同的:uniq([{foo:1},{foo:2}])将只返回[{foo:1}].

也就是说,如果你的数组只包含基元并且你不关心类型(例如它总是数字),这个解决方案是最佳的.

两个世界中最好的

通用解决方案结合了两种方法:它使用基元的哈希查找和对象的线性搜索.

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);
    });
}
Run Code Online (Sandbox Code Playgroud)

排序| uniq的

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

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

同样,这不适用于对象(因为所有对象都相同sort).另外,我们默默地改变原始阵列作为副作用 - 不好!但是,如果您的输入已经排序,则可以采用此方法(只需sort从上面删除).

独一无二......

有时需要根据除了相等之外的某些标准来统一列表,例如,过滤掉不同的对象,但共享一些属性.这可以通过传递回调来优雅地完成.此"键"回调应用于每个元素,并删除具有相同"键"的元素.由于key预计会返回一个原语,哈希表在这里工作正常:

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

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

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

如果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);
    });
}
Run Code Online (Sandbox Code Playgroud)

在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);
    });
}
Run Code Online (Sandbox Code Playgroud)

或者Map:

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

它们也适用于非原始键.

图书馆

双方强调罗短跑提供Set方法.他们的算法基本上类似于上面的第一个片段,归结为:

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))
Run Code Online (Sandbox Code Playgroud)

这是二次方的,但还有很好的额外好处,比如包装原生Map,通过键(uniq用他们的说法)统一的能力,以及已经排序的数组的优化.

如果你正在使用jQuery并且在它之前没有一美元就无法忍受任何事情,那就是这样的:

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

这也是第一个片段的变体.

性能

函数调用在Javascript中很昂贵,因此上述解决方案虽然简洁,但效率不高.为获得最佳性能,请indexOf使用循环替换并删除其他函数调用:

  $.uniqArray = function(a) {
        return $.grep(a, function(item, pos) {
            return $.inArray(item, a) === pos;
        });
  }
Run Code Online (Sandbox Code Playgroud)

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

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;
}
Run Code Online (Sandbox Code Playgroud)

ES6

ES6提供了Set对象,它使事情变得更加容易:

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++)
    uniq_fast(a);
document.write('<br>uniq_fast, ms/loop: ' + (new Date() - d)/LOOPS)
Run Code Online (Sandbox Code Playgroud)

要么

function uniq(a) {
   return Array.from(new Set(a));
}
Run Code Online (Sandbox Code Playgroud)

请注意,与python不同,ES6集按插入顺序迭代,因此此代码保留原始数组的顺序.

但是,如果您需要一个包含唯一元素的数组,为什么不从一开始就使用集合?

发电机

基于生成器的"懒惰"版本iteratee可以在相同的基础上构建:

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

let uniq = a => [...new Set(a)];
Run Code Online (Sandbox Code Playgroud)

  • 过滤器和indexOf已经在ECMAScript 5中引入,所以这在旧的IE版本中不起作用(<9).如果你关心那些浏览器,你将不得不使用具有类似功能的库(jQuery,underscore.js等) (14认同)
  • 如果您希望页面在旧版浏览器中运行,可以使用@RoderickObrist (14认同)
  • 这是"O(n ^ 2)"解决方案,在大型阵列中运行速度非常慢...... (11认同)
  • 试试这个数组:`["toString","valueOf","failed"]`.`toString`和`valueOf`被完全剥离.使用`Object.create(null)`而不是`{}`. (9认同)
  • 任何人都知道Set转换解决方案与其他解决方案相比有多快? (4认同)
  • 注意:Set版本使用浅层比较,因此如果数组中的元素是对象,则将不起作用。 (3认同)
  • 我面对同样的问题而不是我认为最好避免重复.由于JS中没有类似于唯一List的内容,因此我决定只向对象添加属性; 如果你添加相同的两次,它将被覆盖并且重复的问题得到解决.不再需要过滤,我需要的只是1 for循环. (2认同)

Rom*_*aev 430

使用jQuery快速而肮脏:

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var uniqueNames = [];
$.each(names, function(i, el){
    if($.inArray(el, uniqueNames) === -1) uniqueNames.push(el);
});
Run Code Online (Sandbox Code Playgroud)

  • 对于那些不使用它的人来说,不介意非jquery答案 (142认同)
  • 这不是O(n ^ 2)吗? (32认同)
  • 我真的希望在 2020 年我们可以开始贬低 jQuery 和其他更过时的答案...... Stackoverflow 在这里开始显示出一些年龄...... (32认同)
  • 现在可以使用[`Array.prototype.filter`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)在没有jQuery的情况下完成 (10认同)
  • @JorgeFuentesGonzález因为Stackoverflow对jQuery有这个奇怪的东西 (9认同)
  • 由于这已被一位有信誉的人恢复为原始的“inArray”解决方案,因此我将再次提及:此解决方案是 O(n^2),因此效率低下。 (7认同)
  • @EyoelD表示也加入了jQuery解决方案.顺便说一句,我不知道为什么在非jQuery可以完成时使用jQuery解决方案. (4认同)
  • 我同意@NickSteele,但我发现如果你看投票而不是接受的答案,随着时间的推移,它确实会自然发生。最佳答案将倾向于顶部,因为旧的已弃用答案会被否决 (4认同)

Chr*_*ren 312

厌倦了看到所有使用for循环或jQuery的坏例子.Javascript现在拥有完美的工具:排序,映射和缩小.

Uniq在保持现有订单的同时减少

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;},[]);
Run Code Online (Sandbox Code Playgroud)

更快的uniq与排序

可能有更快的方法,但这个很不错.

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;},[]);
Run Code Online (Sandbox Code Playgroud)

2015年更新:ES6版本:

在ES6中,你有集合和传播,这使得删除所有重复项非常容易和高效:

var uniq = [ ...new Set(names) ]; // [ 'Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Carl' ]
Run Code Online (Sandbox Code Playgroud)

根据事件排序:

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

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)
Run Code Online (Sandbox Code Playgroud)

  • ES6版很漂亮. (9认同)
  • 这是完美的,因为与过滤器不同,它实际上允许对对象进行一些深度操作 (4认同)
  • 这个答案值得更多的赞成.只是美丽,只有OP要求的Javascript解决方案!谢谢!! (4认同)
  • 完美的答案,清洁和功能. (4认同)

Cas*_*all 100

Vanilla JS:使用类似Set的对象删除重复项

您始终可以尝试将其放入对象中,然后迭代其键:

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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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;

}
Run Code Online (Sandbox Code Playgroud)

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

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

function remove_duplicates_es6(arr) {
    let s = new Set(arr);
    let it = s.values();
    return Array.from(it);
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

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"]
Run Code Online (Sandbox Code Playgroud)

  • 在更新的浏览器中,你甚至可以做`var c = Object.keys(b)`.应该注意的是,这种方法只适用于字符串,但没关系,这就是原始问题所要求的. (5认同)

Bra*_*one 70

使用Underscore.js

它是一个包含许多用于操作数组的函数的库.

这与jQuery的tux以及Backbone.js的吊带相关.

_.uniq

_.uniq(array, [isSorted], [iterator]) 别名: unique
生成数组的无副本版本,使用===来测试对象相等性.如果您事先知道数组已排序,则为isSorted传递 true将运行更快的算法.如果要基于转换计算唯一项,请传递迭代器 函数.

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

alert(_.uniq(names, false));
Run Code Online (Sandbox Code Playgroud)

注意:Lo-Dash(下划线竞争者)也提供可比较的.uniq实现.

  • 遗憾的是,下划线不提供定义自定义相等功能的能力.他们允许的回调是'iteratee'函数,例如args(item,value,array). (2认同)

HBP*_*HBP 69

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

arr = arr.filter (function (value, index, array) { 
    return array.indexOf (value) == index;
});
Run Code Online (Sandbox Code Playgroud)

  • 小心解释它如何消除欺骗? (3认同)
  • 不幸的是,如果这是一个大数组,则性能会很差-arr.indexOf为O(n),这会使该算法为O(n ^ 2) (2认同)

Ash*_*Jha 55

你可以在JavaScript中,在方法的第二个 - index - 参数的帮助下完成它filter:

var a = [2,3,4,5,5,4];
a.filter(function(value, index){ return a.indexOf(value) == index });
Run Code Online (Sandbox Code Playgroud)

或者简而言之

a.filter((v,i) => a.indexOf(v) == i)
Run Code Online (Sandbox Code Playgroud)

  • @Hitmands你正在比较,我从左边比较.没有其他的 . (5认同)

Jon*_*a33 48

一条线:

let names = ['Mike','Matt','Nancy','Adam','Jenny','Nancy','Carl', 'Nancy'];
let dup = [...new Set(names)];
console.log(dup);
Run Code Online (Sandbox Code Playgroud)

  • 最佳答案,如果您使用的是ES6 (3认同)

Ivo*_*Ivo 34

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

vals.sort().reduce(function(a, b){ if (b != a[0]) a.unshift(b); return a }, [])
Run Code Online (Sandbox Code Playgroud)

就像我在其他例子中看到的那样,在reduce函数中slice也没有必要indexOf!然而,将它与过滤器功能一起使用是有意义的:

vals.filter(function(v, i, a){ return i == a.indexOf(v) })
Run Code Online (Sandbox Code Playgroud)

另一种已经在少数浏览器上运行的ES6(2015)方法是:

Array.from(new Set(vals))
Run Code Online (Sandbox Code Playgroud)

甚至使用传播运营商:

[...new Set(vals)]
Run Code Online (Sandbox Code Playgroud)

干杯!


Sum*_*shi 31

使用Array.filter()这样的

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);
Run Code Online (Sandbox Code Playgroud)

这可以在ES6中缩短到

actualArr.filter((item,index,self) => self.indexOf(item)==index);
Run Code Online (Sandbox Code Playgroud)

是很好的解释Array.filter()

  • 当数组是数组数组时不起作用 (2认同)

Juh*_*han 20

去这个:

var uniqueArray = duplicateArray.filter(function(elem, pos) {
    return duplicateArray.indexOf(elem) == pos;
}); 
Run Code Online (Sandbox Code Playgroud)

现在uniqueArray不包含重复项.


Dek*_*eke 19

最简单的一个我到目前为止遇到过.在es6.

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

 var noDupe = Array.from(new Set(names))
Run Code Online (Sandbox Code Playgroud)

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set


Red*_*edu 17

我已经在其他一些问题上对dupe删除进行了详细的比较,但注意到这是我想在这里分享它的真实地方.

我相信这是最好的方法

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);
Run Code Online (Sandbox Code Playgroud)

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

  • 在firefox v47中减少&lut:14.85ms vs filter&indexOf:2836ms
  • 在chrome v51中减少&lut:23.90ms vs filter&indexOf:1066ms

好吧到目前为止一切都那么好.但是这次以ES6风格正确地做到了.它看起来很酷..!但截至目前,它对抗强大的解决方案将如何表现对我来说是一个谜.让我们先看看代码,然后对它进行基准测试.

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);
Run Code Online (Sandbox Code Playgroud)

哇那简短..!但是性能怎么样?这很漂亮......由于过滤器/ indexOf的重量超过了我们的肩膀,我现在可以在0..99999范围内测试一组1M的正整数随机项,以获得10次连续测试的平均值.我可以说这次是一场真正的比赛.亲自看看结果:)

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");
Run Code Online (Sandbox Code Playgroud)

你会用哪一个..?好吧不是那么快......!不要被欺骗.地图处于位移状态.现在看......在上述所有情况下,我们填充一个大小为n的数组,其数量为范围<n.我的意思是我们有一个100的数组,我们填充随机数0..9所以有确定的重复和"几乎"肯定每个数字都有重复.如果我们用随机数0..9999填充大小为100的数组怎么样.我们现在看看家里的地图播放.这次是100K项目的数组,但是随机数范围是0..100M.我们将进行100次连续测试以平均结果.好的,让我们看看赌注..!< - 没有错字

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");
Run Code Online (Sandbox Code Playgroud)

现在这是Map()的壮观复出..!现在可以在你想要删除欺骗时做出更好的决定.

好吧,我们现在都很高兴.但是主角一直伴随着掌声.我相信你们有些人想知道Set对象会做什么.既然我们对ES6持开放态度并且我们知道Map是之前游戏的赢家,那么让我们将Map与Set进行比较.这次典型的皇马对阵巴塞罗那比赛......或者是这样?让我们看看谁将赢得el classico :)

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");
Run Code Online (Sandbox Code Playgroud)

哇..男人..!出乎意料的是,它根本没有成为一个经典的.更像是巴塞罗那足球俱乐部对阵CA Osasuna :))


ShA*_*KiR 17

解决方案1

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;
}
Run Code Online (Sandbox Code Playgroud)

解决方案2(使用Set)

Array.prototype.unique = function() {
    return Array.from(new Set(this));
}
Run Code Online (Sandbox Code Playgroud)

测试

var x=[1,2,3,3,2,1];
x.unique() //[1,2,3]
Run Code Online (Sandbox Code Playgroud)

性能

当我在chrome中测试两个实现(有和没有Set)的性能时,我发现使用Set的那个实现要快得多!

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");
Run Code Online (Sandbox Code Playgroud)

  • Upvote使用Set.我不知道性能比较 (3认同)

Lev*_*evi 16

以下内容比列出的jQuery方法快80%以上(参见下面的测试).这是几年前类似问题的答案.如果我遇到最初提出它的人,我会发布信用证.纯JS.

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;
Run Code Online (Sandbox Code Playgroud)

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

  • 测试似乎没有使用数组??? 我添加了(又一个)似乎在不同浏览器上一直快速的(参见http://jsperf.com/remove-duplicate-array-tests/10):for(var n = array.length,result = [array [n--]],i; n--;){i = array [n]; if(!(i in result))result.push(i); 返回结果; (2认同)

gil*_*ly3 15

最顶层的答案有复杂性O(n²),但这可以O(n)通过使用对象作为哈希来完成:

function getDistinctArray(arr) {
    var dups = {};
    return arr.filter(function(el) {
        var hash = el.valueOf();
        var isDup = dups[hash];
        dups[hash] = true;
        return !isDup;
    });
}
Run Code Online (Sandbox Code Playgroud)

这适用于字符串,数字和日期.如果您的数组包含复杂对象(即,必须与它们进行比较"[object Object]"),则上述解决方案将不起作用.您可以O(n)通过在对象本身上设置标志来获取对象的实现:

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;
}
Run Code Online (Sandbox Code Playgroud)

  • @Tushar - 此页面上的所有答案都不会从 [your gist](https://gist.github.com/tusharmath/6514784) 中的数组中删除任何重复项。 (2认同)

dre*_*721 14

这是一个简单的问题答案.

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]);
        }
    }
Run Code Online (Sandbox Code Playgroud)


Ori*_*iol 14

在ECMAScript 6(又名ECMAScript 2015)中,Set可用于过滤掉重复项.然后可以使用spread运算符将其转换回数组.

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"],
    unique = [...new Set(names)];
Run Code Online (Sandbox Code Playgroud)


Joh*_*ers 10

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

代码示例:

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 = '<pre>' + JSON.stringify(filteredData, null, '\t') +  '</pre>';
Run Code Online (Sandbox Code Playgroud)

另见这个小提琴.


ofi*_*hai 9

所以选项是:

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]
Run Code Online (Sandbox Code Playgroud)


Moh*_*med 9

这里是没有任何特殊库的简单方法是特殊功能,

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("\n Unique name list:"+get_uniq.length, get_uniq)
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述


csu*_*cat 8

除了比当前答案(减去未来的ES6)更简单,更简洁的解决方案之外,我对此进行了测试,并且速度也快得多:

var uniqueArray = dupeArray.filter(function(item, i, self){
  return self.lastIndexOf(item) == i;
});
Run Code Online (Sandbox Code Playgroud)

一个警告:在IE9中添加了Array.lastIndexOf(),所以如果你需要低于那个,你需要寻找其他地方.


小智 7

通用功能方法

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

// 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) );
Run Code Online (Sandbox Code Playgroud)

我们可以很容易地uniqueunqiueBy使用Sets 得到或使用更快的实现:

const unqiue = uniqueBy(eq);

// const unique = xs => Array.from(new Set(xs));
Run Code Online (Sandbox Code Playgroud)

这种方法的好处:

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

性能注意事项

uniqueBy 并不像使用循环的命令式实现那么快,但由于它的通用性,它更具表现力.

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

内存消耗和垃圾收集

uniqueBy利用push(x) (acc)隐藏在其体内的突变().它重用累加器而不是在每次迭代后丢弃它.这减少了内存消耗和GC压力.由于这种副作用包含在函数内部,因此外部的所有内容都保持纯净.


bod*_*ich 7

在任何地方(甚至在PhotoshopScript中)代码中理解和工作都非常简单.核实!

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"]
Run Code Online (Sandbox Code Playgroud)


小智 5

对 thg435 使用自定义比较器的优秀答案进行了轻微修改:

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);
    });
}
Run Code Online (Sandbox Code Playgroud)


Gib*_*boK 5

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

浏览器支持:

Feature Chrome  Firefox (Gecko)     Internet Explorer   Opera   Safari
Basic support   (Yes)   1.5 (1.8)   9                   (Yes)   (Yes)
Run Code Online (Sandbox Code Playgroud)

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

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);
Run Code Online (Sandbox Code Playgroud)


jet*_*com 5

如果您有任何机会使用

D3.js

你可以做

d3.set(["foo", "bar", "foo", "baz"]).values() ==> ["foo", "bar", "baz"]
Run Code Online (Sandbox Code Playgroud)

https://github.com/mbostock/d3/wiki/Arrays#set_values


MBJ*_*BJH 5

for (i=0; i<originalArray.length; i++) {  
    if (!newArray.includes(originalArray[i])) {
        newArray.push(originalArray[i]); 
    }
}
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

1303344 次

最近记录:

6 年,1 月 前