javascript - Fjern en egenskap i et objekt immutabelt


Jeg bruker Redux. I min reducer prøver jeg å fjerne en egenskap fra et objekt på denne måten:

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

Og jeg vil ha noe sånt uten å måtte endre på den opprinnelige tilstanden:

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

Jeg prøvde:

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

men av en eller annen grunn sletter det egenskapen fra begge tilstander.

Kan du hjelpe meg med å gjøre det?

Svar #1

Hva med å bruke destruktureringstildelingssyntaks?

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

Svar #2

Jeg synes ES5-arraymetoder som filter, map og reduce er nyttige fordi de alltid returnerer nye arrays eller objekter. I dette tilfellet ville jeg bruke Object.keys for å iterere over objektet, og Array#reduce for å gjøre det tilbake til et objekt.

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

Svar #3

Du kan bruke _.omit(object, [paths]) fra lodash-biblioteket

stien kan være nestet for eksempel: _.omit(object, ['key1.key2.key3'])

Svar #4

Bare bruk ES6-objektdestruktureringsfunksjonen

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

Svar #5

Det er fordi du kopierer verdien av state.c til det andre objektet. Og den verdien er en peker til et annet JavaScript-objekt. Så begge disse pekerne peker på det samme objektet.

Prøv dette:

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;

Du kan også lage en dypkopi av objektet. Se på dette spørsmålet, så finner du hva som er best for deg.

Svar #6

Hva med dette:

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

Den filtrerer nøkkelen som skal slettes, og bygger deretter et nytt objekt fra de gjenværende nøklene og det opprinnelige objektet. Ideen er stjålet fra Tyler McGinnes fantastiske ReactJS-program.

JSBin

Svar #7

Fra 2019 er et annet alternativ å bruke Object.fromEntries-metoden. Den har nådd fase 4.

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

Det fine med det er at det håndterer heltallnøkler på en fin måte.

Svar #8

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

Også, hvis du ser etter et verktøysett for funksjonell programmering, se på Ramda.

Svar #9

Du kan bruke Immutability-hjelperen for å fjerne et attributt, i ditt tilfelle:

import update from 'immutability-helper';

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

Relaterte artikler