获取JavaScript数组中的所有唯一值(删除重复项)

2781
我有一个数字数组,我需要确保其中的数字是唯一的。我在互联网上找到了下面的代码片段,它在数组中有零的情况下工作得很好。我在Stack Overflow上找到了一个几乎完全相似的this other script,但它没有出错。
所以为了帮助我学习,有人可以帮我确定原型脚本出了什么问题吗?
Array.prototype.getUnique = function() {
 var o = {}, a = [], i, e;
 for (i = 0; e = this[i]; i++) {o[e] = 1};
 for (e in o) {a.push (e)};
 return a;
}

6
那个较旧的问题是关于查找并返回重复项的(我也感到困惑!)。我的问题更多地涉及到为什么此函数在数组中有零时会失败。 - Mottie
对于未来的读者,当你开始发现你必须要以算法方式频繁地修改数据结构的内容(排序、去重等),或者在每次迭代中搜索其中的元素时,可以安全地假设你一开始使用的数据结构是错误的,并开始使用更适合当前任务的数据结构(例如,在这种情况下,使用哈希集合而不是数组)。 - nurettin
我很久以前从别处复制了这段代码...但它似乎非常简单:o表示对象a表示数组i表示索引,而e则表示嗯,某个东西:P - Mottie
使用 Ramda 中的 R.uniq(list) 可以解决这个问题。https://ramdajs.com/docs/#uniq - varad_s
@user6316468 请注意代码部分的重点。火箭表情符号与示例代码无关。抱歉造成困惑。 - Lukas Liesis
显示剩余9条评论
95个回答

2

为去除重复项,可能有两种情况。首先,所有数据都不是对象;其次,所有数据都是对象。

如果所有数据都是任何一种原始数据类型,如int、float、string等,则可以按照以下方式进行操作:

const uniqueArray = [...new Set(oldArray)]

但是假设你的数组包含像下面这样的JS对象

{
    id: 1,
    name: 'rony',
    email: 'rony@example.com'
}

然后要获取所有唯一的对象,您可以按照以下方式进行操作。
let uniqueIds = [];
const uniqueUsers = oldArray.filter(item => {
    if(uniqueIds.includes(item.id)){
        return false;
    }else{
        uniqueIds.push(item.id);
        return true;
    }
})

您可以使用此方法使任何类型的数组唯一。只需在uniqueIds数组上保留跟踪键即可。

2

使用ES6(一行代码)

基本类型值的数组

let originalArr= ['a', 1, 'a', 2, '1'];

let uniqueArr = [...new Set(originalArr)];

对象数组

let uniqueObjArr = [...new Map(originalObjArr.map((item) => [item["propertyName"], item])).values()];

const ObjArray = [
    {
        name: "Eva Devore",
        character: "Evandra",
        episodes: 15,
    },
    {
        name: "Alessia Medina",
        character: "Nixie",
        episodes: 15,
    },
    {
        name: "Kendall Drury",
        character: "DM",
        episodes: 15,
    },
    {
        name: "Thomas Taufan",
        character: "Antrius",
        episodes: 14,
    },
    {
        name: "Alessia Medina",
        character: "Nixie",
        episodes: 15,
    },
];

let uniqueObjArray = [...new Map(ObjArray.map((item) => [item["id"], item])).values()];

2
您可以使用jQuery。
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]

最初的回答位于:jQuery函数获取数组中所有唯一元素?


7
这个似乎只对整数数组起作用。当我加入一些字符串时,它们都被剥离出结果了。 - hippietrail

2

如果有人正在使用 knockoutjs

ko.utils.arrayGetDistinctValues()

顺便看一下所有的ko.utils.array*工具。


2
最初的回答:
去重通常需要一个给定类型的相等性运算符。但是使用eq函数会阻止我们以有效的方式利用Set来确定重复项,因为Set会退化到===。正如你所知道的那样,===不适用于引用类型。所以我们有点陷入困境了,对吧?
解决方法很简单,只需使用转换函数将(引用)类型转换为我们实际可以使用Set查找的内容即可。例如,我们可以使用哈希函数,或者JSON.stringify数据结构(如果它不包含任何函数)。
通常我们只需要访问一个属性,然后比较该属性而不是Object的引用。
这里有两个满足这些要求的组合器:

