-1

I have an array of objects(shopping cart) and I want to remove duplicates that have same obj.id and obj.arr an empty array.After that to increase obj.quantity by one. Here is the original arr:

const cart = [
    {
        "id": 1,
        "name": "book",
        "arr": [],
        "quantity": 1
    },
    {
        "id": 2,
        "name": "pen",
        "arr": ["asa", "asd"],
        "quantity": 3
    },
    {
        "id": 1,
        "name": "book",
        "arr": [],
        "quantity": 1
    },
    {
        "id": 3,
        "name": "ball pen",
        "arr": ["azx", "dcv"],
        "quantity": 1
    }
]

Expected output should be like this:

const cart = [
    {
        "id": 1,
        "name": "book",
        "arr": [],
        "quantity": 2
    },
    {
        "id": 2,
        "name": "pen",
        "arr": ["asa", "asd"],
        "quantity": 3
    },
    {
        "id": 3,
        "name": "ball pen",
        "arr": ["azx", "dcv"],
        "quantity": 1
    }
]

I don't know how to update object.quantity after removing the duplicate. And if possible I want to keep the item with smallest index.
Could you please help me?
Thank you

Prgrmist
  • 83
  • 1
  • 9

3 Answers3

0

Modified code from How to remove all duplicates from an array of objects?. instead of filter I used reduce to then find and update the quantity of the duplicated item

const cart = [
    {
        "id": 1,
        "name": "book",
        "arr": [],
        "quantity": 1
    },
    {
        "id": 2,
        "name": "pen",
        "arr": ["asa", "asd"],
        "quantity": 3
    },
    {
        "id": 1,
        "name": "book",
        "arr": [],
        "quantity": 1
    },
    {
        "id": 3,
        "name": "ball pen",
        "arr": ["azx", "dcv"],
        "quantity": 1
    }
]

function findDupUpdateQty(arr){
return arr.reduce((acc, currentVal, index, self) => {
   if(index === self.findIndex((e)=> e.id === currentVal.id && e.arr.length === currentVal.arr.length)){
     acc.push(currentVal)
   } else {
   //if there's a duplicate then update quanitity
    acc[acc.findIndex((e)=> e.id === currentVal.id && e.arr.length === currentVal.arr.length)].quantity += currentVal.quantity
   }
   return acc
},[])
}

console.log(findDupUpdateQty(cart))

Edits with some of @Corrl suggestions:

const cart = [
    {
        "id": 1,
        "name": "book",
        "arr": [],
        "quantity": 1
    },
    {
        "id": 2,
        "name": "pen",
        "arr": ["asa", "asd"],
        "quantity": 3
    },
    {
        "id": 1,
        "name": "book",
        "arr": [],
        "quantity": 1
    },
    {
        "id": 3,
        "name": "ball pen",
        "arr": ["azx", "dcv"],
        "quantity": 1
    }
]

function findDupUpdateQty(arr){
return arr.reduce((acc, currentVal, index, self) => {                     //check if both arrays are the same
   let firstFoundIndex = self.findIndex((e)=> e.id === currentVal.id && JSON.stringify(e.arr) === JSON.stringify(currentVal.arr))
   if(index === firstFoundIndex){
     acc.push(currentVal)
   } else {
    self[firstFoundIndex].quantity += currentVal.quantity
   }
   return acc
},[])
}

console.log(findDupUpdateQty(cart))
knicholas
  • 520
  • 4
  • 10
  • When I run this the quantity of the first object says 5 (should be 2) – Corrl Jan 15 '22 at 20:24
  • 1
    that's because I change the quantity of the second book item to 4, for testing. I've changed it back to 1 now. – knicholas Jan 15 '22 at 20:40
  • 1
    Ah I didn't see that. Wouldn't do that since the OP gave the expected result and it might be confusing (like it just was :-)) Not sure about the comparing of the arr.lengths, since the OP said *"obj.arr an empty array"* I made a [REPL](https://jsbin.com/kifaqugixi/edit?css,js,console) with a modified version of your approach. When putting some logic into variables like `firstFoundIndex` it becomes easier to read and understand and is definitely way shorter than my solution... – Corrl Jan 15 '22 at 20:51
  • 1
    @Corrl definitely easier to read. I think comparing arrays content might be best here. In the event a new item is added, same `id` but different `arr`, the item would still be added to the result. Also, the quantity, not sure if it suppose to just increment or add both quantities together. – knicholas Jan 15 '22 at 21:21
  • 1
    Yes, how many duplicates there can be and how exactly the quantity should be modified is not absolutely clear, the OP could specify this. If the condition is "both arrays should be empty" I consider checking `arr.length === 0` the most accurate (or only?) solution... before you compared the length, so `[1].length === [2].length` would have been true, but both arrays are not empty. Now with comparing the stringified version you might have this case `"[1]" === "[1]"` resolving to true - but it doesn't meet the criteria and will bring unwanted results... – Corrl Jan 15 '22 at 21:44
0

To keep the order I would

  • build up a map with the id as key and an array with the objects as value
  • iterate this map and check for duplicates (= more than one object in array)
  • check for the empty array field on all duplicates
  • increase quantity on first occuring and overwrite existing entry (assuming all duplicates have the same quantity and it should only be increased by one, even if there are more than two duplicates)
  • rebuild cart

const cart = [{
  "id": 1,
  "name": "book",
  "arr": [],
  "quantity": 1
}, {
  "id": 2,
  "name": "pen",
  "arr": ["asa", "asd"],
  "quantity": 3
}, {
  "id": 1,
  "name": "book",
  "arr": [],
  "quantity": 1
}, {
  "id": 3,
  "name": "ball pen",
  "arr": ["azx", "dcv"],
  "quantity": 1
}]

function removeDuplicates(cart) {
  
  let map = new Map()

  // groupBy id
  cart.forEach(obj => {
    let entry = map.has(obj.id)
    if (entry) {
      let value = map.get(obj.id)
      map.set(obj.id, [...value, obj])
    } else {
      map.set(obj.id, [obj])
    }
  })


  map.forEach((value, key) => {

    // doubles?
    if (value.length > 1) {

      let allEmptyArrays = value.every(obj => obj.arr.length === 0)

      if (allEmptyArrays) {
        // keep only the first value
        let keepValue = value[0]
        // adjust quantity
        keepValue.quantity++
        // overwrite existing entry
        map.set(key, [keepValue])
      }
    }
  })

  // rebuild cart
  return Array.from(map).map(([_, value]) => value[0])
}

console.log(removeDuplicates(cart))
Corrl
  • 6,206
  • 1
  • 10
  • 36
0

Thank you both!
I was planing to use one of your answers inside of selector(createSelector) from 'reselect' library. But looks like, it is incrementing obj.quantity, for all products, not only for those with obj.arr an empty array.
So I did more research, and I changed my addToCartReducer, based on this post. Now all the action is happening inside the reducer, not inside the selector. Every time I add to cart a product with obj.arr an empty array it increments its quantity by 1.

Prgrmist
  • 83
  • 1
  • 9