Hapus nilai dari objek tanpa mutasi

105

Apa cara yang baik dan singkat untuk menghapus nilai dari suatu objek pada kunci tertentu tanpa mengubah objek aslinya?

Saya ingin melakukan sesuatu seperti:

let o = {firstname: 'Jane', lastname: 'Doe'};
let o2 = doSomething(o, 'lastname');
console.log(o.lastname); // 'Doe'
console.log(o2.lastname); // undefined

Saya tahu ada banyak perpustakaan kekekalan untuk tugas-tugas semacam itu, tetapi saya ingin pergi tanpa perpustakaan. Tetapi untuk melakukan ini, persyaratannya adalah memiliki cara yang mudah dan singkat yang dapat digunakan di seluruh kode, tanpa mengabstraksi metode sebagai fungsi utilitas.

Misalnya untuk menambahkan nilai, saya melakukan hal berikut:

let o2 = {...o1, age: 31};

Ini cukup singkat, mudah diingat dan tidak memerlukan fungsi utilitas.

Apakah ada sesuatu seperti ini untuk menghapus nilai? ES6 sangat disambut.

Terima kasih banyak!

amann
sumber
"Menghapus nilai tanpa mengubah objek" tidak masuk akal. Anda tidak dapat menghapus dari sesuatu dan menjaganya tetap utuh pada saat yang bersamaan. Apa yang sebenarnya Anda lakukan adalah membuat salinan parsial.
JJJ

Jawaban:

231

Memperbarui:

Anda dapat menghapus properti dari objek dengan tugas Penghancuran yang rumit :

const doSomething = (obj, prop) => {
  let {[prop]: omit, ...res} = obj
  return res
}

Padahal, jika nama properti yang ingin Anda hapus bersifat statis, Anda dapat menghapusnya dengan satu baris sederhana:

let {lastname, ...o2} = o

Cara termudah adalah dengan Atau Anda bisa mengkloning objek Anda sebelum memutasinya:

const doSomething = (obj, prop) => {
  let res = Object.assign({}, obj)
  delete res[prop]
  return res
}

Atau Anda dapat menggunakan omitfungsi dari lodashpustaka utilitas :

let o2 = _.omit(o, 'lastname')

Ini tersedia sebagai bagian dari paket lodash , atau sebagai paket lodash.omit yang berdiri sendiri .

Leonid Beschastny
sumber
3
Apakah itu benar-benar solusi yang paling sederhana? Misalnya untuk array yang dapat Anda lakukan a2 = a1.filter(el => el.id !== id)untuk menghilangkan elemen dalam array dengan id tertentu. Apakah tidak ada benda yang seperti itu?
amann
1
Saya setuju dengan @amann. Entah ada adalah solusi yang lebih baik, atau ES6 benar-benar melewatkan sesuatu tentang membuat JS dapat digunakan dengan cara yang lebih fungsional. Misalnya. Ruby memilikikeep_if
Augustin Riedinger
1
@AugustinRiedinger sebenarnya, ada jalan. Lihat pembaruan saya.
Leonid Beschastny
2
Solusi perusakan yang diperbarui sangat bagus. Meskipun pikiranku sedikit terpesona olehnya. Mengapa tidak const {[prop], ...rest} = objberhasil? Mengapa Anda harus menetapkan properti yang diekstrak ke variabel baru ( omitdalam kasus ini)?
branweb
1
@ Antoni4 Anda dapat menambahkan "menghilangkan" atau "dihilangkan" ke no-unused-varsaturan Anda , itu hanya regex.
adrianmc
37

Dengan objek ES7 yang merusak:

