Hapus properti dalam suatu objek secara kekal

145

Saya menggunakan Redux. Di peredam saya, saya mencoba menghapus properti dari objek seperti ini:

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

Dan saya ingin memiliki sesuatu seperti ini tanpa harus mengubah keadaan aslinya:

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

Saya mencoba:

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

tetapi untuk beberapa alasan, itu menghapus properti dari kedua negara.

Bisakah saya membantu melakukan itu?

Vincent Taing
sumber
3
Perhatikan bahwa Object.assignmenciptakan hanya copy dangkal dari statedan oleh karena itu state.cdan newState.cakan menunjuk ke objek bersama sama. Anda mencoba menghapus properti ydari objek bersama cdan bukan dari objek baru newState.
Akseli Palén

Jawaban:

224

Bagaimana dengan menggunakan sintaksis tugas penugasan ?

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

madebydavid
sumber
3
hebat, tanpa perpustakaan tambahan, gunakan es6 dan cukup sederhana.
sbk201
Solusi paling elegan
long.luc
12
Bagus! Berikut ini adalah fungsi pembantu ES6 const deleteProperty = ({[key]: _, ...newObj}, key) => newObj;. Penggunaan: deleteProperty({a:1, b:2}, "a");memberi{b:2}
mikebridge
sangat pintar, saya terlambat untuk yang satu ini, tetapi salah satu favorit baru saya
Gavin
1
Perhatikan bahwa contoh penghapusan dalam akan macet jika deep['c']kosong, jadi dalam kasus umum, Anda mungkin ingin menambahkan tanda centang untuk keberadaan kunci.
Laurent S
46

Saya menemukan metode array ES5 suka filter, mapdan reduceberguna karena mereka selalu mengembalikan array atau objek baru. Dalam hal ini saya akan gunakan Object.keysuntuk beralih pada objek, dan Array#reduceuntuk mengubahnya kembali menjadi objek.

return Object.assign({}, state, {
    c: Object.keys(state.c).reduce((result, key) => {
        if (key !== 'y') {
            result[key] = state.c[key];
        }
        return result;
    }, {})
});
David L. Walsh
sumber
memutar dengan sedikit perubahan saya untuk membuatnya lebih jelas IMO ... juga memungkinkan Anda menghilangkan beberapa properti. const omit = ['prop1', 'prop2'] ... if (omit.indexOf (key) === -1) hasil [key] = state.c [key] hasil pengembalian; ...
josh-sachs
3
ES6 setara untuk mendapatkan salinan myObjectdengan kunci myKeydihapus:Object.keys(myObject).reduce((acc, cur) => cur === myKey ? acc : {...acc, [cur]: myObject[cur]}, {})
transang
38

Anda dapat menggunakan _.omit(object, [paths])dari perpustakaan lodash

path dapat disarangkan misalnya: _.omit(object, ['key1.key2.key3'])

Dmitri
sumber
3
Sayangnya, _.omittidak dapat menghapus properti yang dalam (apa yang diminta OP). Ada omit-deep-lodashmodul untuk tujuan ini.
AlexM
2
Berangkat dari apa yang dikatakan @AlexM. Saya menemukan itu berguna, dan mungkin lebih tepat, bagi kami _.cloneDeep(obj)dari lodash. Itu menyalin objek dengan mudah dan kemudian Anda cukup menggunakan js delete obj.[key]untuk menghapus kunci.
Alex J
Setuju dengan @AlexJ, cloneDeep berfungsi dengan baik dan Anda dapat menggunakan sintaks spread juga: ..._. CloneDeep (status) untuk Redux
Sean Chase
32

Cukup gunakan fitur penghancuran objek 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

