オブジェクトのプロパティを不変に削除する


145

Reduxを使用しています。私のレデューサーでは、次のようにオブジェクトからプロパティを削除しようとしています:

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

そして、私は元の状態を変更することなく、このようなものを持ちたいです:

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

私は試した:

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

しかし、いくつかの理由により、両方の州からプロパティが削除されます。

それを行うのに役立ちますか?


3
Object.assignのみ作成浅いコピーのをstate、したがってstate.cnewState.c同じ共有オブジェクトを指します。新しいオブジェクトからではなくy、共有オブジェクトからプロパティを削除しようとしました。cnewState
AkseliPalén18年

回答:


224

分解代入構文を使用するのはどうですか?

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
素晴らしい、追加のライブラリなしで、es6を十分にシンプルに使用してください。
sbk201 2017

最もエレガントなソリューション
long.luc 2018年

12
いいね!以下は、それに対応するes6ヘルパー関数const deleteProperty = ({[key]: _, ...newObj}, key) => newObj;です。使用法:deleteProperty({a:1, b:2}, "a");与え{b:2}
mikebridge

超賢い、私はこれに遅れるが、私のお気に入りの1つ
Gavin

1
ディープリムーバルの例は、deep['c']が空の場合にクラッシュするため、一般的なケースでは、キーの存在のチェックを追加する必要がある場合があります。
Laurent S

46

ES5の配列メソッドはのようfiltermapありreduce、常に新しい配列またはオブジェクトを返すため便利です。この場合Object.keys、オブジェクトを反復処理するために使用します。Array#reduceに戻すために使用します。

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

IMOをより明確にするために少し変更を加えて、複数のプロパティを省略できるようにします。const omit = ['prop1'、 'prop2'] ... if(omit.indexOf(key)=== -1)result [key] = state.c [key] return result; ...
josh-sachs

3
myObjectキーをmyKey削除してのコピーを取得するのと同等のES6 :Object.keys(myObject).reduce((acc, cur) => cur === myKey ? acc : {...acc, [cur]: myObject[cur]}, {})
transang '27 /

38

lodashライブラリ_.omit(object, [paths])から使用できます

たとえば、パスはネストできます。 _.omit(object, ['key1.key2.key3'])


3
残念ながら、_.omit深いプロパティ(OPが求めていたもの)は削除できません。omit-deep-lodashこの目的のためのモジュールがあります。
AlexM 2017

2
@AlexMが言っていたことをやめます。私はそれが有用であるとわかりました、そしてそれは_.cloneDeep(obj)ロダッシュから私たちにとってより適切かもしれません。これによりオブジェクトが簡単にコピーされ、js delete obj.[key]を使用してキーを削除できます。
Alex J

@AlexJで同意し、cloneDeepは完全に機能し、spread構文も使用できます。..._。cloneDeep(state)for Redux
Sean Chase

32

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.cc左側に2つのがあるよりも少しわかりやすいかもしれません。
dosentmatter

10
注意してください:これは整数でキー設定されたオブジェクトに対しては機能しません
daviestar

3
以下の回答から、削除する変数名を参照する必要がある場合は、レデューサーに戻るconst name = 'c'ことができます。これはトップレベルのキー削除用ですconst {[name]:deletedValue, ...newState} = statenewState
FFF 2018

23

これはstate.c、他のオブジェクトにの値をコピーしているためです。そして、その値は別のJavaScriptオブジェクトへのポインタです。したがって、これらのポインタは両方とも同じオブジェクトを指しています。

これを試して:

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;

オブジェクトのディープコピーを行うこともできます。この質問を参照すると、あなたにとって最適なものが見つかります。


2
これは素晴らしい答えです!state.cは参照であり、参照は問題なくコピーされています。Reduxは正規化された状態の形状を必要としています。つまり、状態をネストするときに参照ではなくIDを使用します。reduxのドキュメントをチェックしてください:redux.js.org/docs/recipes/reducers/NormalizingStateShape.html
Ziggy

17

これはどう:

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

削除するキーをフィルタリングし、残りのキーと初期オブジェクトから新しいオブジェクトを作成します。このアイデアは、タイラー・マクギネスの素晴らしいreactjsプログラムから盗み出されたものです。

JSBin



9

あなたの場合、属性を設定解除するために不変性ヘルパーを使用できます:

import update from 'immutability-helper';

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

次に、すべての配列オブジェクト項目のすべてのプロパティ「y」を削除する方法は?
5ervant

@ 5ervantそれは別の質問だと思いますが、配列をマップして、ここに与えられた解決策のいずれかを適用することをお勧めします
Javier P

8

2019年の時点で、もう1つのオプションはこのObject.fromEntriesメソッドを使用することです。ステージ4に達しました。

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

それの良いところは、整数キーをうまく処理することです。



3

あなたが抱えている問題は、初期状態を詳細に複製していないことです。だからあなたは浅いコピーを持っています。

スプレッド演算子を使用できます

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

または同じコードに従ってください

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

1

私は通常使用します

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

これは実際にはプロパティを削除するのではなく、ほぼすべての目的で1その機能的に同等。これの構文は、かなり良いトレードオフだと私が思う代替案よりもはるかに単純です。

1を使用している場合hasOwnProperty()、あなたはより多くの複雑なソリューションを使用する必要があります。


1

私はこのパターンを使います

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

本の中で別のパターンを見ました

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

2つ目は、キーを削除しません。それをundefinedに設定するだけです。
bman

1

ユーティリティ;))

const removeObjectField = (obj, field) => {

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

    return rest;
}

アクションタイプ

const MY_Y_REMOVE = 'MY_Y_REMOVE';

アクションクリエーター

const myYRemoveAction = (c, y) => {

    const result = removeObjectField(c, y);

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

減速機

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

0

すでにいくつかの回答で示唆されているように、ネストされた状態を変更しようとしているためです。1レベル深くなります。標準的な解決策は、x州レベルでレデューサーを追加することです。

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

より深いレベルの減力剤

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

オリジナルレベルリデューサー

let newState = Object.assign({}, state, {c: newDeepState});
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.