如何在 JavaScript 对象数组中查找重复值,并仅输出唯一值?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39885893/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to find duplicate values in a JavaScript array of objects, and output only unique values?
提问by Rico Letterman
I'm learning JS. Supposing I have the below array of objects:
我正在学习JS。假设我有以下对象数组:
var family = [
{
name: "Mike",
age: 10
},
{
name: "Matt"
age: 13
},
{
name: "Nancy",
age: 15
},
{
name: "Adam",
age: 22
},
{
name: "Jenny",
age: 85
},
{
name: "Nancy",
age: 2
},
{
name: "Carl",
age: 40
}
];
Notice that Nancyis showing up twice (changing only the age). Supposing I want to output only unique names. How do I output the above array of objects, without duplicates? ES6 answers more than welcome.
请注意,Nancy出现了两次(仅更改年龄)。假设我只想输出唯一的名称。如何输出上面的对象数组,没有重复?ES6 的回答非常受欢迎。
Related (couldn't find a good way for usage on objects):
相关(找不到在对象上使用的好方法):
EDITHere's what I tried. It works well with strings but I can't figure how to make it work with objects:
编辑这是我尝试过的。它适用于字符串,但我无法弄清楚如何使其适用于对象:
family.reduce((a, b) => {
if (a.indexOf(b) < 0 ) {
a.push(b);
}
return a;
},[]);
回答by Nina Scholz
You could use a Set
in combination with Array#map
and a spread operator...
in a single line.
您可以在一行Set
中结合使用Array#map
和展开运算符...
。
Map returns an array with all names, which are going into the set initializer and then all values of the set are returned in an array.
Map 返回一个包含所有名称的数组,这些名称将进入集合初始值设定项,然后集合中的所有值都返回到一个数组中。
var family = [{ name: "Mike", age: 10 }, { name: "Matt", age: 13 }, { name: "Nancy", age: 15 }, { name: "Adam", age: 22 }, { name: "Jenny", age: 85 }, { name: "Nancy", age: 2 }, { name: "Carl", age: 40 }],
unique = [...new Set(family.map(a => a.name))];
console.log(unique);
For filtering and return only unique names, you can use Array#filter
with Set
.
对于过滤并仅返回唯一名称,您可以使用Array#filter
with Set
。
var family = [{ name: "Mike", age: 10 }, { name: "Matt", age: 13 }, { name: "Nancy", age: 15 }, { name: "Adam", age: 22 }, { name: "Jenny", age: 85 }, { name: "Nancy", age: 2 }, { name: "Carl", age: 40 }],
unique = family.filter((set => f => !set.has(f.name) && set.add(f.name))(new Set));
console.log(unique);
回答by m0meni
The Solution
解决方案
Store occurrences of name
external to the loop in an object, and filter if there's been a previous occurrence.
name
将循环外部的事件存储在一个对象中,并过滤是否存在先前的事件。
https://jsfiddle.net/nputptbb/2/
https://jsfiddle.net/nputptbb/2/
var occurrences = {}
var filteredFamily = family.filter(function(x) {
if (occurrences[x.name]) {
return false;
}
occurrences[x.name] = true;
return true;
})
you can also generalize this solution to a function
您还可以将此解决方案推广到函数
function filterByProperty(array, propertyName) {
var occurrences = {}
return array.filter(function(x) {
var property = x[propertyName]
if (occurrences[property]) {
return false;
}
occurrences[property]] = true;
return true;
})
}
and use it like
并像使用它一样
var filteredFamily = filterByProperty(family, 'name')
Explanation
解释
Don't compare objects using indexOf
, which only uses the ===
operator between objects. The reason why your current answer doesn't work is because ===
in JS does not compare the objects deeply, but instead compares the references. What I mean by that you can see in the following code:
不要使用 比较对象indexOf
,它只使用===
对象之间的运算符。您当前的答案不起作用的原因是因为===
在 JS 中没有深入比较对象,而是比较引用。我的意思是你可以在下面的代码中看到:
var a = { x: 1 }
var b = { x: 1 }
console.log(a === b) // false
console.log(a === a) // true
Equality will tell you if you found the same exactobject, but not if you found an object with the same contents.
Equality 会告诉您是否找到了完全相同的对象,但不会告诉您是否找到了具有相同内容的对象。
In this case, you can compare your object on name
since it should be a unique key. So obj.name === obj.name
instead of obj === obj
. Moreover another problem with your code that affects its runtime and not its function is that you use an indexOf
inside of your reduce
. indexOf
is O(n)
, which makes the complexity of your algorithm O(n^2)
. Thus, it's better to use an object, which has O(1)
lookup.
在这种情况下,您可以比较您的对象,name
因为它应该是一个唯一的键。所以obj.name === obj.name
代替obj === obj
. 此外,影响其运行,而不是它的功能与您的代码的另一个问题是,你使用indexOf
你的内部reduce
。indexOf
是O(n)
,这使您的算法变得复杂O(n^2)
。因此,最好使用具有O(1)
查找功能的对象。
回答by Divyanshu Rawat
This will work fine.
这将正常工作。
const result = [1, 2, 2, 3, 3, 3, 3].reduce((x, y) => x.includes(y) ? x : [...x, y], []);
console.log(result);
回答by Simon Dragsb?k
I just thought of 2 simple ways for Lodash users
我只是为 Lodash 用户想到了 2 个简单的方法
Given this array:
鉴于此数组:
let family = [
{
name: "Mike",
age: 10
},
{
name: "Matt",
age: 13
},
{
name: "Nancy",
age: 15
},
{
name: "Adam",
age: 22
},
{
name: "Jenny",
age: 85
},
{
name: "Nancy",
age: 2
},
{
name: "Carl",
age: 40
}
]
1. Find duplicates:
1. 查找重复项:
let duplicatesArr = _.difference(family, _.uniqBy(family, 'name'), 'name')
// duplicatesArr:
// [{
// name: "Nancy",
// age: 2
// }]
2 Find if there are duplicates, for validation purpose:
2 查找是否有重复项,用于验证目的:
let uniqArr = _.uniqBy(family, 'name')
if (uniqArr.length === family.length) {
// No duplicates
}
if (uniqArr.length !== family.length) {
// Has duplicates
}
回答by Cezar Augusto
With the code you mentioned, you can try:
使用您提到的代码,您可以尝试:
family.filter((item, index, array) => {
return array.map((mapItem) => mapItem['name']).indexOf(item['name']) === index
})
Or you can have a generic function to make it work for other array of objects as well:
或者您可以使用通用函数使其也适用于其他对象数组:
function printUniqueResults (arrayOfObj, key) {
return arrayOfObj.filter((item, index, array) => {
return array.map((mapItem) => mapItem[key]).indexOf(item[key]) === index
})
}
and then just use printUniqueResults(family, 'name')
然后只需使用 printUniqueResults(family, 'name')
回答by Dave Cousineau
I would probably set up some kind of object. Since you've said ECMAScript 6, you have access to Set
, but since you want to compare values on your objects, it will take a little more work than that.
我可能会设置某种对象。由于您已经说过 ECMAScript 6,您可以访问Set
,但是由于您想比较对象上的值,因此需要做更多的工作。
An example might look something like this (removed namespace pattern for clarity):
一个例子可能看起来像这样(为了清楚起见,删除了命名空间模式):
var setOfValues = new Set();
var items = [];
function add(item, valueGetter) {
var value = valueGetter(item);
if (setOfValues.has(value))
return;
setOfValues.add(value);
items.push(item);
}
function addMany(items, valueGetter) {
items.forEach(item => add(item, valueGetter));
}
Use it like this:
像这样使用它:
var family = [
...
];
addMany(family, item => item.name);
// items will now contain the unique items
Explanation: you need to pull a value from each object as it's added and decide if it has already been added yet, based on the value you get. It requires a value getter, which is a function that given an item, returns a value (item => item.name
). Then, you only add items whose values haven't already been seen.
说明:您需要在添加的每个对象中提取一个值,并根据您获得的值确定它是否已经添加。它需要一个值获取器,它是一个函数,它给定一个项目,返回一个值 ( item => item.name
)。然后,您只添加尚未看到其值的项目。
A class implementation:
类实现:
// Prevents duplicate objects from being added
class ObjectSet {
constructor(key) {
this.key = key;
this.items = [];
this.set = new Set();
}
add(item) {
if (this.set.has(item[this.key])) return;
this.set.add(item[this.key]);
this.items.push(item);
}
addMany(items) {
items.forEach(item => this.add(item));
}
}
var mySet = new ObjectSet('name');
mySet.addMany(family);
console.log(mySet.items);