0

I have a dataset like so:

[
  {
    id: 'abc',
    productType: "Nappies"
    variants: ['3 Pack of Nappies', 'small']
  },
  {
    id: 'abc',
    productType: "Nappies"
    variants: ['6 Pack of Nappies', 'small']
  },
  {
    id: 'abc',
    productType: "Nappies"
    variants: ['12 Pack of Nappies', 'small']
  },
  {
    id: 'abc',
    productType: "Nappies"
    variants: ['3 Pack of Nappies', 'medium']
  },
  {
    id: 'abc',
    productType: "Nappies"
    variants: ['6 Pack of Nappies', 'medium']
  },
  {
    id: 'abc',
    productType: "Nappies"
    variants: ['12 Pack of Nappies', 'medium']
  },
  {
    id: 'def',
    productType: "Wipes"
    variants: ['3 Pack of Wipes']
  },
  {
    id: 'def',
    productType: "Wipes"
    variants: ['6 Pack of Wipes']
  },
  {
    id: 'def',
    productType: "Wipes"
    variants: ['12 Pack of Wipes']
  },
]

As you can see, each of the product IDs is repeated across all the variants. I need to reduce the data down so there is only 1 version of each product with the 'X packs of Nappies', eg:

Desired result:

[
  {
    id: 'abc',
    productType: "Nappies"
    variants: ['3 Pack of Nappies', 'small']
  },
  {
    id: 'abc',
    productType: "Nappies"
    variants: ['6 Pack of Nappies', 'small']
  },
  {
    id: 'abc',
    productType: "Nappies"
    variants: ['12 Pack of Nappies', 'small']
  },
  {
    id: 'def',
    productType: "Wipes"
    variants: ['3 Pack of Wipes']
  },
  {
    id: 'def',
    productType: "Wipes"
    variants: ['6 Pack of Wipes']
  },
  {
    id: 'def',
    productType: "Wipes"
    variants: ['12 Pack of Wipes']
  },
]

The secondary variant in each (size, eg 'small'/'medium') is not important. I've tried running a reduce function that runs a filter and returns true if the reduce object's 'variants' property includes any of the active object's 'variant's property but it returns everything.

      const RemoveDuplicates = (array) => {
        return array.reduce((arr, item) => {
          const removed = arr.filter((obj) => {
            return obj.selectedOptions.some((i) =>
              item.selectedOptions.includes(i)
            )
          })
          return [...removed, item]
        }, [])
      }
  • 1
    It is not clear from your output example what you are asking. Are you sure your output is representative? – mplungjan Sep 01 '21 at 08:56
  • It looks from your expected output that you just want to filter duplicates by `variants` but I get the feeling you actually want to group by `variant` and collect `id`s? – pilchard Sep 01 '21 at 08:57

2 Answers2

1

All you have to is to find a match from the accumulator with below condition

Same id, same productType and same variants[0]

Logic

  • Loop through the array using Array.reduce.
  • The first paramater acc is accumulator, which holds the list of accumulation. Next parameter curr is the current value which holds each node of the array while looping. What you have to do is to push the current value to the accumulator if the requirement is satisfied.
  • To check the requirement, you have to check each curr value is there in the acc. If there is a node in the accumulator against each curr having same id, productType and variants[0] which you call as the version. If the accumulator already have a node with the matching combination, dont push the current value to the accumulator.

const input = [
  { id: 'abc', productType: "Nappies", variants: ['3 Pack of Nappies', 'small'] },
  { id: 'abc', productType: "Nappies", variants: ['6 Pack of Nappies', 'small'] },
  { id: 'abc', productType: "Nappies", variants: ['12 Pack of Nappies', 'small'] },
  { id: 'abc', productType: "Nappies", variants: ['3 Pack of Nappies', 'medium'] },
  { id: 'abc', productType: "Nappies", variants: ['6 Pack of Nappies', 'medium'] },
  { id: 'abc', productType: "Nappies", variants: ['12 Pack of Nappies', 'medium'] },
  { id: 'def', productType: "Wipes", variants: ['3 Pack of Wipes'] },
  { id: 'def', productType: "Wipes", variants: ['6 Pack of Wipes'] },
  { id: 'def', productType: "Wipes", variants: ['12 Pack of Wipes'] },
];

const output = input.reduce((acc, curr) => {
  const node = acc.find((item) =>
    item.id === curr.id &&
    item.productType === curr.productType &&
    item.variants[0] === curr.variants[0]);
    if(!node) {
      acc.push(curr)
    }
  return acc;
}, []);

console.log(output);
Nitheesh
  • 19,238
  • 3
  • 22
  • 49
  • This is very helpful, thanks @nitheesh! I wasn't quite grasping the reducer but this is simple enough, and happy you used ES6. – Michael Precel Sep 02 '21 at 03:09
  • @MichaelPrecel I have updated the answer with the reducer logic, Please check that. Please approve the answer if this is what you are looking for. Happy to have an upvote aswell. – Nitheesh Sep 02 '21 at 03:53
0

You can do this as a standard group-by with a composite key, but instead of accumulating, use the key as a check for duplicates.

const input = [  { id: 'abc', productType: "Nappies", variants: ['3 Pack of Nappies', 'small'] },  { id: 'abc', productType: "Nappies", variants: ['6 Pack of Nappies', 'small'] },  { id: 'abc', productType: "Nappies", variants: ['12 Pack of Nappies', 'small'] },  { id: 'abc', productType: "Nappies", variants: ['3 Pack of Nappies', 'medium'] },  { id: 'abc', productType: "Nappies", variants: ['6 Pack of Nappies', 'medium'] },  { id: 'abc', productType: "Nappies", variants: ['12 Pack of Nappies', 'medium'] },  { id: 'def', productType: "Wipes", variants: ['3 Pack of Wipes'] },  { id: 'def', productType: "Wipes", variants: ['6 Pack of Wipes'] },  { id: 'def', productType: "Wipes", variants: ['12 Pack of Wipes'] },];

const output = Object.values(input.reduce((a, c) => {
  const key = `${c.id}_${c.productType}_${c.variants[0]}`;
  if (!a.hasOwnProperty(key)) {
      a[key] = {...c};
  }
  return a;
}, {}));

console.log(output);
.as-console-wrapper { max-height: 100% !important; top: 0; }
pilchard
  • 12,414
  • 5
  • 11
  • 23