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 的串联字段作为参数。) so 是什么? @Alfrex92, snew Set 的闭包,o 只是数组的每个对象。 @Alfrex92, k 是下一行 keys.map(k =&gt; 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 - 从对象数组中删除重复项的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript ES6 - 计算对象数组的重复项

从数组中删除重复的对象

从 GeoFire 对象数组中删除重复项 [重复]

从对象数组中的嵌套数组中删除重复项

如何从对象数组中删除所有重复项?

javascript 从javascript中的对象数组中删除重复项