const dedupeOn = k => xs => {
  const s = new Set();

  return xs.filter(o =>
    s.has(o[k])
      ? null
      : (s.add(o[k]), o[k]));
};

const dedupeBy = f => xs => {
  const s = new Set();

  return xs.filter(x => {
    const r = f(x);
    
    return s.has(r)
      ? null
      : (s.add(r), x);
  });
};

const xs = [{foo: "a"}, {foo: "b"}, {foo: "A"}, {foo: "b"}, {foo: "c"}];

console.log(
  dedupeOn("foo") (xs)); // [{foo: "a"}, {foo: "b"}, {foo: "A"}, {foo: "c"}]

console.log(
  dedupeBy(o => o.foo.toLowerCase()) (xs)); // [{foo: "a"}, {foo: "b"}, {foo: "c"}]

使用这些组合器,我们在处理各种去重问题时非常灵活。虽然不是最快的方法,但是它是最具表现力和最通用的方法。"最初的回答"

2

这并不是对原问题的直接字面回答,因为我更喜欢在第一次生成数组时就没有重复的值。所以这里是我的UniqueArray

class UniqueArray extends Array {
    constructor(...args) {
        super(...new Set(args));
    }
    push(...args) {
        for (const a of args) if (!this.includes(a)) super.push(a);
        return this.length;
    }
    unshift(...args) {
        for (const a of args.reverse()) if (!this.includes(a)) super.unshift(a);
        return this.length;
    }
    concat(...args) {
        var r = new UniqueArray(...this);
        for (const a of args) r.push(...a);
        return r;
    }
}

> a = new UniqueArray(1,2,3,1,2,4,5,1)
UniqueArray(5) [ 1, 2, 3, 4, 5 ]
> a.push(1,4,6)
6
> a
UniqueArray(6) [ 1, 2, 3, 4, 5, 6 ]
> a.unshift(1)
6
> a
UniqueArray(6) [ 1, 2, 3, 4, 5, 6 ]
> a.unshift(0)
7
> a
UniqueArray(7) [
  0, 1, 2, 3,
  4, 5, 6
]
> a.concat(2,3,7)
UniqueArray(8) [
  0, 1, 2, 3,
  4, 5, 6, 7
]

2

  var myArray = ["a",2, "a", 2, "b", "1"];
  const uniques = [];
  myArray.forEach((t) => !uniques.includes(t) && uniques.push(t));
  console.log(uniques);


2
这里有一个几乎是一行的O(n)算法,它保留了第一个元素,并且你可以将你想要去重的字段单独保存。这是函数式编程中一个相当常见的技巧——使用reduce构建一个数组并返回它。由于我们像这样构建数组,我们保证获得一个稳定的排序,不像[...new Set(array)]方法。我们仍然使用Set来确保没有重复项,因此我们的累加器包含了一个Set和我们正在构建的数组。

const removeDuplicates = (arr) =>
  arr.reduce(
    ([set, acc], item) => set.has(item) ? [set, acc] : [set.add(item), (acc.push(item), acc)],
    [new Set(), []]
  )[1]

上面的方法可以用于简单的值,但不能用于对象,就像[...new Set(array)]一样。如果项是包含id属性的对象,则应该这样做:

const removeDuplicates = (arr) =>
  arr.reduce(
    ([set, acc], item) => set.has(item.id) ? [set, acc] : [set.add(item.id), (acc.push(item), acc)],
    [new Set(), []]
  )[1]


2

let ar = [1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 2, 1];
let unique = ar.filter((value, index) => {
        return ar.indexOf(value) == index;
      });
console.log(unique);


2

使用lodash和身份lambda函数来完成,只需在使用对象之前定义即可

const _ = require('lodash');
...    
_.uniqBy([{a:1,b:2},{a:1,b:2},{a:1,b:3}], v=>v.a.toString()+v.b.toString())
_.uniq([1,2,3,3,'a','a','x'])

并且将拥有:
[{a:1,b:2},{a:1,b:3}]
[1,2,3,'a','x']

(这是最简单的方法)


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