mediumJS ES5#117

Carrito (shopping cart function)

Prompt

Implement a shopping cart function that takes current cart state and a product with quantity (positive to add, negative to remove). Return a NEW cart state without mutating the input. If quantity reaches 0 or below, remove the product. Removing a product not in the cart returns the cart unchanged.

Solution

function updateCart(cart, product, quantity) {
  const existing = cart.findIndex(p => p.product === product)
  if (existing >= 0) {
    const updated = [...cart]
    const newQty = updated[existing].quantity + quantity
    if (newQty <= 0) return updated.filter((_, i) => i !== existing)
    updated[existing] = { ...updated[existing], quantity: newQty }
    return updated
  }
  return quantity > 0 ? [...cart, { product, quantity }] : cart
}
Mentor's take

This isn't really an algorithm question — it's an immutable state-update question, the exact contract of a Redux reducer or a useState updater: (state, action) → newState, never mutating state. React's change detection is reference equality; mutate the array in place and nothing re-renders. That's the class of problem this pattern is FOR.

The branch structure enumerates the cases explicitly: product exists → compute new quantity → either remove (filter) or replace; product absent → append if adding, no-op if removing. Each case returns a new array, and the updated item is itself a new object ({ ...item, quantity }) — copying the array but mutating the item inside it is the classic shallow-copy bug: the spread copies references, not contents, so updated[existing].quantity += n would still corrupt the original cart's item.

Complexity: findIndex + copy = O(n) time, O(n) space per update — the floor for immutable array updates, since a new array must be produced. The "naive" alternative here isn't slower, it's mutating: push/splice/item.quantity += are O(1) and wrong for this contract.

Note the ≤ 0 removal handles over-removal gracefully (quantity 1, remove 5 → gone, not negative), and removing a nonexistent product returns cart unchanged — by reference, which memoized consumers will thank you for.

Red flag: updated[existing].quantity += quantity after a spread — the array is new but the item is shared, so the "immutable" update mutates the caller's state. Spot-check: does every changed object have its own spread?

Say it: "It's a reducer: I return a new array and a new item object for the changed row — spreading the array alone copies references, so mutating the nested item would still leak into the original state."