Değeri mutasyon olmadan nesneden kaldırın


102

Orijinal nesneyi değiştirmeden belirli bir anahtardaki bir nesneden bir değeri kaldırmanın iyi ve kısa yolu nedir?

Şöyle bir şey yapmak istiyorum:

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

Bu tür görevler için çok sayıda değişmezlik kitaplığı olduğunu biliyorum, ancak kitaplık olmadan kurtulmak istiyorum. Ancak bunu yapmak için, yöntemi bir fayda işlevi olarak soyutlamadan kod boyunca kullanılabilecek kolay ve kısa bir yola sahip olmak gerekir.

Örneğin, bir değer eklemek için aşağıdakileri yapıyorum:

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

Bu oldukça kısadır, hatırlaması kolaydır ve bir yardımcı fonksiyon gerektirmez.

Bir değeri kaldırmak için buna benzer bir şey var mı? ES6 çok açığız.

Çok teşekkür ederim!


"Nesneyi değiştirmeden bir değeri kaldırın" hiçbir anlam ifade etmiyor. Aynı anda bir şeyden çıkarıp sağlam tutamazsınız. Aslında yaptığınız şey kısmi bir kopya çıkarmak.
JJJ

Yanıtlar:


226

Güncelleme:

Zor bir Yıkım atamasına sahip bir nesneden bir özelliği kaldırabilirsiniz :

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

Yine de, kaldırmak istediğiniz özellik adı statikse, bunu tek satırlık basit bir satırla kaldırabilirsiniz:

let {lastname, ...o2} = o

En kolay yol basitçe yapmaktır Veya nesnenizi değiştirmeden önce klonlayabilirsiniz:

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

Alternatif olarak omit, lodashyardımcı program kitaplığındaki işlevi kullanabilirsiniz :

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

Lodash paketinin bir parçası olarak veya bağımsız bir lodash.omit paketi olarak mevcuttur.


3
Bu gerçekten en basit çözüm mü? Örneğin diziler için, a2 = a1.filter(el => el.id !== id)bir dizi içindeki bir elemanı belirli bir id ile atlamak için yapabilirsiniz . Bir nesne için böyle bir şey yok mu?
amann

1
@Amann'a katılıyorum. Ya orada olan daha iyi bir çözüm veya ES6 gerçekten daha işlevsel bir şekilde JS kullanışlı yapma konusunda bir şey kaçırmış. Örneğin. Ruby haskeep_if
Augustin Riedinger

1
@AugustinRiedinger aslında, bir yolu var. Güncellememe bakın.
Leonid Beschastny

2
Güncellenmiş yıkım çözümü harika. Zihnim bundan biraz etkilenmiş olsa da. Neden const {[prop], ...rest} = objçalışmıyor? Çıkarılan özelliği neden yeni bir değişkene atamanız gerekiyor ( omitbu durumda)?
branweb

1
@ Antoni4 kuralınıza "ihmal" veya "atlanmış" ekleyebilirsiniz no-unused-vars, bu sadece normal ifadedir .
adrianmc

35

ES7 nesne imhası ile:

const myObject = {
  a: 1,
  b: 2,
  c: 3
};
const { a, ...noA } = myObject;
console.log(noA); // => { b: 2, c: 3 }

1
ya abir değişkende saklanırsa const x = 'a'?
FFF

Hiçbir şeyi değiştirmez. a, dizinin yalnızca ilk öğesini ifade eder. const { a,b, ...noA } = myObject; console.log(noA); // => {c: 3 }
Şunun

1
bu en zarif ve basit çözüm
Joe

1
Anahtarlar sayı ise bunu yapmanın bir yolu var mı?
Marc Sloth Eastman

25

tek satırlık çözüm

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

4

Yukarıdaki yorumlarda önerildiği objectgibi, bunu kullanmaktan hoşlandığım listenizdeki birden fazla öğeyi kaldırmak için genişletmek istiyorsanız filter. vereduce

Örneğin

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


1
Kendinizi bir döngüden kurtarmak için neden azaltmada filtre koşulunu uygulamıyorsunuz?
Ivo Sabev

