Remove a property in an object immutably

JavascriptImmutabilityRedux

Javascript Problem Overview


I am using Redux. In my reducer I'm trying to remove a property from an object like this:

const state = {
    a: '1',
    b: '2',
    c: {
       x: '42',
       y: '43'
    },
}

And I want to have something like this without having to mutate the original state:

const newState = {
    a: '1',
    b: '2',
    c: {
       x: '42',
    },
}

I tried:

let newState = Object.assign({}, state);
delete newState.c.y

but for some reasons, it deletes the property from both states.

Could help me to do that?

Javascript Solutions


Solution 1 - Javascript

How about using destructuring assignment syntax?

const original = {
  foo: 'bar',
  stack: 'overflow',
};

// If the name of the property to remove is constant
const { stack, ...withoutFirst } = original;
console.log(withoutFirst); // Will be { "foo": "bar" }

// If the name of the property to remove is from a variable
const key = 'stack'
const { [key]: value, ...withoutSecond } = original;
console.log(withoutSecond); // Will be { "foo": "bar" }

// To do a deep removal with property names from variables
const deep = {
  foo: 'bar',
  c: {
   x: 1,
   y: 2
  }
};

const parentKey = 'c';
const childKey = 'y';
// Remove the 'c' element from original
const { [parentKey]: parentValue, ...noChild } = deep;
// Remove the 'y' from the 'c' element
const { [childKey]: removedValue, ...childWithout } = parentValue;
// Merge back together
const withoutThird = { ...noChild, [parentKey]: childWithout };
console.log(withoutThird); // Will be { "foo": "bar", "c": { "x": 1 } }

Solution 2 - Javascript

I find ES5 array methods like filter, map and reduce useful because they always return new arrays or objects. In this case I'd use Object.keys to iterate over the object, and Array#reduce to turn it back into an object.

return Object.assign({}, state, {
    c: Object.keys(state.c).reduce((result, key) => {
        if (key !== 'y') {
            result[key] = state.c[key];
        }
        return result;
    }, {})
});

Solution 3 - Javascript

You can use _.omit(object, [paths]) from lodash library

path can be nested for example: _.omit(object, ['key1.key2.key3'])

Solution 4 - Javascript

Just use ES6 object destructuring feature

const state = {
    c: {
       x: '42',
       y: '43'
    },
}

const { c: { y, ...c } } = state // generates a new 'c' without 'y'

console.log({...state, c }) // put the new c on a new state

Solution 5 - Javascript

That's because you are copying the value of state.c to the other object. And that value is a pointer to another javascript object. So, both of those pointers are pointing to the same object.

Try this:

let newState = Object.assign({}, state);
console.log(newState == state); // false
console.log(newState.c == state.c); // true
newState.c = Object.assign({}, state.c);
console.log(newState.c == state.c); // now it is false
delete newState.c.y;

You can also do a deep-copy of the object. See this question and you'll find what's best for you.

Solution 6 - Javascript

How about this:

function removeByKey (myObj, deleteKey) {
  return Object.keys(myObj)
    .filter(key => key !== deleteKey)
    .reduce((result, current) => {
      result[current] = myObj[current];
      return result;
  }, {});
}

It filters the key that should be deleted then builds a new object from the remaining keys and the initial object. The idea is stolen from Tyler McGinnes awesome reactjs program.

JSBin

Solution 7 - Javascript

function dissoc(key, obj) {
  let copy = Object.assign({}, obj)
  delete copy[key]
  return copy
}

Also, if looking for a functional programming toolkit, look at Ramda.

Solution 8 - Javascript

You may use Immutability helper in order to unset an attribute, in your case:

import update from 'immutability-helper';

const updatedState = update(state, {
  c: {
    $unset: ['y']
  }
});    

Solution 9 - Javascript

As of 2019, another option is to use the Object.fromEntries method. It has reached stage 4.

const newC = Object.fromEntries(
    Object.entries(state.c).filter(([key]) => key != 'y')
)
const newState = {...state, c: newC}

The nice thing about it is that it handles integer keys nicely.

Solution 10 - Javascript

It's easy with Immutable.js:

