如何遍历数组数组以过滤掉或拒绝重复条目?

How to iterate over an array of arrays to filter out or reject duplicate entries?

我想遍历这个二维数组。具有多个对象的数组在同一个月(在下面的示例中为一月)中有多个条目。我想过滤掉(拒绝)重复的条目并想要 return 改变的数组。

我在这里尝试了不同的解决方案,但到目前为止都失败了;我将不胜感激!

[[
  { "id":"9","bookingReason":"Netto Neu Eigen","bookingDate":"2021-01-22","bookingType":"Gutschrift","bookingPoints":"100" },
  { "id":"10","bookingReason":"Netto Neu Eigen","bookingDate":"2021-01-23","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"9","bookingReason":"Netto Neu Eigen","bookingDate":"2021-01-22","bookingType":"Gutschrift","bookingPoints":"100" },
  { "id":"10","bookingReason":"Netto Neu Eigen","bookingDate":"2021-01-23","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"29","bookingReason":"Netto Neu Eigen","bookingDate":"2022-01-22","bookingType":"Gutschrift","bookingPoints":"100" },
  { "id":"30","bookingReason":"Netto Neu Eigen","bookingDate":"2022-01-23","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"29","bookingReason":"Netto Neu Eigen","bookingDate":"2022-01-22","bookingType":"Gutschrift","bookingPoints":"100" },
  { "id":"30","bookingReason":"Netto Neu Eigen","bookingDate":"2022-01-23","bookingType":"Gutschrift","bookingPoints":"100" }
]]

编辑对拖拉机的回答: 我想至少要检查每个子数组是否都是唯一的。 因此,如果过滤器第二次找到相同的 ID,则应删除整个重复数组。

编辑对 Peter Seliger 的回答: 结果应该保持二维结构。

[
  [{
    "id": "1",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2021-09-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }], [{
    "id": "2",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2021-08-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }], [{
    "id": "3",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2021-07-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }], [{
    "id": "4",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2021-06-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }], [{
    "id": "5",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2021-05-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }], [{
    "id": "6",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2021-04-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }], [{
    "id": "7",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2021-03-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }, {
    "id": "12",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2021-03-24",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }], [{
    "id": "8",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2021-02-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }, {
    "id": "11",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2021-02-23",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }], [{
    "id": "9",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2021-01-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }, {
    "id": "10",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2021-01-23",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }], [{
    "id": "9",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2021-01-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }, {
    "id": "10",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2021-01-23",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }], [{
    "id": "8",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2021-02-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }, {
    "id": "11",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2021-02-23",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }], [{
    "id": "7",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2021-03-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }, {
    "id": "12",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2021-03-24",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }], [{
    "id": "21",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2022-09-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }], [{
    "id": "22",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2022-08-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }], [{
    "id": "23",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2022-07-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }], [{
    "id": "24",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2022-06-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }], [{
    "id": "25",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2022-05-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }], [{
    "id": "26",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2022-04-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }], [{
    "id": "27",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2022-03-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }, {
    "id": "32",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2022-03-24",
    "bookingType": "Gutschrift",
    "bookingPoints": "200"
  }], [{
    "id": "28",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2022-02-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }, {
    "id": "31",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2022-02-23",
    "bookingType": "Gutschrift",
    "bookingPoints": "400"
  }], [{
    "id": "29",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2022-01-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }, {
    "id": "30",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2022-01-23",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }], [{
    "id": "29",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2022-01-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }, {
    "id": "30",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2022-01-23",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }], [{
    "id": "28",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2022-02-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }, {
    "id": "31",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2022-02-23",
    "bookingType": "Gutschrift",
    "bookingPoints": "400"
  }], [{
    "id": "27",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2022-03-22",
    "bookingType": "Gutschrift",
    "bookingPoints": "100"
  }, {
    "id": "32",
    "bookingReason": "Netto Neu Eigen",
    "bookingDate": "2022-03-24",
    "bookingType": "Gutschrift",
    "bookingPoints": "200"
  }]
]

OP 的实际含义...

"I want to filter out the double entries and want to return the filtered array."

... 是 OP 想要直接改变提供的数据结构或者可能是它的 structured clone。这变得更加清楚...

"So if the filter finds for example the same id a second time, the whole duplicate array should get removed."

"Edit answer to Peter Seliger: The result should keep the 2-dimensional structure."

因此必须想出一种递归工作 rejecting 方法。

提供的实现使用自定义键(属性 名称)特定值的查找。如果该值尚不存在,则迭代继续进行,但该值将分配给查找。任何具有相同、已分配值的数组项都会从数组中获取 spliceed(这是一个变异任务)并推入本地 rejected 数组。函数的 return 值是一个对象,它具有两个数组...... mutated 引用传递的和 processed/mutated 数据结构和 rejected 这是项目的平面列表被提供的数据结构拒绝。

function rejecItemsOfSameKeyAndValueRecursively(
  arr = [], key = '', lookup = new Map,
) {
  const rejected = [];

  for (let idx = 0; idx < arr.length; idx++) {
    const item = arr[idx];

    if (Array.isArray(item)) {

      // recursion in order to handle nested array structures.
      rejected
        .push(
          ...rejecItemsOfSameKeyAndValueRecursively(item, key, lookup).rejected
        );

      // ... and in case one wants to also get rid
      //     of the now possibly empty array item ...
      if (item.length === 0) {
        // remove empty array item.
        arr.splice(idx, 1);
        // re/adjust the proceeding index value.
        --idx; 
      }

    } else if (!!item && typeof item === 'object') {
      const value = item[key];

      if (lookup.has(value)) {

        rejected
          .push(
            // remove duplicate item from array.
            arr.splice(idx, 1)
          );

        --idx; // re/adjust the proceeding index value.

      } else {
        lookup.set(value, value);
      }
    }
  }
  return { mutated: arr, rejected };
}

const sampleData = [[
  { "id":"1","bookingReason":"Netto Neu Eigen","bookingDate":"2021-09-22","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"2","bookingReason":"Netto Neu Eigen","bookingDate":"2021-08-22","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"3","bookingReason":"Netto Neu Eigen","bookingDate":"2021-07-22","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"4","bookingReason":"Netto Neu Eigen","bookingDate":"2021-06-22","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"5","bookingReason":"Netto Neu Eigen","bookingDate":"2021-05-22","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"6","bookingReason":"Netto Neu Eigen","bookingDate":"2021-04-22","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"7","bookingReason":"Netto Neu Eigen","bookingDate":"2021-03-22","bookingType":"Gutschrift","bookingPoints":"100" },
  { "id":"12","bookingReason":"Netto Neu Eigen","bookingDate":"2021-03-24","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"8","bookingReason":"Netto Neu Eigen","bookingDate":"2021-02-22","bookingType":"Gutschrift","bookingPoints":"100" },
  { "id":"11","bookingReason":"Netto Neu Eigen","bookingDate":"2021-02-23","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"9","bookingReason":"Netto Neu Eigen","bookingDate":"2021-01-22","bookingType":"Gutschrift","bookingPoints":"100" },
  { "id":"10","bookingReason":"Netto Neu Eigen","bookingDate":"2021-01-23","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"9","bookingReason":"Netto Neu Eigen","bookingDate":"2021-01-22","bookingType":"Gutschrift","bookingPoints":"100" },
  { "id":"10","bookingReason":"Netto Neu Eigen","bookingDate":"2021-01-23","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"8","bookingReason":"Netto Neu Eigen","bookingDate":"2021-02-22","bookingType":"Gutschrift","bookingPoints":"100" },
  { "id":"11","bookingReason":"Netto Neu Eigen","bookingDate":"2021-02-23","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"7","bookingReason":"Netto Neu Eigen","bookingDate":"2021-03-22","bookingType":"Gutschrift","bookingPoints":"100" },
  { "id":"12","bookingReason":"Netto Neu Eigen","bookingDate":"2021-03-24","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"21","bookingReason":"Netto Neu Eigen","bookingDate":"2022-09-22","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"22","bookingReason":"Netto Neu Eigen","bookingDate":"2022-08-22","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"23","bookingReason":"Netto Neu Eigen","bookingDate":"2022-07-22","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"24","bookingReason":"Netto Neu Eigen","bookingDate":"2022-06-22","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"25","bookingReason":"Netto Neu Eigen","bookingDate":"2022-05-22","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"26","bookingReason":"Netto Neu Eigen","bookingDate":"2022-04-22","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"27","bookingReason":"Netto Neu Eigen","bookingDate":"2022-03-22","bookingType":"Gutschrift","bookingPoints":"100" },
  { "id":"32","bookingReason":"Netto Neu Eigen","bookingDate":"2022-03-24","bookingType":"Gutschrift","bookingPoints":"200" }
], [
  { "id":"28","bookingReason":"Netto Neu Eigen","bookingDate":"2022-02-22","bookingType":"Gutschrift","bookingPoints":"100" },
  { "id":"31","bookingReason":"Netto Neu Eigen","bookingDate":"2022-02-23","bookingType":"Gutschrift","bookingPoints":"400" }
], [
  { "id":"29","bookingReason":"Netto Neu Eigen","bookingDate":"2022-01-22","bookingType":"Gutschrift","bookingPoints":"100" },
  { "id":"30","bookingReason":"Netto Neu Eigen","bookingDate":"2022-01-23","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"29","bookingReason":"Netto Neu Eigen","bookingDate":"2022-01-22","bookingType":"Gutschrift","bookingPoints":"100" },
  { "id":"30","bookingReason":"Netto Neu Eigen","bookingDate":"2022-01-23","bookingType":"Gutschrift","bookingPoints":"100" }
], [
  { "id":"28","bookingReason":"Netto Neu Eigen","bookingDate":"2022-02-22","bookingType":"Gutschrift","bookingPoints":"100" },
  { "id":"31","bookingReason":"Netto Neu Eigen","bookingDate":"2022-02-23","bookingType":"Gutschrift","bookingPoints":"400" }
], [
  { "id":"27","bookingReason":"Netto Neu Eigen","bookingDate":"2022-03-22","bookingType":"Gutschrift","bookingPoints":"100" },
  { "id":"32","bookingReason":"Netto Neu Eigen","bookingDate":"2022-03-24","bookingType":"Gutschrift","bookingPoints":"200" }
]];

const cloneDataStructure = (typeof structuredClone === 'function')
  ? structuredClone
  : value => JSON.parse(JSON.stringify(value));


const data = cloneDataStructure(sampleData);
const { mutated, rejected } = rejecItemsOfSameKeyAndValueRecursively(data, 'id');

console.log(
  '... mutated/rejected by `id` ... ',
);
console.log(
  '(mutated === data) ?..',
  (mutated === data),
);
console.log(
  { mutated, rejected },
);


const data_2 = cloneDataStructure(sampleData);
const { mutated: mutated_2, rejected: rejected_2 } =
  rejecItemsOfSameKeyAndValueRecursively(cloneDataStructure(data_2), 'bookingPoints');

console.log(
  '... mutated/rejected by `bookingPoints` ... ',
);
console.log(
  '(mutated_2 === data_2) ?..',
  (mutated_2 === data_2),
);
console.log(
  { mutated_2, rejected_2 },
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

我不确定我是否正确地阅读了这个问题,但如果你需要做的就是采用这个参差不齐的二维数组并从任何嵌套数组中过滤掉重复的条目,那么我认为它是很简单:

const dedupe = (
  data, found = new Set(), result,
  test = (x) => ((result = ! found .has (x .id)), (found .add (x .id)), result)
) => data .map ((group) => group .filter (test)) .filter (x => x .length > 0)


const data = [[{id: "1", bookingReason: "Netto Neu Eigen", bookingDate: "2021-09-22", bookingType: "Gutschrift", bookingPoints: "100"}], [{id: "2", bookingReason: "Netto Neu Eigen", bookingDate: "2021-08-22", bookingType: "Gutschrift", bookingPoints: "100"}], [{id: "3", bookingReason: "Netto Neu Eigen", bookingDate: "2021-07-22", bookingType: "Gutschrift", bookingPoints: "100"}], [{id: "4", bookingReason: "Netto Neu Eigen", bookingDate: "2021-06-22", bookingType: "Gutschrift", bookingPoints: "100"}], [{id: "5", bookingReason: "Netto Neu Eigen", bookingDate: "2021-05-22", bookingType: "Gutschrift", bookingPoints: "100"}], [{id: "6", bookingReason: "Netto Neu Eigen", bookingDate: "2021-04-22", bookingType: "Gutschrift", bookingPoints: "100"}], [{id: "7", bookingReason: "Netto Neu Eigen", bookingDate: "2021-03-22", bookingType: "Gutschrift", bookingPoints: "100"}, {id: "12", bookingReason: "Netto Neu Eigen", bookingDate: "2021-03-24", bookingType: "Gutschrift", bookingPoints: "100"}], [{id: "8", bookingReason: "Netto Neu Eigen", bookingDate: "2021-02-22", bookingType: "Gutschrift", bookingPoints: "100"}, {id: "11", bookingReason: "Netto Neu Eigen", bookingDate: "2021-02-23", bookingType: "Gutschrift", bookingPoints: "100"}], [{id: "9", bookingReason: "Netto Neu Eigen", bookingDate: "2021-01-22", bookingType: "Gutschrift", bookingPoints: "100"}, {id: "10", bookingReason: "Netto Neu Eigen", bookingDate: "2021-01-23", bookingType: "Gutschrift", bookingPoints: "100"}], [{id: "9", bookingReason: "Netto Neu Eigen", bookingDate: "2021-01-22", bookingType: "Gutschrift", bookingPoints: "100"}, {id: "10", bookingReason: "Netto Neu Eigen", bookingDate: "2021-01-23", bookingType: "Gutschrift", bookingPoints: "100"}], [{id: "8", bookingReason: "Netto Neu Eigen", bookingDate: "2021-02-22", bookingType: "Gutschrift", bookingPoints: "100"}, {id: "11", bookingReason: "Netto Neu Eigen", bookingDate: "2021-02-23", bookingType: "Gutschrift", bookingPoints: "100"}], [{id: "7", bookingReason: "Netto Neu Eigen", bookingDate: "2021-03-22", bookingType: "Gutschrift", bookingPoints: "100"}, {id: "12", bookingReason: "Netto Neu Eigen", bookingDate: "2021-03-24", bookingType: "Gutschrift", bookingPoints: "100"}], [{id: "21", bookingReason: "Netto Neu Eigen", bookingDate: "2022-09-22", bookingType: "Gutschrift", bookingPoints: "100"}], [{id: "22", bookingReason: "Netto Neu Eigen", bookingDate: "2022-08-22", bookingType: "Gutschrift", bookingPoints: "100"}], [{id: "23", bookingReason: "Netto Neu Eigen", bookingDate: "2022-07-22", bookingType: "Gutschrift", bookingPoints: "100"}], [{id: "24", bookingReason: "Netto Neu Eigen", bookingDate: "2022-06-22", bookingType: "Gutschrift", bookingPoints: "100"}], [{id: "25", bookingReason: "Netto Neu Eigen", bookingDate: "2022-05-22", bookingType: "Gutschrift", bookingPoints: "100"}], [{id: "26", bookingReason: "Netto Neu Eigen", bookingDate: "2022-04-22", bookingType: "Gutschrift", bookingPoints: "100"}], [{id: "27", bookingReason: "Netto Neu Eigen", bookingDate: "2022-03-22", bookingType: "Gutschrift", bookingPoints: "100"}, {id: "32", bookingReason: "Netto Neu Eigen", bookingDate: "2022-03-24", bookingType: "Gutschrift", bookingPoints: "200"}], [{id: "28", bookingReason: "Netto Neu Eigen", bookingDate: "2022-02-22", bookingType: "Gutschrift", bookingPoints: "100"}, {id: "31", bookingReason: "Netto Neu Eigen", bookingDate: "2022-02-23", bookingType: "Gutschrift", bookingPoints: "400"}], [{id: "29", bookingReason: "Netto Neu Eigen", bookingDate: "2022-01-22", bookingType: "Gutschrift", bookingPoints: "100"}, {id: "30", bookingReason: "Netto Neu Eigen", bookingDate: "2022-01-23", bookingType: "Gutschrift", bookingPoints: "100"}], [{id: "29", bookingReason: "Netto Neu Eigen", bookingDate: "2022-01-22", bookingType: "Gutschrift", bookingPoints: "100"}, {id: "30", bookingReason: "Netto Neu Eigen", bookingDate: "2022-01-23", bookingType: "Gutschrift", bookingPoints: "100"}], [{id: "28", bookingReason: "Netto Neu Eigen", bookingDate: "2022-02-22", bookingType: "Gutschrift", bookingPoints: "100"}, {id: "31", bookingReason: "Netto Neu Eigen", bookingDate: "2022-02-23", bookingType: "Gutschrift", bookingPoints: "400"}], [{id: "27", bookingReason: "Netto Neu Eigen", bookingDate: "2022-03-22", bookingType: "Gutschrift", bookingPoints: "100"}, {id: "32", bookingReason: "Netto Neu Eigen", bookingDate: "2022-03-24", bookingType: "Gutschrift", bookingPoints: "200"}]]

console .log (dedupe (data))
.as-console-wrapper {max-height: 100% !important; top: 0}

我们写了一些我通常试图避免的东西:一个有状态的函数关闭我们目前看到的 ids 的可变集合。此函数用作嵌套数组上 filter 调用的回调。我倾向于避免有状态函数,但替代方法似乎是复杂的嵌套折叠,而且效果很好。

我假设我们不希望在删除任何基本重复项后留下空组。如果这个假设是错误的,我们可以简单地删除最后的 filter 调用。

那个版本在语法上很密集。以下变体可能对某些人更有吸引力:

const dedupe = (data) => {
  const found = new Set()
  const test = (x) => {
    const result = ! found .has (x .id)
    found .add (x .id)
    return result
  }
  return data .map ((group) => group .filter (test)) 
              .filter (x => x .length > 0)
}

他们做同样的事情。