Niezmiennie usuwa właściwość z obiektu


145

Używam Redux. W moim reduktorze próbuję usunąć właściwość z obiektu takiego:

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

I chcę mieć coś takiego bez konieczności modyfikowania pierwotnego stanu:

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

Próbowałem:

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

ale z pewnych powodów usuwa właściwość z obu stanów.

Czy może mi w tym pomóc?


3
Zauważ, że Object.assigntworzy tylko płytkie kopię z statea zatem state.ci newState.cbędzie wskazywać na ten sam obiekt udostępniony. Próbowałeś usunąć właściwość yz udostępnionego obiektu, ca nie z nowego obiektu newState.
Akseli Palén

Odpowiedzi:


224

A co powiesz na użycie składni przydziału destrukturyzacji ?

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


3
wspaniałe, bez dodatkowej biblioteki, korzystaj z es6 i dość proste.
sbk201

Najbardziej eleganckie rozwiązanie
long.luc

12
Miły! Oto funkcja pomocnicza es6, która może się z tym pogodzić const deleteProperty = ({[key]: _, ...newObj}, key) => newObj;. Sposób użycia: deleteProperty({a:1, b:2}, "a");daje{b:2}
mikebridge

super sprytny, spóźniłem się na ten, ale jeden z moich nowych ulubionych
Gavin

1
Zwróć uwagę, że przykład głębokiego usuwania ulegnie awarii, jeśli deep['c']jest pusty, więc w ogólnym przypadku możesz chcieć dodać sprawdzenie obecności klucza.
Laurent S,

46

Uważam ES5 metod tablicowych jak filter, mapi reduceużyteczny, ponieważ zawsze powrócić nowych tablic lub obiektów. W tym przypadku Object.keysużyłbym do iteracji po obiekcie i Array#reduceprzekształcenia go z powrotem w obiekt.

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

wbijanie się w moją niewielką zmianę, aby uczynić ją bardziej przejrzystą IMO ... pozwala również pominąć wiele właściwości. const omit = ['prop1', 'prop2'] ... if (omit.indexOf (key) === -1) result [key] = state.c [key] return result; ...
josh-sachs

3
Odpowiednik ES6, aby otrzymać kopię myObjectbez klucza myKey:Object.keys(myObject).reduce((acc, cur) => cur === myKey ? acc : {...acc, [cur]: myObject[cur]}, {})
transang

38

Możesz użyć _.omit(object, [paths])z biblioteki lodash

ścieżka może być zagnieżdżona na przykład: _.omit(object, ['key1.key2.key3'])


3
Niestety _.omitnie można usunąć głębokich właściwości (o co prosił OP). Do omit-deep-lodashtego celu służy moduł.
AlexM,

2
Wychodzę z tego, co mówił @AlexM. Uznałem, że jest to przydatne i może być bardziej odpowiednie dla nas _.cloneDeep(obj)z lodash. To łatwo kopiuje obiekt, a następnie możesz po prostu użyć js, delete obj.[key]aby usunąć klucz.
Alex J

Zgadzam się w / @AlexJ, cloneDeep działa doskonale i możesz z nim również korzystać ze składni spreadu: ..._. CloneDeep (stan) dla Redux
Sean Chase

32

Po prostu użyj funkcji destrukturyzacji obiektów 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


8
const {y, ...c} = state.cmoże być trochę jaśniejsze niż posiadanie dwóch cpo lewej stronie.
dosentmatter

10
uwaga: to nie działa dla obiektu z kluczem liczb całkowitych
daviestar,

3
Z poniższej odpowiedzi, jeśli chcesz odwołać się do nazwy zmiennej do usunięcia: const name = 'c'możesz to zrobić, const {[name]:deletedValue, ...newState} = statea następnie powrócić newStatedo swojego reduktora. To jest dla usunięcia klucza najwyższego poziomu
FFF

23

Dzieje się tak, ponieważ kopiujesz wartość state.cdo innego obiektu. Ta wartość jest wskaźnikiem do innego obiektu javascript. Zatem oba te wskaźniki wskazują ten sam obiekt.

Spróbuj tego:

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;

Możesz także wykonać głęboką kopię obiektu. Zobacz to pytanie, a znajdziesz to, co jest dla Ciebie najlepsze.


2
To doskonała odpowiedź! state.cjest odniesieniem i jest kopiowany poprawnie. Redux chce znormalizowanego kształtu stanu, co oznacza używanie identyfikatorów zamiast odniesień podczas zagnieżdżania stanu. Sprawdź dokumenty redux: redux.js.org/docs/recipes/reducers/NormalizingStateShape.html
Ziggy

17

Co powiesz na to:

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

Filtruje klucz, który powinien zostać usunięty, a następnie tworzy nowy obiekt z pozostałych kluczy i obiektu początkowego. Pomysł został skradziony z programu niesamowitych reakcji Tylera McGinnesa.

JSBin


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

Ponadto, jeśli szukasz funkcjonalnego zestawu narzędzi do programowania, spójrz na Ramdę .


9

Możesz użyć pomocnika niezmienności , aby usunąć atrybut, w twoim przypadku:

import update from 'immutability-helper';

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

Jak więc usunąć całą właściwość „y” każdego elementu obiektu tablicy?
5ervant

@ 5ervant Myślę, że to kolejne pytanie, ale proponuję zmapować tablicę i zastosować tutaj dowolne z podanych rozwiązań
Javier P

8

Od 2019 roku inną opcją jest użycie Object.fromEntriesmetody. Osiągnął etap 4.

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

Zaletą jest to, że ładnie obsługuje klucze całkowite.



3

Problem, który masz, polega na tym, że nie klonujesz głęboko swojego stanu początkowego. Więc masz płytką kopię.

Możesz użyć operatora spreadu

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

Lub postępując zgodnie z tym samym kodem

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

1

Zwykle używam

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

Zdaję sobie sprawę, że w rzeczywistości nie jest to usuwanie własności, ale prawie do wszystkich celów 1 jest to równoważne funkcjonalnie. Składnia tego jest znacznie prostsza niż alternatywy, które uważam za całkiem niezły kompromis.

1 Jeśli używasz hasOwnProperty(), będziesz musiał użyć bardziej skomplikowanego rozwiązania.


1

Używam tego wzoru

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

ale w książce widziałem inny wzór

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

Drugi nie wyjmuje klucza. Po prostu ustawia go na niezdefiniowany.
bman

1

użyteczność;))

const removeObjectField = (obj, field) => {

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

    return rest;
}

Rodzaj działania

const MY_Y_REMOVE = 'MY_Y_REMOVE';

twórca akcji

const myYRemoveAction = (c, y) => {

    const result = removeObjectField(c, y);

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

reduktor

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

0

Jak już wskazano w niektórych odpowiedziach, dzieje się tak, ponieważ próbujesz zmodyfikować stan zagnieżdżony, tj. jeden poziom głębiej. Rozwiązaniem kanonicznym byłoby dodanie reduktora na xpoziomie stanu:

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

Głębszy reduktor poziomu

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

Oryginalny reduktor poziomu

let newState = Object.assign({}, state, {c: newDeepState});
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.