Hủy bỏ một tài sản trong một đối tượng bất biến


145

Tôi đang sử dụng Redux. Trong trình giảm tốc của tôi, tôi đang cố gắng xóa một thuộc tính khỏi một đối tượng như thế này:

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

Và tôi muốn có một cái gì đó như thế này mà không phải thay đổi trạng thái ban đầu:

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

Tôi đã thử:

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

nhưng vì một số lý do, nó xóa tài sản khỏi cả hai tiểu bang.

Có thể giúp tôi làm điều đó?


3
Lưu ý rằng Object.assignchỉ tạo một bản sao nông củastate và do đó state.cnewState.csẽ trỏ đến đối tượng được chia sẻ cùng. Bạn đã cố xóa thuộc tính ykhỏi đối tượng chia sẻ cchứ không phải từ đối tượng mới newState.
Akseli Palén

Câu trả lời:


224

Làm thế nào về việc sử dụng cú pháp chuyển nhượng phá hủ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 } }


3
tuyệt vời, không có thư viện thêm, sử dụng es6 và đủ đơn giản.
sbk201

Giải pháp thanh lịch nhất
long.luc

12
Đẹp! Đây là một chức năng trợ giúp es6 đi cùng với nó const deleteProperty = ({[key]: _, ...newObj}, key) => newObj;. Cách sử dụng: deleteProperty({a:1, b:2}, "a");cho{b:2}
mikebridge

siêu thông minh, tôi đến muộn cái này, nhưng một trong những mục yêu thích mới của tôi
Gavin

1
Lưu ý rằng ví dụ loại bỏ sâu sẽ sụp đổ nếu deep['c']trống, vì vậy trong trường hợp chung, bạn có thể muốn thêm một kiểm tra cho sự hiện diện của khóa.
Laurent S

46

Tôi thấy phương pháp mảng ES5 như filter, mapreducehữu ích bởi vì họ luôn luôn trả mảng mới hoặc các đối tượng. Trong trường hợp này, tôi sẽ sử dụng Object.keysđể lặp lại đối tượng và Array#reducebiến nó trở lại thành một đối tượng.

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

thay đổi với sự thay đổi nhỏ của tôi để làm cho IMO rõ ràng hơn ... cũng cho phép bạn bỏ qua nhiều thuộc tính. const omit = ['prop1', 'prop2'] ... if (omit.indexOf (key) === -1) result [key] = state.c [key] return result; ...
josh-sachs

3
ES6 tương đương để lấy một bản sao myObjectvới khóa myKeyđã bị xóa:Object.keys(myObject).reduce((acc, cur) => cur === myKey ? acc : {...acc, [cur]: myObject[cur]}, {})
transang

38

Bạn có thể sử dụng _.omit(object, [paths])từ thư viện lodash

đường dẫn có thể được lồng nhau, ví dụ: _.omit(object, ['key1.key2.key3'])


3
Thật không may, _.omitkhông thể xóa các thuộc tính sâu (những gì OP đã yêu cầu). Có omit-deep-lodashmô-đun cho mục đích này.
AlexM

2
Đi tắt những gì @AlexM đã nói. Tôi thấy nó hữu ích, và nó có thể phù hợp hơn với chúng tôi _.cloneDeep(obj)từ lodash. Điều đó sao chép đối tượng dễ dàng và sau đó bạn có thể chỉ cần sử dụng js delete obj.[key]để loại bỏ khóa.
Alex J

Đồng ý với w / @AlexJ, cloneDeep hoạt động hoàn hảo và bạn cũng có thể sử dụng cú pháp lây lan với nó: ..._. CloneDeep (bang) cho Redux
Sean Chase

32

Chỉ cần sử dụng tính năng hủy đối tượng 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ó thể rõ ràng hơn một chút so với việc có hai cchữ cái ở phía bên tay trái.
dosentmatter

10
hãy cẩn thận: điều này không hoạt động đối với một đối tượng được khóa bởi các số nguyên
daviestar

