【问题标题】:Algorithm + ES6 - Compare 4 arrays of Objects and remove all repeated items算法 + ES6 - 比较 4 个对象数组并删除所有重复项
【发布时间】:2021-02-01 01:55:40
【问题描述】:

我正在尝试根据偏好删除四个数组之间的所有重复对象。所有数组都有唯一的元素,并且可能没有顺序。这是一张试图解释问题的图片:

如您所见,如果数组的首选项较低,则元素将保留在其中。例如,id 为“6”的对象在优先级为 2、3 和 4 的数组中重复出现。因此,算法必须检测到这一点并从优先级为 3 和 4 的数组中删除这些对象,因为 2

所以,如果输入数据是:

  arr_p1 = [{ id: "892d" }, {id: "kla8x" }, {id: "sys32" }]
  arr_p2 = [{id: "saa1" }, { id: "892d" }]
  arr_p3 = [{ id: "kla8x" }, {id: "saa1" }, {id: "pp182" }]

输出必须是:

  arr_p1 = [{ id: "892d" }, {id: "kla8x" }, {id: "sys32" }]
  arr_p2 = [{id: "saa1" }]
  arr_p3 = [{id: "pp182" }]

关于如何以良好的复杂性顺序解决这种情况的任何想法?

所有数组的大小都限制为 40 个对象。

我唯一能想到的就是按标识符对每个数组中的所有对象进行排序。然后,取随着每个列表的指针移动的对象的最低标识符,从最低首选项 (1) 到最高 (4),如果它在较高的首选项列表之一中,则将其删除...但是我需要在不改变元素顺序的情况下这样做...

Pd:我正在使用 JS 和 ES6。

【问题讨论】:

  • 数组的数量是固定的吗?
  • 对象中的数据是否超过此 ID?
  • 是的,数组的个数是固定的,不,唯一的数据就是id。

标签: javascript arrays algorithm ecmascript-6


【解决方案1】:

将所有项目组合到一个数组中,然后使用Array.reduceRight() 以相反的顺序将它们缩减为一个 Map。颠倒的顺序将导致第一个项目覆盖最后一个项目。

现在您可以使用 Map 过滤每个数组,并仅保留 Map 上存在的项目。

复杂度为 O(N1 + N2 + N3),其中 Nx 是该数组的长度。

const arr_p1 = [{ id: "892d" }, {id: "kla8x" }, {id: "sys32" }]
const arr_p2 = [{id: "saa1" }, { id: "892d" }]
const arr_p3 = [{ id: "kla8x" }, {id: "saa1" }, {id: "pp182" }]

// create an array of all items and reduce it in a reversed order to a Map
const dupsMap = [...arr_p1, ...arr_p2, ...arr_p3]
  // create the Map by using the `id` as the key, and the object as the value
  .reduceRight((acc, o) => acc.set(o.id, o), new Map())
  
const filterArr = arr => arr.filter(o =>
  dupsMap.get(o.id) === o // keep the item if it was the object that was used as value
)
  
const arr_p1f = filterArr(arr_p1)
const arr_p2f = filterArr(arr_p2)
const arr_p3f = filterArr(arr_p3)

console.log({ arr_p1f, arr_p2f, arr_p3f })

您可以轻松创建一个可以处理任意数量数组的通用函数,并使用解构从其返回值中获取各个数组。

const dedupArrays = (...arrs) => {
  const dupsMap = arrs.flat() // convert arrays to a single array
    // a reduce right to create a Map of [id, object]
    .reduceRight((acc, o) => acc.set(o.id, o), new Map())
    
  // map the array of arrays, and filter each sub array
  return arrs.map(arr => arr.filter(o => dupsMap.get(o.id) === o))
}

const arr_p1 = [{ id: "892d" }, {id: "kla8x" }, {id: "sys32" }]
const arr_p2 = [{id: "saa1" }, { id: "892d" }]
const arr_p3 = [{ id: "kla8x" }, {id: "saa1" }, {id: "pp182" }]

const [arr_p1f, arr_p2f, arr_p3f] = dedupArrays(arr_p1, arr_p2, arr_p3)

