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.