Search code examples
javascriptarraysrecursionfiltersplice

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


I want to iterate over this 2-dimensional array. Arrays with multiple objects have multiple entries in the same month (in the example below January). I want to filter out (reject) the duplicate entries and want to return the altered array.

I tried different solutions here on SO but failed so far; I will appreciate any input!

[[
  { "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" }
]]

Edit answer to traktor: I guess the minimum would be to check if every subarray is unique. 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.

[
  [{
    "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"
  }]
]

Solution

  • What the OP actually means with ...

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

    ... is that the OP wants to mutate either the provided data structure directly or maybe a structured clone of it. This becomes more clear with ...

    "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."

    Thus one has to come up with a recursively working rejecting approach.

    The provided implementation uses a lookup for a custom key (property name) specific value. In case the value does not yet exist, the iteration proceeds but the value gets assigned to the lookup. Any array item which features the same, already assigned, value gets spliceed from the array (which is a mutation task) and pushed into the local rejected array. The function's return value is an object which features two arrays ... mutated with a reference to the passed and processed/mutated data structure and rejected which is a flat list of items that were rejected from the provided data structure.

    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; }