Ramon Diogo
sumber
8
const {y, ...c} = state.cmungkin sedikit lebih jelas daripada memiliki dua cdi sisi kiri.
dosentmatter
10
berhati-hatilah: ini tidak berfungsi untuk objek yang dikunci oleh integer
daviestar
3
Dari jawaban di bawah ini, jika Anda perlu referensi nama variabel yang akan dihapus: const name = 'c'maka Anda dapat melakukannya const {[name]:deletedValue, ...newState} = statekemudian kembali newStatedalam peredam Anda. Ini untuk penghapusan kunci tingkat atas
FFF
23

Itu karena Anda menyalin nilai state.cke objek lain. Dan nilai itu adalah pointer ke objek javascript lain. Jadi, kedua pointer tersebut menunjuk ke objek yang sama.

Coba ini:

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;

Anda juga bisa melakukan penyalinan mendalam objek. Lihat pertanyaan ini dan Anda akan menemukan yang terbaik untuk Anda.

Aᴍɪʀ
sumber
2
Ini jawaban yang sangat bagus! state.cadalah referensi, dan referensi sedang disalin dengan baik. Redux menginginkan bentuk keadaan yang dinormalisasi, yang berarti menggunakan id alih-alih referensi ketika negara bagian bersarang. Lihat redux
Ziggy
17

Bagaimana dengan ini:

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

Itu menyaring kunci yang harus dihapus kemudian membangun objek baru dari kunci yang tersisa dan objek awal. Idenya dicuri dari program reaksi luar biasa Tyler McGinnes.

JSBin

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

Juga, jika mencari toolkit pemrograman fungsional, lihat Ramda .

Dominykas Mostauskis
sumber
9

Anda dapat menggunakan pembantu Ketidakmampuan untuk membatalkan pengaturan atribut, dalam kasus Anda:

import update from 'immutability-helper';

const updatedState = update(state, {
  c: {
    $unset: ['y']
  }
});    
Javier P
sumber
Lalu bagaimana cara menghapus semua properti "y" dari setiap item objek array?
5ervant
@ 5ervant Saya pikir itu pertanyaan lain, tapi saya sarankan Anda untuk memetakan array dan menerapkan salah satu solusi yang diberikan di sini
Javier P
8

Pada 2019, opsi lain adalah menggunakan Object.fromEntriesmetode ini. Sudah mencapai tahap 4.

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

Yang menyenangkan tentang ini adalah bahwa ia menangani kunci integer dengan baik.

jian
sumber
3

Masalah yang Anda hadapi adalah Anda tidak mengkloning keadaan awal Anda secara mendalam. Jadi, Anda memiliki salinan dangkal.

Anda dapat menggunakan operator spread

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

Atau mengikuti kode yang sama

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

Saya biasanya menggunakan

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

Saya menyadari ini tidak benar-benar menghapus properti tetapi untuk hampir semua tujuan 1 secara fungsional setara. Sintaks untuk ini jauh lebih sederhana daripada alternatif yang saya rasa merupakan tradeoff yang cukup bagus.

1 Jika Anda menggunakan hasOwnProperty(), Anda harus menggunakan solusi yang lebih rumit.

Tidak dicintai
sumber
1

Saya menggunakan pola ini

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

tetapi dalam buku saya melihat pola lain

return Object.assign({}, state, { name: undefined } )
zloctb
sumber
Yang kedua, tidak menghapus kunci. Itu hanya mengaturnya untuk tidak terdefinisi.
bman
1

utilitas;))

const removeObjectField = (obj, field) => {

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

    return rest;
}

tipe aksi

const MY_Y_REMOVE = 'MY_Y_REMOVE';

pencipta aksi

const myYRemoveAction = (c, y) => {

    const result = removeObjectField(c, y);

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

peredam

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

Seperti yang ditunjukkan dalam beberapa jawaban sudah, itu karena Anda mencoba untuk mengubah keadaan bersarang yaitu. satu tingkat lebih dalam. Solusi kanonik adalah dengan menambahkan peredam pada xtingkat negara:

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

Peredam tingkat lebih dalam

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

Peredam tingkat asli

let newState = Object.assign({}, state, {c: newDeepState});
Mieszko
sumber