const myObject = {
  a: 1,
  b: 2,
  c: 3
};
const { a, ...noA } = myObject;
console.log(noA); // => { b: 2, c: 3 }
senbon
sumber
1
bagaimana jika adisimpan dalam variabel const x = 'a'?
FFF
Tidak mengubah apa pun. a hanya mengacu pada elemen pertama dari array. Ini bisa jadi seperti ini: const { a,b, ...noA } = myObject; console.log(noA); // => {c: 3 }
senbon
1
ini adalah solusi yang paling elegan dan sederhana
Joe
1
Apakah ada cara untuk melakukan ini jika kuncinya adalah angka?
Marc Sloth Eastman
25

solusi satu baris

const removeKey = (key, {[key]: _, ...rest}) => rest;
punksta
sumber
4

Seperti yang disarankan dalam komentar di atas jika Anda ingin memperpanjang ini untuk menghapus lebih dari satu item dari Anda yang objectingin saya gunakan filter. danreduce

misalnya

    const o = {
      "firstname": "Jane",
      "lastname": "Doe",
      "middlename": "Kate",
      "age": 23,
      "_id": "599ad9f8ebe5183011f70835",
      "index": 0,
      "guid": "1dbb6a4e-f82d-4e32-bb4c-15ed783c70ca",
      "isActive": true,
      "balance": "$1,510.89",
      "picture": "http://placehold.it/32x32",
      "eyeColor": "green",
      "registered": "2014-08-17T09:21:18 -10:00",
      "tags": [
        "consequat",
        "ut",
        "qui",
        "nulla",
        "do",
        "sunt",
        "anim"
      ]
    };

    const removeItems = ['balance', 'picture', 'tags']
    console.log(formatObj(o, removeItems))

    function formatObj(obj, removeItems) {
      return {
        ...Object.keys(obj)
          .filter(item => !isInArray(item, removeItems))
          .reduce((newObj, item) => {
            return {
              ...newObj, [item]: obj[item]
            }
          }, {})
      }
    }

    function isInArray(value, array) {
      return array.indexOf(value) > -1;
    }

ak85
sumber
1
Mengapa Anda tidak menerapkan kondisi filter di reduce, sehingga Anda dapat menghemat satu loop?
Ivo Sabev
terima kasih atas tip @IvoSabev Saya memiliki beberapa fungsi dalam kode saya di mana saya melakukan filter kemudian mengurangi tetapi akan mempertimbangkan saran Anda ke depan untuk menyimpan loop.
ak85
4

Untuk menambahkan beberapa bumbu membawa Kinerja. Periksa utas ini di bawah

https://github.com/googleapis/google-api-nodejs-client/issues/375

Penggunaan operator delete memiliki efek negatif kinerja untuk pola kelas tersembunyi V8. Secara umum disarankan untuk tidak menggunakannya.

Alternatifnya, untuk menghapus properti enumerable milik objek, kita dapat membuat salinan objek baru tanpa properti tersebut (contoh menggunakan lodash):

_.omit (o, 'prop', 'prop2')

Atau bahkan tentukan nilai properti menjadi null atau undefined (yang secara implisit diabaikan saat membuat serial ke JSON):

o.prop = tidak ditentukan

Anda juga bisa menggunakan cara yang merusak

const {remov1, remov2, ...new} = old;
old = new;

Dan contoh yang lebih praktis:

this._volumes[this._minCandle] = undefined;
{ 
     const {[this._minCandle]: remove, ...rest} = this._volumes;
     this._volumes = rest; 
}

Seperti yang Anda lihat, Anda dapat menggunakan [somePropsVarForDynamicName]: scopeVarNamesintaks untuk nama dinamis. Dan Anda bisa meletakkan semua dalam tanda kurung (blok baru) sehingga sisanya akan dikumpulkan setelah itu.

Berikut tesnya: masukkan deskripsi gambar di sini

eksekutif:

masukkan deskripsi gambar di sini

Atau kita bisa pergi dengan beberapa fungsi seperti

function deleteProps(obj, props) {
    if (!Array.isArray(props)) props = [props];
    return Object.keys(obj).reduce((newObj, prop) => {
        if (!props.includes(prop)) {
            newObj[prop] = obj[prop];
        }
        return newObj;
    }, {});
}

