web-dev-qa-db-fra.com

Supprimer une propriété dans un objet immuablement

J'utilise Redux. Dans mon réducteur, j'essaie de supprimer une propriété d'un objet comme celui-ci:

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

Et je veux avoir quelque chose comme ça sans avoir à muter l’état original:

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

J'ai essayé:

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

mais pour certaines raisons, il supprime la propriété des deux états.

Pourrait m'aider à faire ça?

114
Vincent Taing

Que diriez-vous d'utiliser la syntaxe attribution de déstructuration ?

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 } }
159
madebydavid

Les méthodes de tableaux ES5 telles que filter, map et reduce sont utiles car elles renvoient toujours de nouveaux tableaux ou objets. Dans ce cas, j'utiliserais Object.keys pour parcourir l'objet, et Array#reduce pour le reconvertir en objet.

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

Vous pouvez utiliser _.omit(object, [paths]) de lodash library

chemin peut être imbriqué par exemple: _.omit(object, ['key1.key2.key3'])

34
Dmitri

Il suffit d'utiliser la fonctionnalité de déstructuration d'objet ES6

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
23
Ramon Diogo

C'est parce que vous copiez la valeur de state.c vers l'autre objet. Et cette valeur est un pointeur sur un autre objet javascript. Ainsi, ces deux pointeurs pointent sur le même objet.

Essaye ça:

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;

Vous pouvez également faire une copie en profondeur de l'objet. Voir cette question et vous trouverez ce qui vous convient le mieux.

22
Aᴍɪʀ

Que dis-tu de ça:

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

Il filtre la clé à supprimer, puis crée un nouvel objet à partir des clés restantes et de l’objet initial. L'idée est volée au programme impressionnant de Tyler McGinnes.

JSBin

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

En outre, si vous recherchez une boîte à outils de programmation fonctionnelle, regardez Ramda .

10

Vous pouvez utiliser assistant d'immutabilité pour supprimer un attribut, dans votre cas:

import update from 'immutability-helper';

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

C'est facile avec Immutable.js :

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

description de deleteIn ()

5
quotesBro

Le problème que vous rencontrez est que vous n'êtes pas en train de cloner en profondeur votre état initial. Donc, vous avez une copie superficielle.

Vous pouvez utiliser l'opérateur spread

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

Ou en suivant votre même code

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

J'utilise normalement

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

Je me rends compte que ce n'est pas réellement enlever la propriété, mais pour presque toutes les fins 1 son équivalent fonctionnel. La syntaxe utilisée est bien plus simple que les alternatives, ce qui, à mon avis, est un très bon compromis.

1 Si vous utilisez hasOwnProperty(), vous devrez utiliser la solution plus complexe.

1
Luke McGregor

À partir de 2019, une autre option consiste à utiliser la méthode Object.fromEntries. Il a atteint l'étape 4.

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

La bonne chose à ce sujet est qu’elle gère bien les clés entières.

1
yidaohuuu

J'utilise ce motif

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

mais dans le livre j'ai vu un autre motif

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

tilité;))

const removeObjectField = (obj, field) => {

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

    return rest;
}

type d'action

const MY_Y_REMOVE = 'MY_Y_REMOVE';

créateur d'action

const myYRemoveAction = (c, y) => {

    const result = removeObjectField(c, y);

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

réducteur

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

Comme cela a déjà été suggéré dans certaines des réponses, c'est parce que vous essayez de modifier un état imbriqué, par exemple. un niveau plus profond. Une solution canonique serait d’ajouter un réducteur au niveau de l’état x:

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

Réducteur de niveau plus profond

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

Réducteur de niveau d'origine

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