console.log({ arr_p1f, arr_p2f, arr_p3f })

【讨论】:

    【解决方案2】:

    您可以生成一个偏好对象(哈希映射)来将 id 映射到偏好。从第三个数组运行到第一个数组,这样较低的顺序会覆盖较高的顺序。

    然后,当您拥有偏好映射时,您可以通过检查 id 的偏好是否与当前数组匹配来过滤所有数组。

    let arr_p1 = [{ id: "892d" }, {id: "kla8x" }, {id: "sys32" }];
    let arr_p2 = [{id: "saa1" }, { id: "892d" }];
    let arr_p3 = [{ id: "kla8x" }, {id: "saa1" }, {id: "pp182" }];
    
    let pref = {};
    
    arr_p3.forEach(e => pref[e.id] = 3);
    arr_p2.forEach(e => pref[e.id] = 2);
    arr_p1.forEach(e => pref[e.id] = 1);
    
    arr_p1 = arr_p1.filter(e => pref[e.id] === 1);
    arr_p2 = arr_p2.filter(e => pref[e.id] === 2);
    arr_p3 = arr_p3.filter(e => pref[e.id] === 3);
    
    console.log(arr_p1);
    console.log(arr_p2);
    console.log(arr_p3);

    【讨论】:

      【解决方案3】:

      我有几个提示给你,而不是一个完整的答案,因为我认为这是一个家庭作业问题?

      策略

      构建一组“已看到的项目”

      检查每个新数组,删除所有重复条目(在新数组中)。

      从最喜欢的数组开始

      这样,每当删除某些内容时,它就会从不太受欢迎的数组中删除。

      例如,在伪代码中

      let elementsSeen = new Set(   most preferred array of elements   )
      
      for array in listOfArraysInDecreasingOrderOfPreference {
      
        for element in array {
          if element is in elementsSeen, delete it from array
        }
        elementsSeen = union of elementsSeen and array
      }
      

      复杂性

      每个项目都必须查看。它必须与每个其他项目进行比较,但其复杂性不必很大,因为“设置”过程可以使用散列,即不必对每个传入对象与每个现有对象进行单独比较。几乎所有传入的对象都会有一个与现有对象不同的哈希表值,这很快,但代价是花费了一些时间在哈希上,并在表上花费了一些内存。

      在最坏的情况下,散列不再帮助您,它是 O(N x M),其中 N 是数组的数量,M 是每个数组的大小。

      【讨论】:

        【解决方案4】:

        您的问题暗示您想要改变原始数组。

        所以如果你仍然想改变数组,你可以。

        1. 为每个级别创建一组 ID。
        2. 向后循环每个级别,如果任何 id 位于更高级别,则从数组中删除。

        这里也有一些优化,例如。 slice(0, -1),所以我们不需要为最后一个级别创建一个 SET,就像检查以前的级别一样。一旦已知项目被删除,在循环内部,使用中断然后继续下一步。老实说,我不知道这有什么复杂性.. :)

        例如。

        const arr_p1 = 
          [{ id: "892d" }, {id: "kla8x" }, {id: "sys32" }];
        const arr_p2 = 
          [{id: "saa1" }, { id: "892d" }];
        const arr_p3 = 
          [{ id: "kla8x" }, {id: "saa1" }, {id: "pp182" }];
          
          
        function dedupe(alist) {
          const hasList = alist.map(
            m => new Set(m.slice(0, -1).map(i => i.id)));
          for (let l = alist.length -1; l > 0; l --) {
            for (let i = alist[l].length -1; i >= 0; i --) {
              for (let h = 0; h < l; h += 1) {
                if (hasList[h].has(alist[l][i].id)) {
                  alist[l].splice(i, 1);
                  break;
                }
              }
            }
          }
        }
        
        
        dedupe([arr_p1, arr_p2, arr_p3]);
        console.log(arr_p1);
        console.log(arr_p2);
        console.log(arr_p3);

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-06-19
          • 2023-03-15
          • 2017-12-31
          • 2020-10-16
          • 2021-05-09
          • 2013-02-02
          • 2020-12-14
          • 2023-03-09
          相关资源
          最近更新 更多