untuk naskah ketikan

function deleteProps(obj: Object, props: string[]) {
    if (!Array.isArray(props)) props = [props];
    return Object.keys(obj).reduce((newObj, prop) => {
        if (!props.includes(prop)) {
            newObj[prop] = obj[prop];
        }
        return newObj;
    }, {});
}

Pemakaian:

let a = {propH: 'hi', propB: 'bye', propO: 'ok'};

a = deleteProps(a, 'propB'); 

// or 

a = deleteProps(a, ['propB', 'propO']);

Dengan cara ini objek baru dibuat. Dan properti cepat dari objek disimpan. Yang bisa penting atau penting. Jika pemetaan dan objek akan diakses berkali-kali.

Juga bergaul undefinedbisa menjadi cara yang baik untuk dilakukan. Bila Anda mampu membelinya. Dan untuk kunci Anda juga dapat memeriksa nilainya. Misalnya untuk mendapatkan semua kunci aktif Anda melakukan sesuatu seperti:

const allActiveKeys = Object.keys(myObj).filter(k => myObj[k] !== undefined);
//or
const allActiveKeys = Object.keys(myObj).filter(k => myObj[k]); // if any false evaluated value is to be stripped.

Undefined tidak cocok untuk daftar besar. Atau pengembangan dari waktu ke waktu dengan banyak props yang akan masuk. Karena penggunaan memori akan terus bertambah dan tidak akan pernah dibersihkan. Jadi tergantung dari penggunaannya. Dan hanya membuat objek baru sepertinya merupakan cara yang baik.

Kemudian Premature optimization is the root of all evilakan dimulai. Jadi, Anda perlu waspada terhadap trade off. Dan apa yang dibutuhkan dan apa yang tidak.

Catatan tentang _.omit () dari lodash

Ini dihapus dari versi 5. Anda tidak dapat menemukannya di repo. Dan di sini ada masalah yang membicarakannya.

https://github.com/lodash/lodash/issues/2930

v8

Anda dapat memeriksa ini yang merupakan bacaan yang baik https://v8.dev/blog/fast-properties

Mohamed Allal
sumber
1

Masalah saya dengan jawaban yang diterima, dari standar aturan ESLint, jika Anda mencoba untuk merusak:

    const { notNeeded, alsoNotNeeded, ...rest } = { ...ogObject };

2 variabel baru, notNeededdan alsoNotNeededmungkin memunculkan peringatan atau kesalahan tergantung pada pengaturan Anda karena sekarang tidak digunakan. Jadi mengapa membuat vars baru jika tidak digunakan?

Saya pikir Anda harus deletebenar-benar menggunakan fungsinya.

Phil Lucks
sumber
4
The no-unused-varsAturan sebenarnya memiliki opsi ignoreRestSiblingssekarang untuk menutupi kasus penggunaan ini: eslint.org/docs/rules/no-unused-vars#ignorerestsiblings
amann
0

dengan lodash cloneDeep dan hapus

(catatan: klon lodash dapat digunakan sebagai pengganti untuk objek dangkal)

const obj = {a: 1, b: 2, c: 3}
const unwantedKey = 'a'

const _ = require('lodash')
const objCopy = _.cloneDeep(obj)
delete objCopy[unwantedKey]
// objCopy = {b: 2, c: 3}
FFF
sumber
0

Untuk kode saya, saya menginginkan versi singkat untuk nilai kembalian map () tetapi solusi operasi multiline / mutli "jelek". Fitur utamanya adalah yang lama void(0)yang memutuskan untuk undefined.

let o2 = {...o, age: 31, lastname: void(0)};

Properti tetap berada di objek:

console.log(o2) // {firstname: "Jane", lastname: undefined, age: 31}

tetapi kerangka transmisi membunuhnya untuk saya (bc stringify):

console.log(JSON.stringify(o2)) // {"firstname":"Jane","age":31}
Jonny
sumber