Nesnedeki bir özelliği değişmez bir şekilde kaldırma


145

Redux kullanıyorum. Benim redüktörde böyle bir nesneden bir özelliği kaldırmaya çalışıyorum:

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

Ve orijinal durumu değiştirmeden böyle bir şeye sahip olmak istiyorum:

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

Denedim:

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

ancak bazı nedenlerden dolayı mülkü her iki eyaletten de siler.

Bunu yapmama yardım edebilir misin?


3
Not Object.assignsadece yaratır sığ kopya ait stateve bu nedenle state.cve newState.caynı paylaşılan nesneye işaret edecektir. Özelliği yeni nesneden değil y, paylaşılan nesneden silmeye çalıştınız . cnewState
Akseli Palén

Yanıtlar:


224

Yıkıcı atama sözdizimini kullanmaya ne dersiniz ?

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
harika, ekstra kütüphane olmadan, es6 ve yeterince basit kullanın.
sbk201

En şık çözüm
long.luc

12
Güzel! İşte onunla birlikte gitmek için bir es6 yardımcı işlevi const deleteProperty = ({[key]: _, ...newObj}, key) => newObj;. Kullanım: deleteProperty({a:1, b:2}, "a");verir{b:2}
mikebridge

süper zeki, buna geç kaldım, ama yeni favorilerimden biri
Gavin

1
Derin kaldırma örneğinin deep['c']boş olması durumunda kilitleneceğini unutmayın , bu nedenle genel bir durumda, anahtarın varlığı için bir kontrol eklemek isteyebilirsiniz.
Laurent S

46

Ben gibi ES5 dizi yöntemlerini bulmak filter, mapve reducekullanışlı onlar her zaman yeni diziler veya nesneleri döndürmek için. Bu durumda Object.keys, nesne üzerinde yineleme yapmak ve Array#reduceonu bir nesneye dönüştürmek için kullanırdım.

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

daha net hale getirmek için hafif değişiklikimle uğraşmak da birden fazla özelliği atlamanızı sağlar. const omit = ['prop1', 'prop2'] ... if (omit.indexOf (anahtar) === -1) sonuç [anahtar] = durum.c [anahtar] sonuç döndürür; ...
josh-sachs

3
ES6 eşdeğeri myObjectanahtar myKeykaldırılmış olarak bir kopyasını almak için :Object.keys(myObject).reduce((acc, cur) => cur === myKey ? acc : {...acc, [cur]: myObject[cur]}, {})
transang

38

Sen kullanabilirsiniz _.omit(object, [paths])gelen lodash kütüphanesinden

yol yuvalanabilir örneğin: _.omit(object, ['key1.key2.key3'])


3
Ne yazık ki, _.omitderin özellikleri silemezsiniz (OP'nin istediği). omit-deep-lodashBu amaçla bir modül var .
AlexM

2
@AlexM'in söylediklerini gidiyor. Yararlı buldum ve bizim _.cloneDeep(obj)için lodash'tan daha uygun olabilir . Bu, nesneyi kolayca kopyalar ve ardından delete obj.[key]anahtarı kaldırmak için js tuşunu kullanabilirsiniz.
Alex J

@AlexJ, w / cloneDeep mükemmel çalışır ve onunla forma sözdizimini de kullanabilirsiniz: ..._. Redux için cloneDeep (eyalet)
Sean Chase

32

Sadece ES6 nesne imha özelliğini kullanın

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.ccsol tarafta iki tane olmaktan biraz daha açık olabilir .
dosentmatter

10
dikkat: bu tamsayılar tarafından anahtarlanmış bir nesne için çalışmıyor
daviestar

3
Aşağıdaki yanıttan, silinecek değişken adına başvurmanız const name = 'c'gerekirse : o zaman redüktörünüze const {[name]:deletedValue, ...newState} = stategeri dönebilirsiniz newState. Bu üst düzey bir anahtar silme işlemi içindir
FFF

23

Bunun nedeni, state.c diğer nesneye . Ve bu değer başka bir javascript nesnesinin göstergesidir. Yani, her iki işaretçi de aynı nesneyi işaret ediyor.

Bunu dene:

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;

Nesnenin derin kopyasını da yapabilirsiniz. Bu soruya bakın ve sizin için en iyi olanı bulacaksınız.


2
Bu mükemmel bir cevap! state.cbir referans ve referans gayet iyi kopyalanıyor. Redux normalleştirilmiş bir durum şekli ister; bu, durumu iç içe yerleştirirken referanslar yerine kimlikler kullanmak anlamına gelir. Redux belgelerine göz atın: redux.js.org/docs/recipes/reducers/NormalizingStateShape.html
Ziggy

17

Buna ne dersin:

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

Silinecek anahtarı filtreler, ardından kalan anahtarlardan ve ilk nesneden yeni bir nesne oluşturur. Fikir Tyler McGinnes müthiş tepki programından çalındı.

JSBin



9

Sizin durumunuzda bir özelliği ayarlamak için Değiştirilebilirlik yardımcısını kullanabilirsiniz :

import update from 'immutability-helper';

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

Sonra her dizi nesne öğesinin tüm "y" özelliği nasıl silinir?
5ervant

@ 5ervant Bunun başka bir soru olduğunu düşünüyorum, ancak diziyi eşlemenizi ve burada verilen çözümlerden herhangi birini uygulamanızı öneririm
Javier P

8

2019 itibariyle, başka bir seçenek de Object.fromEntriesyöntemi kullanmaktır . Aşama 4'e ulaştı.

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

Bu konuda güzel bir şey, tamsayı tuşlarını güzel işlemesi.



3

Yaşadığınız sorun, başlangıç ​​durumunuzu derinlemesine klonlamamanızdır. Yani sığ bir kopyanız var.

Forma operatörünü kullanabilirsiniz

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

Veya aynı kodu takip ederek

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

1

Normalde kullanırım

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

Bu aslında özelliği kaldırma değil ama hemen hemen tüm amaçlar için 1 işlevsel olarak eşdeğer farkında . Bunun sözdizimi, oldukça iyi bir takas olduğunu düşündüğüm alternatiflerden çok daha basit.

1 Kullanıyorsanız hasOwnProperty(), daha karmaşık çözümü kullanmanız gerekecektir.


1

Bu kalıbı kullanıyorum

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

ama kitapta başka bir desen gördüm

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

İkincisi, anahtarı çıkarmaz. Sadece tanımsız olarak ayarlar.
bman

1

Yarar ;))

const removeObjectField = (obj, field) => {

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

    return rest;
}

Eylem Türü

const MY_Y_REMOVE = 'MY_Y_REMOVE';

aksiyon yaratıcısı

const myYRemoveAction = (c, y) => {

    const result = removeObjectField(c, y);

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

redüktör

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

0

Zaten bazı cevaplarda ima edildiği gibi, bunun nedeni iç içe bir durumu değiştirmeye çalışmanızdır. bir seviye daha derine. Kurallı bir çözüm, xdevlet düzeyinde bir redüktör eklemek olacaktır :

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

Daha derin seviye düşürücü

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

Orijinal seviye düşürücü

let newState = Object.assign({}, state, {c: newDeepState});
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.