ChatGPT解决这个技术问题 Extra ChatGPT

从 JS 数组中删除重复值 [重复]

这个问题在这里已经有了答案:Get all unique values in a JavaScript array (remove duplicates) (85 answers) 4 年前关闭。

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

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

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

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

类似的问题:

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

_.uniq(peoplenames) 解决了这个问题lodash.com/docs#uniq
@ConnorLeech 使用 lodash 很容易,但没有经过优化
最简单的方法(在我看来)是使用 Set 对象,它可以让您存储任何类型的唯一值。换句话说,Set 会自动为我们删除重复项。 const names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"]; let unique = [...new Set(names)]; console.log(unique); // 'Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Carl'
世界上有太多的麦克 - 为什么不删除它们?南希得到了这个。
在我的解决方案中,我在过滤之前对数据进行排序:` const result = data.sort().filter((v, idx, t) => idx==0 || v != t[idx-1]);

4
41 revs, 12 users 85%

TL;博士

使用 Set 构造函数和 spread syntax

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

“聪明”但幼稚的方式

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

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

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

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

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

哈希表来拯救

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}]。

也就是说,如果您的数组只包含原语并且您不关心类型(例如它始终是数字),那么这个解决方案是最佳的。

来自两个世界的最好的

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

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

排序 |独特的

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

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

同样,这不适用于对象(因为所有对象对于 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);
    })
}

一个特别有用的 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]]

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

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

Map

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

这两者也适用于非原始键。

第一个还是最后一个?

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

使用上面的 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))

图书馆

underscoreLo-Dash 都提供 uniq 方法。他们的算法基本上类似于上面的第一个片段,归结为:

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

这是二次的,但还有一些不错的附加功能,例如包装原生 indexOf、通过键唯一化的能力(用他们的说法是 iteratee)以及对已排序数组的优化。

如果你在使用 jQuery 并且在它前面没有一美元就无法忍受任何东西,它是这样的:

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

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

表现

JavaScript 中的函数调用很昂贵,因此上述解决方案虽然简洁,但并不是特别有效。为获得最佳性能,请将 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;
}

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

函数 uniq(a) { var seen = {}; return a.filter(function(item) { return seen.hasOwnProperty(item) ? false : (seen[item] = true); }); } function uniq_fast(a) { var seen = {};变种 = []; var len = a.length;变量 j = 0; for(var i = 0; i < len; i++) { var item = a[i];如果(看到[项目]!== 1){看到[项目] = 1;出[j++] = 项目; } } 返回; } ///// 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 = 新日期(); 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 对象,这让事情变得简单多了:

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

或者

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

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

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

发电机

uniq 的“惰性”、基于生成器的版本可以在相同的基础上构建:

从参数中获取下一个值

如果已经看过,请跳过它

否则,产生它并将其添加到一组已经看到的值

函数* uniqIter(a) { 让我们看到 = new Set(); for (let x of a) { if (!seen.has(x)) { seen.add(x);产量 x; } } } // 示例:function* randomsBelow(limit) { while (1) yield Math.floor(Math.random() * limit); } // 注意 randomsBelow 是无穷的 count = 20;限制 = 30; for (let r of uniqIter(randomsBelow(limit))) { console.log(r); if (--count === 0) break } // 读者练习:如果我们将 `limit` 设置为小于 `count` 会发生什么以及为什么


在 ECMAScript 5 中引入了 filter 和 indexOf,因此这在旧 IE 版本 (<9) 中不起作用。如果您关心这些浏览器,您将不得不使用具有类似功能的库(jQuery、underscore.js 等)
@RoderickObrist 如果您希望您的页面在旧版浏览器中工作,您可能会这样做
这是 O(n^2) 解决方案,它可以在大型阵列中运行非常缓慢...
试试这个数组:["toString", "valueOf", "failed"]toStringvalueOf 被完全剥离。使用 Object.create(null) 而不是 {}
与其他解决方案相比,任何人都知道 Set 转换解决方案有多快?
M
Martijn Pieters

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

不介意那些不使用它的人的非 jquery 答案
由于这已由知名人士恢复为原始 inArray 解决方案,因此我将再次提及:此解决方案是 O(n^2),因此效率低下。
我真的希望在 2020 年我们可以开始贬值 jQuery 和其他更过时的答案...... Stackoverflow 开始在这里显示一些年龄......
我同意@NickSteele,但如果您查看选票而不是接受的答案,我发现它确实会随着时间的推移自然发生。随着旧的弃用答案被否决,最佳答案将被排在首位
让 uniqueNames = names.filter((item, pos ,self) => self.indexOf(item) == pos);
C
Christian Landgren

厌倦了使用 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;},[]);

排序更快的 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;},[]);

2015 年更新:ES6 版本:

在 ES6 中,你有 Sets 和 Spread,这使得删除所有重复项变得非常容易和高效:

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

根据出现排序:

有人询问是否根据有多少个唯一名称对结果进行排序:

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)

好的!是否可以根据重复对象的频率对数组进行排序?那么上例中的 "Nancy" 是否移动到修改后的数组的前面(或后面)?
@ALx - 我更新了一个基于出现排序的示例。
在您的第二个示例中,sort() 似乎被错误地调用:如果 a 是 < b 然后它返回与 a == b 相同的值,这可能导致未排序的结果。除非你在这里做一些我错过的聪明的事情,否则应该是.sort(function(a,b){ return a > b ? 1 : a < b ? -1 : 0; })
如果数据只是一个名称数组,除了消除重复之外没有其他要求,为什么还要使用排序、映射和归约呢?只需使用一组在 O(n) 时间内完成的工作。 -- msdn.microsoft.com/en-us/library/dn251547
@Dave 是的 - 请参阅上面 [...new Set(names)] 中的示例
C
Casey Kuball

Vanilla JS:使用像集合这样的对象删除重复项

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

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:通过跟踪已经看到的值来删除重复项(订单安全)

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

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:使用新的 Set 数据结构(订单安全)

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

function remove_duplicates_es6(arr) {
    let s = new Set(arr);
    let it = s.values();
    return Array.from(it);
}

示例用法:

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"]

在较新的浏览器中,您甚至可以执行 var c = Object.keys(b)。应该注意的是,这种方法仅适用于字符串,但没关系,这就是原始问题所要求的。
还应注意,您可能会丢失数组的顺序,因为对象不会按顺序保持其属性。
@JuanMendes 我创建了一个订单安全版本,如果之前没有看到该值,它只会复制到新数组。
这条线 obj[arr[i]] = true; 发生了什么?
@kittu,即获取数组的第 i 个元素,并将其放入对象中(用作集合)。键是元素,值是 true,这完全是任意的,因为我们只关心对象的键。
A
Audwin Oyong

使用数组 .filter.indexOf 函数的单行版本:

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

愿意解释一下它是如何消除欺骗的吗?
@web_dev:它没有!我已经更正了之前破坏代码的编辑。希望它现在更有意义。谢谢提问!
不幸的是,如果这是一个大数组,则性能很差—— arr.indexOf 为 O(n),这使得该算法为 O(n^2)
正如@CaseyKuball 建议的那样,这个解决方案实际上非常慢 - 请参阅stackoverflow.com/questions/67424599/…
B
Brandon Boone

使用 Underscore.js

它是一个包含大量用于操作数组的函数的库。

这是与 jQuery 的 tux 和 Backbone.js 的吊带搭配的领带。

_.uniq

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

Example

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

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

注意:Lo-Dashunderscore 的竞争对手)也提供了类似的 .uniq 实现。


不幸的是,下划线不提供定义自定义相等函数的能力。他们确实允许的回调是针对“迭代”函数,例如带有 args(项目、值、数组)的函数。
[...new Set(Array)] 绰绰有余
@norbekoff - 绝对,大声笑。 〜10年后!
J
Jonca33

一条线:

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

最佳答案,如果您使用的是 ES6
这3个点是什么意思?
@Vitalicus,这是 ES6 中的扩展运算符。阅读更多here
A
Ashutosh Jha

借助 filter 方法的第二个 - 索引 - 参数,您可以简单地在 JavaScript 中执行此操作:

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

或简而言之

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

这仅适用于包含原语的数组?
这个 a.indexOf(v)==i 应该是 a.indexOf(v) === a.lastIndexOf(v)
@Hitmands 你从右边比较,我从左边比较。没有其他的 。
也可以在不需要 a 变量的情况下工作,因为数组是 filter 的第三个参数:[1/0, 2,1/0,2,3].filter((v,i,a) => a.indexOf(v) === i)(请注意,它也适用于 Infinity ☺ )
S
Sumit Joshi

像这样使用 Array.filter()

var actualArr = ['Apple', 'Apple', 'Banana', 'Mango', 'Strawberry', 'Banana']; console.log('实际数组:' + actualArr); var filteredArr = actualArr.filter(function(item, index) { if (actualArr.indexOf(item) == index) return item; }); console.log('过滤后的数组:'+filteredArr);

这可以在 ES6 中缩短到

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

Here 很好地解释了 Array.filter()


你能详细说明你在这里做了什么吗? :-)
当数组是数组数组时不起作用
不适用于区分大小写的数组
I
Ivo

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

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

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

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

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

Array.from(new Set(vals))

甚至使用 spread operator

[...new Set(vals)]

干杯!


Set 对于那些习惯使用 python 的人来说非常棒并且非常直观。太糟糕了,他们没有那些伟大的(联合,相交,差异)方法。
我使用了利用 set 机制的简单的一行代码。这是针对自定义自动化任务的,因此我对在最新版本的 Chrome(在 jsfiddle 内)中使用它并不持怀疑态度。但是,我仍然想知道对数组进行重复数据删除的最短所有浏览器兼容方法。
集合是新规范的一部分,您应该使用排序/减少组合来确保跨浏览器兼容性@AlexanderDixon
.reduce() 不是跨浏览器兼容的,因为我必须应用 poly-fill。不过,我很感谢你的回应。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
g
gilly3

最佳答案具有 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;
    });
}

这适用于字符串、数字和日期。如果您的数组包含对象,则上述解决方案将不起作用,因为当强制转换为字符串时,它们都将具有 "[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;
}

2019 年编辑: 现代版本的 JavaScript 使这个问题更容易解决。无论您的数组是否包含对象、字符串、数字或任何其他类型,都可以使用 Set

function getDistinctArray(arr) {
    return [...new Set(arr)];
}

实现如此简单,不再需要定义函数。


您是否考虑过您的方法对性能的影响?
@Tushar - 您的要点给出了 404。没有排序算法具有 O(n) 复杂性。排序不会更快。
@Tushar - 该数组中没有实际的重复项。如果要从数组中删除与数组中其他对象具有完全相同属性和值的对象,则需要编写自定义相等检查函数来支持它。
@Tushar - 此页面上的所有答案都不会从 your gist 中的此类数组中删除任何重复项。
请注意,IE 迟到了 Set
D
Deke

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

 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


对于 Mac 用户,即使这是一个 ES6 函数,它也可以在 macOS 10.11.6 El Capitan 中使用,使用脚本编辑器。
M
Michael Oryl

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

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

Set 的构造函数实际上需要 new 关键字
@Ivo 谢谢。以前 Firefox 的实现不需要 new,我想知道 ES6 草案是否改变了这种行为。
某些构造函数可能确实被称为函数,尽管这种行为取决于浏览器对规范的实现;)
S
ShAkKiR

解决方案 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;
}

解决方案 2(使用 Set)

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

测试

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

表现

当我在 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); } 返回一个; } Array.prototype.unique2 = function() { return Array.from(new Set(this)); } 变量 x=[]; for(var i=0;i<10000;i++){ x.push("x"+i);x.push("x"+(i+1)); } console.time("unique1");控制台.log(x.unique1()); console.timeEnd("unique1"); console.time("unique2");控制台.log(x.unique2()); console.timeEnd("unique2");


为使用 Set 点赞。我不知道性能比较虽然
我在某处读到 Array 比 Set 更快(整体性能),但是当我在 chrome 中测试时,使用 Set 的实现要快得多!查看编辑后的答案:)
更好的做法是使用 Object.defineProperty(Array.prototype,"unique".. 而不是 Array.prototype.unique = ... 在此处查看更多信息 stackoverflow.com/questions/10105824/…
Set 方法似乎不适用于 Node.js 中的我。 new Set([5,5]) 在某些情况下似乎返回 [5,5]。我和你一样困惑。编辑:我发现发生了什么。 new Set([new Number(5), new Number(5)]) 返回 [5,5]。显然,如果我用 new 实例化它们,Node 认为这两个数字 5 是不同的……老实说,这是我见过的最愚蠢的事情。
@Demonblack 这是一个有效的问题。 x=new Number(5) 和另一个 y=new Number(5) 将是两个不同的对象,与 var x=5 和 var y=5 相反。 new 关键字将创建一个新对象。我知道这个解释很明显,但这就是我所知道的:)
P
Pang

去这个:

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

现在 uniqueArray 不包含重复项。


m
mgthomas99

下面的方法比列出的 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;

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


我在修订版 4 中添加了一个更快的版本。请查看!
测试似乎没有使用数组???我添加了(又一个)一个似乎在不同浏览器上始终快速的一个(参见 jsperf.com/remove-duplicate-array-tests/10): for (var n = array.length, result = [array[n--]], i; n-- ;) { i = 数组[n]; if (!(i in result)) result.push(i); } 返回结果;
C
Community

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

我相信这是最好的方法

var myArray = [100, 200, 100, 200, 100, 100, 200, 200, 200, 200], 减少 = Object.keys(myArray.reduce((p,c) => (p[c] = true, p),{}));控制台.log(减少);

好的..即使这个是 O(n) 而其他的是 O(n^2) 我很想看看这个减少/查找表和 filter/indexOf 组合之间的基准比较(我选择 Jeetendras 非常好的实现{1 })。我准备了一个 100K 项目数组,其中填充了 0-9999 范围内的随机正整数,并删除了重复项。我重复测试了 10 次,结果的平均值表明它们在性能上是不匹配的。

在 firefox v47 中,reduce & lut:14.85ms vs filter & indexOf:2836ms

在 chrome v51 中,reduce & lut:23.90ms vs filter & indexOf:1066ms

好吧,到目前为止一切都很好。但是这次让我们以 ES6 风格正确地完成它。它看起来很酷..!但到目前为止,它在强大的 lut 解决方案中的表现对我来说是个谜。让我们先看看代码,然后对其进行基准测试。

var myArray = [100, 200, 100, 200, 100, 100, 200, 200, 200, 200], 减少 = [...myArray.reduce((p,c) => p.set(c,true) ,新地图()).keys()];控制台.log(减少);

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

var ranar = [], red1 = a => Object.keys(a.reduce((p,c) => (p[c] = true,p),{})), red2 = a => reduce = [ ...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 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 占用:" + avg1 + "msec"); console.log("map & spread 占用:" + avg2 + "msec");

你会用哪一个..?好吧,没那么快……!不要被欺骗。地图处于位移状态。现在看……在上述所有情况下,我们用范围 < 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 => reduce = [ ...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 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 占用:" + avg1 + "msec"); console.log("map & spread 占用:" + avg2 + "msec");

现在这是 Map() 的壮观回归..!现在,当您想删除骗局时,您可以做出更好的决定。

好吧,我们现在都很开心。但在掌声中,主角总是排在最后。我相信你们中的一些人想知道 Set 对象会做什么。既然我们对 ES6 持开放态度,并且我们知道 Map 是前几场比赛的赢家,让我们将 Map 与 Set 作为决赛进行比较。这次是典型的皇家马德里对巴塞罗那的比赛……还是这样?让我们看看谁会赢得 el classico :)

var ranar = [], red1 = a => reduce = [...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 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 占用:" + avg1 + "msec"); console.log("set & A.from 取值:" + avg2 + "msec");

哇..男人..!出乎意料的是,它根本就不是经典之作。更像是巴塞罗那足球俱乐部对阵 CA 奥萨苏纳 :))


顺便说一句,我在 Typescript 中得到 arr.reduce(...).keys(...).slice is not a function 试图使用你的 ES6 方法
d
drew7721

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

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。如果我们不检查它的类型,它将不适用于具有混合类型的数组。简单但有效的答案
J
John Slegers

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

代码示例:

var 数据 = [2,3,4,5,5,4]; var filter = function(value, index){ return this.indexOf(value) == index }; var filtersData = data.filter(filter, data); document.body.innerHTML = '

' + JSON.stringify(filteredData, null, '\t') + '
';

另见this Fiddle


天才!而且,例如,如果您想要重复的,(而不是删除它们)您所要做的就是将 this.indexOf(value) == index 替换为 this.indexOf(value, index+1) > 0 谢谢!
您甚至可以将其恢复到单个“过滤器”行:filterData = data.filter((v, i) => (data.indexOf(v) == i) );
上次我打扰了!抱歉...拿起我的第一个答案,在 2 行中,您可以获得 JSON var JSON_dupCounter = {};,其中包含重复的内容以及重复的次数:data.filter((testItem, index) => (data.indexOf(testItem, index + 1) > 0)).forEach((found_duplicated) => (JSON_dupCounter[found_duplicated] = (JSON_dupCounter [found_duplicated] || 1) + 1));
这仅适用于基元数组?
@frozen :如果可以使用 == 来确定相等性的所有内容。所以,如果你正在处理例如。数组、对象或函数,过滤器仅适用于引用相同数组、对象或函数 (see demo) 的不同条目。如果您想根据 different criteria 确定相等性,则需要在过滤器中包含这些条件。
o
ofir_aghai

所以选项是:

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]

b
bodich

这里很容易理解和在任何地方工作(甚至在 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"]

M
Mohideen bin Mohammed

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

name_list = ["迈克","马特","南希","亚当","珍妮","南希","卡尔"]; get_uniq = name_list.filter(function(val,ind) { return name_list.indexOf(val) == ind; }) console.log("原名列表:"+name_list.length, name_list) console.log("\n唯一名称列表:"+get_uniq.length, get_uniq)

https://i.stack.imgur.com/PbJoQ.png


c
csuwldcat

除了比当前答案更简单、更简洁的解决方案(减去面向未来的 ES6 解决方案)之外,我还对其进行了测试,并且它也更快:

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

一个警告: Array.lastIndexOf() 是在 IE9 中添加的,因此如果您需要低于此值,则需要查看其他地方。


佚名

泛型函数方法

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

// 小的、可重用的辅助函数 const apply = f => a => f(a);常量翻转 = 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)); // 实际的去重函数 const uniqueBy = f => foldl( acc => x => some(f(x)) (acc) ? acc : push(x) (acc) ) ([]); // 比较器 const eq = y => x => x === y; // 字符串相等大小写不敏感 :D const seqCI = y => x => x.toLowerCase() === y.toLowerCase(); // 模拟数据 const xs = [1,2,3,1,2,3,4];常量ys = [“a”,“b”,“c”,“A”,“B”,“C”,“D”]; console.log(uniqueBy(eq) (xs) ); console.log(uniqueBy(seqCI) (ys));

我们可以轻松地从 unqiueBy 派生 unique 或使用利用 Set 的更快实现:

const unqiue = uniqueBy(eq);

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

这种方法的好处:

使用单独的比较器功能的通用解决方案

声明式和简洁的实现

重用其他小型通用功能

性能注意事项

uniqueBy 不如使用循环的命令式实现快,但由于其通用性,它更具表现力。

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

内存消耗和垃圾回收

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


M
MBJH
for (i=0; i<originalArray.length; i++) {  
    if (!newArray.includes(originalArray[i])) {
        newArray.push(originalArray[i]); 
    }
}

喜欢香草js。谢谢
G
GibboK

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

浏览器支持:

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

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

S
Shankar ARUL

如果你有任何机会使用

D3.js

你可以做

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

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


很漂亮,但加载功能强大的完整可视化库以仅过滤重复项似乎有点过头了。幸运的是,我出于某种目的需要该库,我将使用它。非常感谢。
P
Pang

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

G
Gwenc37
$(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);
});

T
THE AMAZING

https://jsfiddle.net/2w0k5tz8/

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

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

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