Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deleting object in nested array in an immutable way

Tags:

javascript

So this is how my data looks:

categories = [
  {
    name: ""
    products: [
      {
        id: 1,
        ...
      },
      {
        id: 2,
        ...
      }
    ]
  },
  {
    name: ""
    products: [
      {
        id: 3,
        ...
      },
      {
        id: 4,
        ...
      }
    ]
  },
  ...
]

I want to remove a product with id 1 and this is my code:

categories.map(category => category.products.filter(product => product.id !== 1))

Is this the right code? If so how I create a new array and set it with the new values?

like image 407
Unknown Avatar asked Apr 23 '26 09:04

Unknown


2 Answers

You're close, but your code is replacing category objects with just their filtered products array, losing the rest of the object properties. You need to copy each category object as well as its products array, which you typically do via spread notation:

updatedCategories = categories.map(category => ({
    ...category,
    products: category.products.filter(product => product.id !== 1)
}));
like image 122
T.J. Crowder Avatar answered Apr 25 '26 21:04

T.J. Crowder


Just a modified version on T.J. Crowder's response.

The filterGroupedBy function is more versatile and de-coupled from the business logic.

I even created a lodash mixin that you can call via:

  • _.filterGroupedBy(list, key, fn) or
  • _.chain(list).filterGroupedBy(key, fn).value()

Demo

const disp = (value) => console.log(JSON.stringify(value))

const filterGroupedBy = (list, key, fn) => 
  list.map(item => ({
      ...item,
      [key] : item[key].filter(entry => fn(entry))
  }))
  
_.mixin({
  'filterGroupedBy' : (list, key, fn) => filterGroupedBy(list, key, fn)
})

let categories = [{
  name: "",
  products: [{ id: 1 }, { id: 2 }]
}, {
  name: "",
  products: [{ id: 3 }, { id: 4 }]
}]

// plain js
disp(filterGroupedBy(categories, 'products', (prod) => prod.id !== 1))

// lodash - static and chain
disp(_.filterGroupedBy(categories, 'products', (prod) => prod.id !== 1))
disp(_.chain(categories).filterGroupedBy('products', (prod) => prod.id !== 1).value())
.as-console-wrapper { top: 0; max-height: 100% !important; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

Nested Data

If you have nested data, you can iterate through a path to target the object you want to filter on.

You just need a function similar to Object.byPath (a custom polyfill).

const main = () => {
  let categories = [{
    name: "",
    products: {
      data : [{ id: 1 }, { id: 2 }]
    }
  }, {
    name: "",
    products: {
      data : [{ id: 3 }, { id: 4 }]
    }
  }]

  // plain js
  disp(filterGroupedBy(categories, 'products.data', p => p.id !== 1))
  
  // lodash - static and chain
  disp(_.filterGroupedBy(categories, 'products.data', p => p.id !== 1))
  disp(_.chain(categories).filterGroupedBy('products.data', p => p.id !== 1).value())
}

// Required by filterGroupedBy
if (Object.byPath === undefined) {
  Object.byPath = (obj, path) => path
    .replace(/\[(\w+)\]/g, '.$1')
    .replace(/^\./, '')
    .split(/\./g)
    .reduce((ref, key) => key in ref ? ref[key] : ref, obj)
}

/* #ifndef filterGroupedBy */
const filterGroupedBy = (list, path, fn) => 
  list.map(item => {
      let lastIndex = path.lastIndexOf('.'),
          target = path.substring(0, lastIndex),
          key = path.substring(lastIndex + 1)
      return Object.assign({}, Object.byPath(item, target), {
        [key] : Object.byPath(item, path).filter(entry => fn(entry))
      })
  })

_.mixin({
  'filterGroupedBy' : (list, key, fn) => filterGroupedBy(list, key, fn)
})
/* #endif */

const disp = (value) => console.log(JSON.stringify(value))
main()
.as-console-wrapper { top: 0; max-height: 100% !important; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
like image 29
Mr. Polywhirl Avatar answered Apr 25 '26 21:04

Mr. Polywhirl



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!