ipucu için teşekkürler @IvoSabev Kodumda filtrelediğim ve azalttığım birkaç işlev var, ancak önerinizi döngüyü kaydetmek için ileriye dönük olarak değerlendireceğim.
ak85

4

Performans getiren biraz baharat eklemek için. Bu ipliği kontrol edin

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

Silme operatörünün kullanılması, V8 gizli sınıflar kalıbı için performansı olumsuz etkiler. Genel olarak kullanmamanız tavsiye edilir.

Alternatif olarak, nesnenin kendi numaralandırılabilir özelliklerini kaldırmak için, bu özellikler olmadan yeni bir nesne kopyası oluşturabiliriz (örnek lodash kullanarak):

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

Veya özellik değerini null veya undefined olarak tanımlayın (bu, JSON'a serileştirirken örtük olarak yoksayılır):

o.prop = tanımsız

Sen de yok etme yolunu kullanabilirsin

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

Ve daha pratik bir örnek:

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

Gördüğünüz gibi [somePropsVarForDynamicName]: scopeVarName, dinamik adlar için sözdizimi kullanabilirsiniz . Ve hepsini parantez içine (yeni blok) koyabilirsiniz, böylece geri kalanı ondan sonra toplanacaktır.

İşte bir test: görüntü açıklamasını buraya girin

exec:

görüntü açıklamasını buraya girin

Veya aşağıdaki gibi bir işlevle gidebiliriz

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

daktilo için

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

Kullanım:

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

a = deleteProps(a, 'propB'); 

// or 

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

Bu şekilde yeni bir nesne yaratılır. Ve nesnenin hızlı özelliği korunur. Hangisi önemli veya önemli olabilir. Haritalama ve nesneye birçok kez erişilecekse.

Ayrıca ilişki undefinedkurmak, birlikte gitmek için iyi bir yol olabilir. Ödeyebileceğin zaman. Ve anahtarlar için de değeri kontrol edebilirsiniz. Örneğin, tüm aktif anahtarları almak için aşağıdaki gibi bir şey yaparsınız:

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.

Tanımsız, büyük liste için uygun değildir. Ya da zaman içinde birçok donanımın geleceği geliştirme. Bellek kullanımı artmaya devam edeceği ve asla temizlenmeyeceği için. Yani kullanıma bağlıdır. Ve sadece yeni bir nesne yaratmak iyi bir yol gibi görünüyor.

Sonra Premature optimization is the root of all evilirade devreye girer. Dolayısıyla, değiş tokuşun farkında olmanız gerekir. Ve neye ihtiyaç duyulur ve ne olmaz.

Lodash'tan _.omit () hakkında not

Sürüm 5'ten kaldırılmıştır. Depoda bulamazsınız. Ve işte bundan bahseden bir konu.

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

v8

İyi bir okuma olan bunu kontrol edebilirsiniz https://v8.dev/blog/fast-properties


1

İmha etmeye çalışırsanız, ESLint kural standardından kabul edilen yanıtla ilgili sorunum:

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

2 yeni değişken notNeededve alsoNotNeededartık kullanılmadıkları için kurulumunuza bağlı olarak bir uyarı veya hata verebilir. Peki kullanılmadıysa neden yeni değişkenler oluşturalım?

deleteİşlevi gerçekten kullanmanız gerektiğini düşünüyorum .


4
no-unused-varsKural aslında bir seçeneği vardır ignoreRestSiblings: Bu kullanım durumunda kapsayacak şekilde şimdi eslint.org/docs/rules/no-unused-vars#ignorerestsiblings
Amann

0

lodash clone ileDerin ve sil

(not: sığ nesneler yerine zona klonu kullanılabilir)

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}

0

Kodum için map () 'in dönüş değeri için kısa bir versiyon istedim, ancak multiline / multli işlem çözümleri "çirkin" idi. Anahtar özellik eskidir void(0)hangi kararlılık undefined.

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

Özellik, nesnede kalır:

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

ama gönderme çerçevesi onu benim için öldürüyor (bc stringify):

console.log(JSON.stringify(o2)) // {"firstname":"Jane","age":31}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.