I faced a completely thick problem when I was trying to filter a nested array
object. I kept mutating the original variable, even though .map() or
.filter() do not mutate the array they’re operating on.
More specifically I was generating tables from a array, and had a moment where I was sure I had discovered a bug JavaScript. That’s of course never the case, JavaScript worked as it should.
Unwanted mutation
In the following code I tried to filter out the objects with green apples:
const tableData = [
{
title: 'Apples',
rows: [
{ name: 'Pink Lady', color: 'light red' },
{ name: 'Granny Smith', color: 'green' },
{ name: 'Red Delicious', color: 'red' },
],
},
// More fruit tables here...
]
// First map the tables
const filteredTable = tableData.map(data => {
// Filter the rows
const filteredColumns = data.rows.filter(row => {
return row.color.includes('red')
})
// Put the filtered data back into the shape
// highlight-next-line
data.rows = filteredColumns
return data
})
// The original data will be mutated like the filtered data, this condition
// should evaluate `true`
const assert = JSON.stringify(tableData) === JSON.stringify(filteredTable)
window.alert(assert)At a glance this should work, since tableData is stored into a new variable as
it’s being mapped and filtered. But, tableData is 'magically' mutated to have
the same shape as the filtered data.
Achieving immutability
The unwanted mutation happens because the object inside the array is edited. New object needs to be created inside the filter:
const filteredTable = tableData.map(data => {
const filteredColumns = data.rows.filter(row => {
return row.name.includes('red')
})
return {
title: data.title,
rows: filteredColumns,
}
})Or spread the data, if that’s more convenient:
return {
...data,
rows: filteredColumns,
}Or use Object.assign(), whatever is the best for your use-case:
return Object.assign({}, data, {
rows: filteredColumns,
})Conclusions
I was contemplating if this is blog-post-worthy at all, but hope this comes helpful to someone searching.
Thanks for reading!