Michael Precel
Michael Precel

Reputation: 3

Reducing array of objects based on object's inner array

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]
        }, [])
      }

Upvotes: 0

Views: 55

Answers (2)

Nitheesh
Nitheesh

Reputation: 19986

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

Upvotes: 1

pilchard
pilchard

Reputation: 12909

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

Upvotes: 0

Related Questions