【问题标题】:ES6 - Removing duplicates from array of objectsES6 - 从对象数组中删除重复项
【发布时间】: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},
]

【问题讨论】:

    标签: javascript dictionary filter unique reduce


    【解决方案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 的工作原理,这是适合这类事物的数据结构。
                猜你喜欢
                • 2018-08-15
                • 2018-03-29
                • 2018-06-02
                • 2019-07-05
                • 2017-04-10
                • 2016-03-12
                相关资源
                最近更新 更多