不变地删除对象中的属性


145

我正在使用Redux。在我的reducer中,我试图从这样的对象中删除一个属性:

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.c并且newState.c将指向同一共享库。您试图y从共享对象c而不是新对象中删除属性newState
AkseliPalén18年

Answers:


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

最优雅的解决方案
long.luc

12
真好!这是一个es6辅助函数const deleteProperty = ({[key]: _, ...newObj}, key) => newObj;。用法:deleteProperty({a:1, b:2}, "a");{b:2}
mikebridge '18年

超级聪明,我来不及了,但是我最喜欢的之一
加文

1
请注意,如果deep['c']为空,则深度删除示例将崩溃,因此在一般情况下,您可能需要添加对密钥是否存在的检查。
Laurent S

46

我觉得ES5阵列的方法,如filtermapreduce有用的,因为他们总是返回新的数组或对象。在这种情况下,我将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'] ...如果(omit.indexOf(key)=== -1)result [key] = state.c [key]返回结果;...
josh-sachs

2
ES6等同于删除myObject密钥myKey后获得的副本:Object.keys(myObject).reduce((acc, cur) => cur === myKey ? acc : {...acc, [cur]: myObject[cur]}, {})
transang

38

您可以_.omit(object, [paths])lodash库中使用

路径可以嵌套,例如: _.omit(object, ['key1.key2.key3'])


3
不幸的是,_.omit无法删除深层属性(OP询问的内容)。有用omit-deep-lodash于此目的的模块。
AlexM

2
@AlexM在说什么。我发现_.cloneDeep(obj)lodash 对我们有用,也许更合适。这样可以轻松地复制对象,然后您可以简单地使用js delete obj.[key]删除密钥。
Alex J

同意w / @AlexJ,cloneDeep可以完美地工作,您也可以使用它的传播语法:..._。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.c可能比c左手有两个更清晰。
剂量ntmatter

10
当心:这不适用于以整数为键的对象
daviestar

3
从下面的答案中,如果您需要引用要删除的变量名:const name = 'c'那么您可以这样做,const {[name]:deletedValue, ...newState} = state然后返回newState您的reducer。这是用于顶级密钥删除
FFF

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;
  }, {});
}

它过滤应删除的密钥,然后从其余密钥和初始对象中构建一个新对象。泰勒·麦金尼斯(Tyler McGinnes)出色的reactjs程序盗用了这个想法。

联合会



9

在您的情况下,可以使用不可变性帮助程序来取消设置属性:

import update from 'immutability-helper';

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

那么如何删除每个数组对象项的所有属性“ y”?
5ervant

@ 5ervant我认为这是另一个问题,但是我建议您映射数组并在此处应用任何给定的解决方案
Javier P

8

从2019年开始,另一种选择是使用该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 } )

第二个,不删除密钥。只是将其设置为undefined。
bman19年

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

正如已经在某些答案中暗示的那样,这是因为您正在尝试修改嵌套状态,即。更深一层。一个规范的解决方案是在x状态级别添加一个reducer :

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});
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.