const newState = state.deleteIn(['c', 'y']);

description of deleteIn()

Solution 11 - Javascript

Here's an easy 1-liner you can use that allows you to partially apply the prop you want to remove. This makes it easy to pass to Array.map.

const removeProp = prop => ({ [prop]: _, ...rest }) => ({ ...rest })

Now you can use it like this:

const newArr = oldArr.map(removeProp('deleteMe'))

Solution 12 - Javascript

The issue you are having is that you are not deep cloning your initial state. So you have a shallow copy.

You could use spread operator

  const newState = { ...state, c: { ...state.c } };
  delete newState.c.y

Or following your same code

let newState = Object.assign({}, state, { c: Object.assign({}, state.c) });
delete newState.c.y

Solution 13 - Javascript

I normally use

Object.assign({}, existingState, {propToRemove: undefined})

I realise this isn't actually removing the property but for almost all purposes 1 its functionally equivalent. The syntax for this is much simpler than the alternatives which I feel is a pretty good tradeoff.

1 If you are using hasOwnProperty(), you will need to use the more complicated solution.

Solution 14 - Javascript

I use this pattern

const newState = Object.assign({}, state);
      delete newState.show;
      return newState;

but in book i saw another pattern

return Object.assign({}, state, { name: undefined } )

Solution 15 - Javascript

utility ;))

const removeObjectField = (obj, field) => {

    // delete filter[selectName]; -> this mutates.
    const { [field]: remove, ...rest } = obj;

    return rest;
}

action type

const MY_Y_REMOVE = 'MY_Y_REMOVE';

action creator

const myYRemoveAction = (c, y) => {

    const result = removeObjectField(c, y);

        return dispatch =>
            dispatch({
                type: MY_Y_REMOVE,
                payload: result
            })
    }

reducer

export default (state ={}, action) => {
  switch (action.type) {
    case myActions.MY_Y_REMOVE || :
      return { ...state, c: action.payload };
    default:
      return state;
  }
};

Solution 16 - Javascript

As hinted in some of the answers already, it's because you are trying to modify a nested state ie. one level deeper. A canonical solution would be to add a reducer on the x state level:

const state = {
    a: '1',
    b: '2',
    c: {
       x: '42',
       y: '43'
    },
}

Deeper level reducer

let newDeepState = Object.assign({}, state.c);
delete newDeepState.y;

Original level reducer

let newState = Object.assign({}, state, {c: newDeepState});

Solution 17 - Javascript

Use a combination of Object.assign, JSON.parse and JSON.stringify

const obj1 = { a: "a", b: "b" };
const obj2 = { c: "c", a: undefined };

const merged = Object.assign({}, obj1, obj2);

const sanitized = JSON.parse(JSON.stringify(merged));

console.log(sanitized); // -> { b: "b", c: "c" }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionVincent TaingView Question on Stackoverflow
Solution 1 - JavascriptmadebydavidView Answer on Stackoverflow
Solution 2 - JavascriptDavid L. WalshView Answer on Stackoverflow
Solution 3 - JavascriptDmitriView Answer on Stackoverflow
Solution 4 - JavascriptRamon DiogoView Answer on Stackoverflow
Solution 5 - JavascriptAᴍɪʀView Answer on Stackoverflow
Solution 6 - JavascriptSebKView Answer on Stackoverflow
Solution 7 - JavascriptDominykas MostauskisView Answer on Stackoverflow
Solution 8 - JavascriptJavier PView Answer on Stackoverflow
Solution 9 - JavascriptjianView Answer on Stackoverflow
Solution 10 - JavascriptquotesBroView Answer on Stackoverflow
Solution 11 - JavascriptBrennan CheungView Answer on Stackoverflow
Solution 12 - JavascriptJuan CarreyView Answer on Stackoverflow
Solution 13 - JavascriptNot lovedView Answer on Stackoverflow
Solution 14 - JavascriptzloctbView Answer on Stackoverflow
Solution 15 - JavascriptMusaView Answer on Stackoverflow
Solution 16 - JavascriptMieszkoView Answer on Stackoverflow
Solution 17 - JavascriptAleem IsiakaView Answer on Stackoverflow