ES6 - 从对象数组中删除重复项
Posted
技术标签:
【中文标题】ES6 - 从对象数组中删除重复项【英文标题】:ES6 - Removing duplicates from array of objects 【发布时间】:2019-05-01 17:51:46 【问题描述】:假设一个对象数组如下:
const listOfTags = [
id: 1, label: "Hello", color: "red", sorting: 0,
id: 2, label: "World", color: "green", sorting: 1,
id: 3, label: "Hello", color: "blue", sorting: 4,
id: 4, label: "Sunshine", color: "yellow", sorting: 5,
id: 5, label: "Hello", color: "red", sorting: 6,
]
如果标签和颜色相同,则会出现重复条目。在这种情况下,id = 1 和 id = 5 的对象是重复的。
如何过滤此数组并删除重复项?
我知道您可以通过以下方式过滤一个键的解决方案:
const unique = [... new Set(listOfTags.map(tag => tag.label)]
但是多个键呢?
根据评论中的要求,这里是所需的结果:
[
id: 1, label: "Hello", color: "red", sorting: 0,
id: 2, label: "World", color: "green", sorting: 1,
id: 3, label: "Hello", color: "blue", sorting: 4,
id: 4, label: "Sunshine", color: "yellow", sorting: 5,
]
【问题讨论】:
【参考方案1】:也许有帮助。从数组中提取重复项,然后删除所有重复项
// Initial database data
[
key: "search", en:"Search" ,
key: "search", en:"" ,
key: "alert", en:"Alert" ,
key: "alert", en:"" ,
key: "alert", en:""
]
// Function called
async function removeDuplicateItems()
try
// get data from database
const data = (await getList());
// array reduce method for obj.key
const reduceMethod = data.reduce((x, y) =>
x[y.key] = ++x[y.key] || 0;
return x;
, );
// find duplicate items by key and checked whether "en" attribute also has value
const duplicateItems = data.filter(obj => !obj.en && reduceMethod[obj.key]);
console.log('duplicateItems', duplicateItems);
// remove all dublicate items by id
duplicateItems.forEach(async (obj) =>
const deleteResponse = (await deleteItem(obj.id)).data;
console.log('Deleted item: ', deleteResponse);
);
catch (error)
console.log('error', error);
// Now database data:
[
key: "search", en:"Search" ,
key: "alert", en:"Alert"
]
【讨论】:
【参考方案2】:const listOfTags = [
id: 1, label: "Hello", color: "red", sorting: 0,
id: 2, label: "World", color: "green", sorting: 1,
id: 3, label: "Hello", color: "blue", sorting: 4,
id: 4, label: "Sunshine", color: "yellow", sorting: 5,
id: 5, label: "Hello", color: "red", sorting: 6,
];
let keysList = Object.keys(listOfTags[0]); // Get First index Keys else please add your desired array
let unq_List = [];
keysList.map(keyEle=>
if(unq_List.length===0)
unq_List = [...unqFun(listOfTags,keyEle)];
else
unq_List = [...unqFun(unq_List,keyEle)];
);
function unqFun(array,key)
return [...new Map(array.map(o=>[o[key],o])).values()]
console.log(unq_List);
【讨论】:
【参考方案3】:迟到了,但我不知道为什么没有人提出更简单的建议:
listOfTags.filter((tag, index, array) => array.findIndex(t => t.color == tag.color && t.label == tag.label) == index);
【讨论】:
太棒了。谢谢!【参考方案4】:您可以在此处使用 reduce 来获取过滤后的对象。
listOfTags.reduce((newListOfTags, current) =>
if (!newListOfTags.some(x => x.label == current.label && x.color == current.color))
newListOfTags.push(current);
return newListOfTags;
, []);
【讨论】:
【参考方案5】:基于值可以转换为字符串的假设,可以调用
distinct(listOfTags, ["label", "color"])
distinct
在哪里:
/**
* @param array arr The array you want to filter for dublicates
* @param array<string> indexedKeys The keys that form the compound key
* which is used to filter dublicates
* @param boolean isPrioritizeFormer Set this to true, if you want to remove
* dublicates that occur later, false, if you want those to be removed
* that occur later.
*/
const distinct = (arr, indexedKeys, isPrioritizeFormer = true) =>
const lookup = new Map();
const makeIndex = el => indexedKeys.reduce(
(index, key) => `$index;;$el[key]`, ''
);
arr.forEach(el =>
const index = makeIndex(el);
if (lookup.has(index) && isPrioritizeFormer)
return;
lookup.set(index, el);
);
return Array.from(lookup.values());
;
旁注:如果你使用distinct(listOfTags, ["label", "color"], false)
,它会返回:
[
id: 1, label: "Hello", color: "red", sorting: 6,
id: 2, label: "World", color: "green", sorting: 1,
id: 3, label: "Hello", color: "blue", sorting: 4,
id: 4, label: "Sunshine", color: "yellow", sorting: 5,
]
【讨论】:
【参考方案6】:const listOfTags = [
id: 1, label: "Hello", color: "red", sorting: 0,
id: 2, label: "World", color: "green", sorting: 1,
id: 3, label: "Hello", color: "blue", sorting: 4,
id: 4, label: "Sunshine", color: "yellow", sorting: 5,
id: 5, label: "Hello", color: "red", sorting: 6,
]
const unique = [];
listOfTags.map(x => unique.filter(a => a.label == x.label && a.color == x.color).length > 0 ? null : unique.push(x));
console.log(unique);
【讨论】:
【参考方案7】:您可以在闭包中使用Set
进行过滤。
const
listOfTags = [ id: 1, label: "Hello", color: "red", sorting: 0 , id: 2, label: "World", color: "green", sorting: 1 , id: 3, label: "Hello", color: "blue", sorting: 4 , id: 4, label: "Sunshine", color: "yellow", sorting: 5 , id: 5, label: "Hello", color: "red", sorting: 6 ],
keys = ['label', 'color'],
filtered = listOfTags.filter(
(s => o =>
(k => !s.has(k) && s.add(k))
(keys.map(k => o[k]).join('|'))
)
(new Set)
);
console.log(filtered);
.as-console-wrapper max-height: 100% !important; top: 0;
【讨论】:
代码中的一些 cmets 对一些不太喜欢闭包的开发人员来说非常有用。这是一个很好的例子,其中闭包非常适合。对于那些感兴趣的人:listOfTags.filter
中的第一个函数是一个工厂函数,它会立即用一个新的空集 s
调用。在过滤完成之前,s
将可用。第二个功能是实际的过滤功能。每个对象o
都会调用它并返回一个布尔值。 (在这种情况下,另一个闭包函数进行实际的过滤器测试,对象o
的串联字段作为参数。)
s
和 o
是什么?
@Alfrex92, s
是new Set
的闭包,o
只是数组的每个对象。
@Alfrex92, k
是下一行 keys.map(k => o[k]).join('|')
一些属性的联合键的另一个闭包。
@NinaScholz - 这个article 谈论性能。你对你的方法做过性能测试吗?【参考方案8】:
一种方法是创建一个对象(或 Map),使用 2 个值的组合作为键,将当前对象作为值,然后从该对象中获取值
const listOfTags = [
id: 1, label: "Hello", color: "red", sorting: 0,
id: 2, label: "World", color: "green", sorting: 1,
id: 3, label: "Hello", color: "blue", sorting: 4,
id: 4, label: "Sunshine", color: "yellow", sorting: 5,
id: 5, label: "Hello", color: "red", sorting: 6,
]
const uniques = Object.values(
listOfTags.reduce((a, c) =>
a[c.label + '|' + c.color] = c;
return a
, ))
console.log(uniques)
【讨论】:
【参考方案9】:我会根据您感兴趣的属性将其放入带有复合键的临时 Map 中来解决这个问题。例如:
const foo = new Map();
for(const tag of listOfTags)
foo.set(tag.id + '-' tag.color, tag);
【讨论】:
这也是我最初的想法之一,但我发现字符串连接不是很优雅。 @Andy 这并不奇怪。这基本上就是 hashmaps 的工作原理,这是适合这类事物的数据结构。以上是关于ES6 - 从对象数组中删除重复项的主要内容,如果未能解决你的问题,请参考以下文章