获取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个回答

6

很奇怪之前没有人提出过这个建议...如果要通过对象键(下面是id)在数组中删除重复项,可以像这样操作:

const uniqArray = array.filter((obj, idx, arr) => (
  arr.findIndex((o) => o.id === obj.id) === idx
)) 

filter()findIndex()不都必须遍历数组吗?这将使其成为双重循环,因此运行成本是其他答案的两倍。 - Adam Katz
@AdamKatz是的,它会迭代n+1次数组。请注意,此处其他答案使用map、filter、indexOf、reduce等组合也必须这样做,这在问题本身上就是固有的。如果要避免,可以使用new Set()或类似Grozz回答中的查找对象。 - daviestar

5

对于具有一些唯一ID的基于对象的数组,我有一个简单的解决方案,通过它您可以在线性复杂度下进行排序

function getUniqueArr(arr){
    const mapObj = {};
    arr.forEach(a => { 
       mapObj[a.id] = a
    })
    return Object.values(mapObj);
}

4
使用对象键创建唯一数组,我尝试了以下操作:
function uniqueArray( ar ) {
  var j = {};

  ar.forEach( function(v) {
    j[v+ '::' + typeof v] = v;
  });


  return Object.keys(j).map(function(v){
    return j[v];
  });
}   

uniqueArray(["1",1,2,3,4,1,"foo", false, false, null,1]);

这将返回["1", 1, 2, 3, 4, "foo", false, null]


1
我认为你的答案是最快的解决方案,因为它使用了哈希。 - Max Makhrov

4

如前所述,如果您可以使用,[...new Set(values)]是最好的选项。

否则,下面是一个一行代码的解决方案,它不需要为每个索引迭代数组:

values.sort().filter((val, index, arr) => index === 0 ? true : val !== arr[index - 1]);

该方法仅将每个值与其前一个值进行比较。结果会被排序。

Example:

let values = [ 1, 2, 3, 3, 4, 5, 5, 5, 4, 4, 4, 5, 1, 1, 1, 3, 3 ];
let unique = values.sort().filter((val, index, arr) => index === 0 ? true : val !== arr[index - 1]);
console.log(unique);


当同一行中存在多个相同的值时,无法正常工作。 - Ponciusz
添加了代码片段,似乎可以工作。 - lonix

4
任务是从一个由各种类型(基本类型和非基本类型)组成的数组中获取一个唯一的数组。
基于使用 `new Set(...)` 的方法并不新颖。这里它被 `JSON.stringify(...)`,`JSON.parse(...)` 和 `[].map` 方法所利用。优点包括“通用性”(适用于任何类型的数组)、“简短的ES6符号表示法”以及可能的“性能优势”(对于这种情况)。

const dedupExample = [
    { a: 1 },
    { a: 1 },
    [ 1, 2 ],
    [ 1, 2 ],
    1,
    1,
    '1',
    '1'
]

const getUniqArrDeep = arr => {
    const arrStr = arr.map(item => JSON.stringify(item))
    return [...new Set(arrStr)]
        .map(item => JSON.parse(item))
}

console.info(getUniqArrDeep(dedupExample))
   /* [ {a: 1}, [1, 2], 1, '1' ] */


由于您正在将字符串化,然后重新解析,因此在这里绝对不是正面的性能属性。 - airtonix
“正面表现”和“属性在这里”是什么意思?需要一些解释。 - Roman
将一个对象转换为字符串,然后再解析回来,显然是 JS 中最花费资源的操作之一。http://jsben.ch/wQ9RU - airtonix
@airtonix,确实如此,我们需要考虑性能问题。有些情况就像“艰难时刻需要做出艰难的决定”一样。 :-) - Roman

3

您可以使用Set来消除重复项。

const originalNumbers = [1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 1, 2, 9];
const uniqueNumbersSet = new Set(originalNumbers);

/** get the array back from the set */
const uniqueNumbersArray = Array.from(uniqueNumbersSet);

/** uniqueNumbersArray outputs to: [1, 2, 3, 4, 5, 9] */

了解有关Set的更多信息:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Set


JavaScript中的Set()是生成唯一数组的好方法。 - Mayank Dudakiya

3
一个简单的代码如下所示:
let arr = [1,'k',12,1,1,'k','12'];
let distictArr=arr.filter((item, index, arr) => arr.indexOf(item) === index);

console.log(distictArr); // [1, 'k', 12, '12']

3
在其他答案的基础上,这里提供另一种变体,它采用了一个可选标志来选择策略(保留第一次出现还是保留最后一次): 不需要扩展Array.prototype
function unique(arr, keepLast) {
  return arr.filter(function (value, index, array) {
    return keepLast ? array.indexOf(value, index + 1) < 0 : array.indexOf(value) === index;
  });
};

// Usage
unique(['a', 1, 2, '1', 1, 3, 2, 6]); // -> ['a', 1, 2, '1', 3, 6]
unique(['a', 1, 2, '1', 1, 3, 2, 6], true); // -> ['a', '1', 1, 3, 2, 6]

Extending Array.prototype

Array.prototype.unique = function (keepLast) {
  return this.filter(function (value, index, array) {
    return keepLast ? array.indexOf(value, index + 1) < 0 : array.indexOf(value) === index;
  });
};

// Usage
['a', 1, 2, '1', 1, 3, 2, 6].unique(); // -> ['a', 1, 2, '1', 3, 6]
['a', 1, 2, '1', 1, 3, 2, 6].unique(true); // -> ['a', '1', 1, 3, 2, 6]

3

使用 One Liner 查找对象数组中的唯一值

const uniqueBy = (x,f)=>Object.values(x.reduce((a,b)=>((a[f(b)]=b),a),{}));
// f -> should must return string because it will be use as key

const data = [
  { comment: "abc", forItem: 1, inModule: 1 },
  { comment: "abc", forItem: 1, inModule: 1 },
  { comment: "xyz", forItem: 1, inModule: 2 },
  { comment: "xyz", forItem: 1, inModule: 2 },
];

uniqueBy(data, (x) => x.forItem +'-'+ x.inModule); // find unique by item with module
// output
// [
//   { comment: "abc", forItem: 1, inModule: 1 },
//   { comment: "xyz", forItem: 1, inModule: 2 },
// ];

// can also use for strings and number or other primitive values

uniqueBy([1, 2, 2, 1], (v) => v); // [1, 2]
uniqueBy(["a", "b", "a"], (v) => v); // ['a', 'b']

uniqueBy(
  [
    { id: 1, name: "abc" },
    { id: 2, name: "xyz" },
    { id: 1, name: "abc" },
  ],
  (v) => v.id
);
// output
// [
//   { id: 1, name: "abc" },
//   { id: 2, name: "xyz" },
// ];

你可以使用 uniqBy 替代 uniqueBy。 - Danish

2

看这里。Jquery提供了uniq方法:https://api.jquery.com/jQuery.unique/

var ids_array = []

$.each($(my_elements), function(index, el) {
    var id = $(this).attr("id")
    ids_array.push(id)
});

var clean_ids_array = jQuery.unique(ids_array)

$.each(clean_ids_array, function(index, id) {
   elment = $("#" + id)   // my uniq element
   // TODO WITH MY ELEMENT
});

1
如果您阅读了您链接的页面上的描述:描述:对DOM元素数组进行排序,去除重复项。请注意,这仅适用于DOM元素数组,而不适用于字符串或数字。 - Mottie

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