# The unexpected mutability when editing nested arrays

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:

```js
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)
```

[Interactive example](/editing-nested-arrays)

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:

```js {6,7,8,9}
const filteredTable = tableData.map(data => {
  const filteredColumns = data.rows.filter(row => {
    return row.name.includes('red')
  })

  return {
    title: data.title,
    rows: filteredColumns,
  }
})
```

[Interactive example](/editing-nested-arrays)

Or spread the `data`, if that’s more convenient:

```js
return {
  ...data,
  rows: filteredColumns,
}
```

Or use `Object.assign()`, whatever is the best for your use-case:

```js
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!
