不变地删除对象中的属性

我正在使用 Redux,在我的 reduce 中,我试图从一个对象中移除一个属性,如下所示:

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

但由于某些原因,它从两个州都删除了该属性。

能帮我吗?

110876 次浏览

这是因为您正在将 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;

你也可以做一个对象的深度拷贝。参见 这个问题,你会发现什么是最适合你的。

我发现像 filtermapreduce这样的 ES5数组方法很有用,因为它们总是返回新的数组或对象。在这种情况下,我将使用 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;
}, {})
});

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

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

这样吧:

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

它过滤应该删除的键,然后从剩余的键和初始对象构建一个新对象。这个想法是从 Tyler McGinnes 的精彩反应项目中偷来的。

JSBin

使用 一成不变的很简单:

const newState = state.deleteIn(['c', 'y']);

DeleteIn ()的说明

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

另外,如果寻找函数式编程工具包,请参考 拉姆达

您可以使用 不变性助手来取消属性设置,在您的例子中:

import update from 'immutability-helper';


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

使用 破坏性转让语法如何?

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

我通常会用

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

我意识到这实际上并不是移除这个属性,而是为了几乎所有的目的 1的功能等同。这种方法的语法要比其他方法简单得多,我认为这是一种很好的折衷方法。

1 如果您使用的是 hasOwnProperty(),则需要使用更复杂的解决方案。

正如已经在一些答案中暗示的那样,这是因为您正试图修改嵌套状态。再深一层。一个规范的解决方案是在 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});

我用这个模式

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

但在书中我看到了另一种模式

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

只需使用 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

公用事业;)

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

您遇到的问题是没有深度克隆初始状态。所以你只有一个浅拷贝。

你可以用扩展运算符

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

或者遵循同样的原则

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

截至2019年,另一个选择是使用 Object.fromEntries方法。它已经达到了第4阶段。

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

它的优点是可以很好地处理整型键。

这里有一个简单的1线程,你可以使用,让你部分应用的道具,你想删除。这使得它很容易传递到 Array.map

const removeProp = prop => ({ [prop]: _, ...rest }) => ({ ...rest })

现在你可以这样使用它:

const newArr = oldArr.map(removeProp('deleteMe'))

使用 Object.sign、 JSON.parse 和 JSON.stringify 的组合

const obj1 = { a: "a", b: "b" };
const obj2 = { c: "c", a: undefined };


const merged = Object.assign({}, obj1, obj2);


const sanitized = JSON.parse(JSON.stringify(merged));


console.log(sanitized); // -> { b: "b", c: "c" }