3
Từ câu trả lời dưới đây, nếu bạn cần tham chiếu tên biến sẽ bị xóa: const name = 'c'thì bạn có thể làm const {[name]:deletedValue, ...newState} = statesau đó quay lại newStatetrong bộ giảm tốc của mình. Đây là để xóa khóa cấp cao nhất
FFF

23

Đó là bởi vì bạn đang sao chép giá trị của state.cđối tượng khác. Và giá trị đó là một con trỏ đến một đối tượng javascript khác. Vì vậy, cả hai con trỏ đó đều trỏ đến cùng một đối tượng.

Thử cái này:

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;

Bạn cũng có thể làm một bản sao sâu của đối tượng. Xem câu hỏi này và bạn sẽ tìm thấy những gì tốt nhất cho bạn.


2
Đây là một câu trả lời tuyệt vời! state.clà một tài liệu tham khảo và tài liệu tham khảo đang được sao chép tốt. Redux muốn một hình dạng trạng thái chuẩn hóa, có nghĩa là sử dụng id thay vì tham chiếu khi trạng thái lồng nhau. Kiểm tra các tài liệu redux: redux.js.org/docs/recipes/reducers/N normalizingStateShape.html
Ziggy

17

Còn cái này thì sao:

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

Nó lọc khóa cần xóa sau đó xây dựng một đối tượng mới từ các khóa còn lại và đối tượng ban đầu. Ý tưởng bị đánh cắp từ chương trình Reacjs tuyệt vời của Tyler McGinnes.

Mã não


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

Ngoài ra, nếu tìm kiếm một bộ công cụ lập trình chức năng, hãy xem Ramda .


9

Bạn có thể sử dụng trình trợ giúp Bất biến để hủy đặt thuộc tính, trong trường hợp của bạn:

import update from 'immutability-helper';

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

Vậy thì làm thế nào để xóa tất cả thuộc tính "y" của mọi mục đối tượng mảng?
5ervant

@ 5ervant Tôi nghĩ đó là một câu hỏi khác, nhưng tôi khuyên bạn nên lập bản đồ mảng và áp dụng bất kỳ giải pháp nào được đưa ra ở đây
Javier P

8

Kể từ năm 2019, một lựa chọn khác là sử dụng Object.fromEntriesphương pháp này. Nó đã đạt đến giai đoạn 4.

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

Điều tuyệt vời ở đây là nó xử lý các khóa số nguyên một cách độc đáo.



3

Vấn đề bạn đang gặp phải là bạn không nhân bản sâu trạng thái ban đầu của mình. Vì vậy, bạn có một bản sao nông.

Bạn có thể sử dụng toán tử trải

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

Hoặc theo cùng mã của bạn

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

1

Tôi thường sử dụng

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

Tôi nhận ra rằng điều này không thực sự loại bỏ tài sản nhưng cho hầu hết các mục đích 1 chức năng của nó tương đương. Cú pháp cho việc này đơn giản hơn nhiều so với các lựa chọn thay thế mà tôi cảm thấy là một sự đánh đổi khá tốt.

1 Nếu bạn đang sử dụng hasOwnProperty(), bạn sẽ cần sử dụng giải pháp phức tạp hơn.


1

Tôi sử dụng mẫu này

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

nhưng trong cuốn sách tôi đã thấy một mô hình khác

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

Cái thứ hai, không loại bỏ chìa khóa. Nó chỉ đặt nó thành không xác định.
bman

1

tiện ích;))

const removeObjectField = (obj, field) => {

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

    return rest;
}

Loại hành động

const MY_Y_REMOVE = 'MY_Y_REMOVE';

người tạo hành động

const myYRemoveAction = (c, y) => {

    const result = removeObjectField(c, y);

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

hộp giảm tốc

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

0

Như đã gợi ý trong một số câu trả lời, đó là vì bạn đang cố gắng sửa đổi trạng thái lồng nhau tức là. sâu hơn một cấp. Một giải pháp kinh điển sẽ là thêm một bộ giảm tốc ở xcấp tiểu bang:

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

Giảm cấp độ sâu hơn

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

Giảm cấp gốc

let newState = Object.assign({}, state, {c: newDeepState});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.