Javascript'te koşullu olarak bir nesneye üye nasıl eklenir?


385

Şartlı olarak eklenmiş bir üye ile bir nesne oluşturmak istiyorum. Basit yaklaşım:

var a = {};
if (someCondition)
    a.b = 5;

Şimdi daha deyimsel bir kod yazmak istiyorum. Deniyorum:

a = {
    b: (someCondition? 5 : undefined)
};

Ama şimdi, değeri olan bbir üye . Bu istenen sonuç değildir.aundefined

Kullanışlı bir çözüm var mı?

Güncelleme

Genel davayı birkaç üyeyle halledebilecek bir çözüm arıyorum.

a = {
  b: (conditionB? 5 : undefined),
  c: (conditionC? 5 : undefined),
  d: (conditionD? 5 : undefined),
  e: (conditionE? 5 : undefined),
  f: (conditionF? 5 : undefined),
  g: (conditionG? 5 : undefined),
 };

23
Evet var; kodun ilk biti.
Paolo Bergantino

5
Deyimsel JavaScript diye bir şey olduğundan emin değilim ...
Michael Berkowski

Gerçekten önemli mi? Hiç tanımlamazsanız a.b, geri alma yine de a.bgeri dönecektir undefined.
Teemu

5
@Teemu: inOperatörün ne zaman kullanıldığı önemli olabilir .

1
Şimdilik değişmez nesnelerde koşullu özelliklere sahip olmanın bir yolu yoktur, ancak keşke ES7'ye eklemelerini dilerim, bu özellikle sunucu tarafı programlamada çok kullanışlı olabilir!
Ali

Yanıtlar:


118

Saf Javascript'te, ilk kod snippet'inizden daha deyimsel bir şey düşünemiyorum.

Ancak, jQuery kitaplığını kullanmak söz konusu değilse , belgelerin söylediği gibi $ .extend () gereksinimlerinizi karşılamalıdır:

Tanımsız özellikler kopyalanmaz.

Bu nedenle şunları yazabilirsiniz:

var a = $.extend({}, {
    b: conditionB ? 5 : undefined,
    c: conditionC ? 5 : undefined,
    // and so on...
});

Ve (eğer beklediğiniz sonuçları elde conditionBolduğunu false, daha sonra bmevcut olmaz a).


21
JQuery gerektirmeyen çok daha iyi bir yöntem var. Jamie Hill'in cevabına bir göz atın.
Andrew Rasmussen

13
@Andrew, bu cevap benim yazdığım sırada olmayan ES6'yı gerektiriyor.
Frédéric Hamidi

null aynı şekilde çalışır mı? veya tanımsız olması gerekir mi?
Aous1000

Bu aslında yanlış bir cevaptır, çünkü jQuery kullanır ve bu üçlü koşul bir özelliği bir nesneden kaldırmaz, bu sadece bir özelliği tanımsız olarak ayarlar. Bunu yapmanın doğru yolu için @lagistos cevabına bakınız,
Alexander Kim

Aşağıdaki Jamie'nin cevabına bir göz
atın

868

@InspiredJW bunu ES5 ile yaptı ve @trincot'un işaret ettiği gibi, es6 kullanmak daha iyi bir yaklaşım. Ancak yayma operatörünü ve mantıksal VE kısa devre değerlendirmesini kullanarak biraz daha şeker ekleyebiliriz:

const a = {
   ...(someCondition && {b: 5})
}

2
Bunun doğru olduğundan emin değilim, teklif belirtiyor Null/Undefined Are Ignored, yok falsesayıldığını söylemiyor . Transpiller şu anda buna izin verebilir, ancak uyumlu mu? Aşağıdakiler olmalı {...someCondition ? {b: 5} : null}ama kompakt değil.
Benjamin Dobell

23
Bunun yaygın teklif yapan insanlar için geçerli olup olmadığını sordum ve bunun iyi olduğunu söylediler. github.com/tc39/proposal-object-rest-spread/issues/45 , cc @BenjaminDobell
김민준

73
@AlanH spread operatörü, Object.assign&& operatörünün kısayoluna benzer ve daha düşük önceliğe sahiptir. Özelliği olmayan değeri (boolean, null, undefined, number) yok sayar ve nesnenin tüm özelliklerini ...yerine yerleştirdikten sonra ekler . &&operatörün doğru ise doğru değeri döndürdüğünü veya aksi halde yanlış olduğunu unutmayın . eğer öyleyse someConditiondoğrudur {b : 5}geçilecek ...özelliğini ekleyerek sonuçlanan operatör biçin adeğerle 5. is someConditionfalse, operatöre falseaktarılacaktır .... hiçbir şey eklenmedi. akıllı. Onu seviyorum.
Félix Brunet

11
Harika bir cevap, ancak koşulun ve ortaya çıkan nesnenin parantez içine alınması, bu örneğin okunabilirliğini büyük ölçüde artıracaktır. Herkes JS operatörünün önceliğini ezbere hatırlamıyor.
Nörotransmitter

6
Diğer tek sorun, bunu yanlış booleanlar için kullanamazsınız.
ggb667

92

EcmaScript2015 ile şunları kullanabilirsiniz Object.assign:

Object.assign(a, conditionB ? { b: 1 } : null,
                 conditionC ? { c: 2 } : null,
                 conditionD ? { d: 3 } : null);

Bazı açıklamalar:

Daha da özlü

(@Jamie sivri out olduğu gibi) falsy değerlerin yerine kendi enumerable özellikler içerdiğinden, (şöyle, ikinci noktayı alarak, bunu kısaltabilir false, 0, NaN, null, undefined, '', hariç document.all):

Object.assign(a, conditionB && { b: 1 },
                 conditionC && { c: 2 },
                 conditionD && { d: 3 });


1
İlk çözümü tercih ederim: neler olup bittiğini anlamak daha kolay - İlkel bir değerle ne olacağından emin olmazdım (en azından spesifikasyona bakmadan).
Axel Rauschmayer

1
Üçlü mantığı basitleştiren stenoyu seviyorum. Tam da ihtiyacım olan şey bu. Teşekkür ederim!
theUtherSide

80
const obj = {
   ...(condition) && {someprop: propvalue},
   ...otherprops
}

Canlı Demo:

const obj = {
  ...(true) && {someprop: 42},
  ...(false) && {nonprop: "foo"},
  ...({}) && {tricky: "hello"},
}

console.log(obj);


7
Bu kod snippet'i soruyu çözebilir, ancak bir açıklama dahil olmak , yayınınızın kalitesini artırmaya yardımcı olur. Gelecekte okuyucular için soruyu cevapladığınızı ve bu kişilerin kod önerinizin nedenlerini bilmeyebileceğini unutmayın.
Ralf Stubner


koşul eşleşmezse tanımsız olarak döner.
Mustkeem K

2
Bu işe yarıyor ve diğer IMO cevaplarından daha iyi bir sözdizimine sahip.
Seph Reed

1
Kısa açıklama şu şekildedir: "..." yayma operatörü nesne değişmezini yapıbozumuna ekler ve bunu "obj" e ekler, örneğin bu durumda ... (true) && {someprop: 42}, yeniden yapılandırılacak tüm terim "(true) && {someprop: 42}", bu durumda boolean doğrudur ve terim sadece {someprop: 42} 'i verir, bu daha sonra yapısöktürülür ve obj'e eklenir. eğer boolean yanlışsa, o zaman terim yanlış olur ve hiçbir şey yapısökülmez ve
objeye

50

Boole ile yayılmış sözdiziminin kullanılması (burada önerildiği gibi) geçerli sözdizimi değildir. Spread sadece tekrarlanabilir modellerle kullanılabilir .

Aşağıdakileri öneririm:

const a = {
   ...(someCondition? {b: 5}: {} )
}

2
@HossamMourad, önerilen kod 2 yıldan daha önce yayınlanan bir cevapta zaten kullanıldığından, olmamalıdır. Ve ilk açıklama yanlıştır. Bu geçerli bir sözdizimi:{...false}
trincot

1
@Itai, bu doğru değil; ilkel değerler forma sözdizimi ile kullanıldığında kaydırılır. {...false}geçerli bir sözdizimidir.
trincot

@ trincot, referans verebilir misiniz?
Itai Noam

3
EcmaScript 2018 Dil Spesifikasyonu, bölüm 12.2.6 (.8) CopyDataProperties, değer üzerinde gerçekleştirildiğini belirtir ( falsebu durumda). Bu bir ilkel boksu içerir (bölüm 7.3.23, 7.1.13). MDN'ye bağlanmak zorunda olduğunuz bağlantı parantez içinde bu istisnadan bahseder.
trincot

Referans olarak, aynı konuda genişlediğim yazıya da bakınız .
trincot

26

Gelişmiş Nesne Özelliklerini kullanmaya ve özelliği yalnızca doğruysa ayarlamaya ne dersiniz, örneğin:

[isConditionTrue() && 'propertyName']: 'propertyValue'

Bu nedenle koşul karşılanmazsa, tercih edilen özelliği oluşturmaz ve böylece atabilirsiniz. Bkz. Http://es6-features.org/#ComputedPropertyNames

GÜNCELLEME: Axel Rauschmayer'in blog makalesinde, nesne değişmezleri ve dizilerin içine koşullu olarak giriş eklemek hakkındaki yaklaşımını takip etmek daha da iyidir ( http://2ality.com/2017/04/conditional-literal-entries.html ):

const arr = [
  ...(isConditionTrue() ? [{
    key: 'value'
  }] : [])
];

const obj = {
  ...(isConditionTrue() ? {key: 'value'} : {})
};

Bana çok yardımcı oldu.


1
Neredeyse işe yarayacak. Sorun, ekstra bir falseanahtar ekleyecek olmasıdır . Örneğin {[true && 'a']: 17, [false && 'b']: 42},{a:17, false: 42}
viebel

3
Daha özlü bir yol buldum: ...isConditionTrue() && { propertyName: 'propertyValue' }
Dimitri Reifschneider

Daha iyi bir yol: ... (isConditionTrue ()? {Key: 'value'}: {})
Dimitri Reifschneider

Axel Rauschmayer blog linki bu cevabı veriyor. Makaledeki "... insertIf (cond, 'a')" örneği tam olarak aradığım şeydi. Teşekkürler
Joseph Simpson

20

daha basit,

const a = {
    ...(condition && {b: 1}) // if condition is true 'b' will be added.
}

5

Hedef, nesnenin bağımsız görünmesini ve bir küme parantezinin içinde olmasını sağlamaksa, bunu deneyebilirsiniz:

var a = new function () {
    if (conditionB)
        this.b = 5;

    if (conditionC)
        this.c = 5;

    if (conditionD)
        this.d = 5;
};

5

Bu sunucu tarafını (jquery olmadan) yapmak istiyorsanız, lodash 4.3.0'ı kullanabilirsiniz:

a = _.pickBy({ b: (someCondition? 5 : undefined) }, _.negate(_.isUndefined));

Ve bu lodash 3.10.1 kullanarak çalışıyor

a = _.pick({ b: (someCondition? 5 : undefined) }, _.negate(_.isUndefined));

ES6'da lodash'a gerek yok.
Dan Dascalescu

5
var a = {
    ...(condition ? {b: 1} : '') // if condition is true 'b' will be added.
}

Umarım bu duruma göre bir giriş eklemek için çok etkili bir yoldur. Bir nesne hazır bilgilerinin içine koşullu girişlerin nasıl ekleneceği hakkında daha fazla bilgi için.


3
[...condition?'':['item']]dizeye dize öğesi eklenir
Wayou

Bu cevap Jamie Hill'in bir yıl önceki cevabından nasıl daha iyi ?
Dan Dascalescu

1
@DanDascalescu Jamie Hill'in cevabı cevabımdan daha iyi, bu şekilde düşünmedim ve daha çok üçlü operatörlü bir adamdım.
Madhankumar

4

Bu uzun zamandır cevaplandı, ancak diğer fikirlere baktığımda bazı ilginç türevler buldum:

Aynı özelliğe tanımsız değerler atayın ve daha sonra silin

Anonim bir kurucu kullanarak nesnenizi oluşturun ve tanımsız üyeleri her zaman en sonunda kaldırdığınız kukla üyeye atayın . Bu size üye başına tek bir satır (umarım çok karmaşık değil) + sonunda 1 ek satır verecektir.

var a = new function() {
    this.AlwaysPresent = 1;
    this[conditionA ? "a" : "undef"] = valueA;
    this[conditionB ? "b" : "undef"] = valueB;
    this[conditionC ? "c" : "undef"] = valueC;
    this[conditionD ? "d" : "undef"] = valueD;
    ...
    delete this.undef;
};

4

Tanımlanmamış tüm değerlerinizi koşulsuz olarak ekleyebilir ve daha sonra JSON.stringifyhepsini kaldırmak için kullanabilirsiniz :

const person = {
  name: undefined,
  age: 22,
  height: null
}

const cleaned = JSON.parse(JSON.stringify(person));

// Contents of cleaned:

// cleaned = {
//   age: 22,
//   height: null
// }

2

Bunu yapardım

var a = someCondition ? { b: 5 } : {};

Bir satır kod sürümü ile düzenlendi


koşul yanlışsa, a doğru değildir, çıplaktır.
bingjie2680

@ bingjie2680 O olmadığı açıktır ne bir zaman olmalıdır someCondition yanlıştır. Az önce varsaydım a = undefined. Kolayca şu şekilde değiştirilebilir return { b: undefined };: /
jwchang

@ bingjie2680 Sonra olması gerektiği return {};de elseparçası
jwchang

1
Bu değil gerçekten bunu ... olduğunu ne yapacağını? Yani, kelimenin tam anlamıyla söz diziminin tamamını sizin koşullu yapsanız bile, koşullu işleci neden kullanmıyorsunuz? a = condition ? {b:5} : undefined;

3
Tebrikler, üç ölü basit çizgiyi kazançsız bir şekilde 7 satırlık bir işleve çevirdiniz. Hayır, anonim bir işlev kullanmak bir fayda değildir.

2

Lodash kütüphanesini kullanarak _.omitBy

var a = _.omitBy({
    b: conditionB ? 4 : undefined,
    c: conditionC ? 5 : undefined,
}, _.IsUndefined)

Bu, isteğe bağlı istekleriniz olduğunda kullanışlıdır

var a = _.omitBy({
    b: req.body.optionalA,  //if undefined, will be removed
    c: req.body.optionalB,
}, _.IsUndefined)

0

Şartlı olarak üye ekleme konusundaki ilk yaklaşımınızın gayet iyi olduğunu düşünüyorum. Gerçekten üyeye sahip istememek kabul etmiyorsanız bait adeğerine sahip undefined. Operatör ile undefinedbir fordöngü kullanımı ile bir kontrol eklemek yeterince basittir in. Ancak yine de, undefinedüyeleri filtrelemek için kolayca bir işlev yazabilirsiniz .

var filterUndefined = function(obj) {
  var ret = {};
  for (var key in obj) {
    var value = obj[key];
    if (obj.hasOwnProperty(key) && value !== undefined) {
      ret[key] = value;
    }
  }
  return ret;
};

var a = filterUndefined({
  b: (conditionB? 5 : undefined),
  c: (conditionC? 5 : undefined),
  d: (conditionD? 5 : undefined),
  e: (conditionE? 5 : undefined),
  f: (conditionF? 5 : undefined),
  g: (conditionG? 5 : undefined),
});

Ayrıca delete, nesneyi yerinde düzenlemek için operatörü de kullanabilirsiniz .


0

Bir nesneye sarın

Böyle bir şey biraz daha temiz

 const obj = {
   X: 'dataX',
   Y: 'dataY',
   //...
 }

 const list = {
   A: true && 'dataA',
   B: false && 'dataB',
   C: 'A' != 'B' && 'dataC',
   D: 2000 < 100 && 'dataD',
   // E: conditionE && 'dataE',
   // F: conditionF && 'dataF',
   //...
 }

 Object.keys(list).map(prop => list[prop] ? obj[prop] = list[prop] : null)

Bir diziye sarın

Veya Jamie Hill'in yöntemini kullanmak ve çok uzun bir koşul listesine sahip olmak istiyorsanız, ...sözdizimini birden çok kez yazmanız gerekir . Biraz daha temiz hale getirmek için, bunları bir diziye sarabilir, ardından reduce()tek bir nesne olarak döndürmek için kullanabilirsiniz .

const obj = {
  X: 'dataX',
  Y: 'dataY',
  //...

...[
  true && { A: 'dataA'},
  false && { B: 'dataB'},
  'A' != 'B' && { C: 'dataC'},
  2000 < 100 && { D: 'dataD'},
  // conditionE && { E: 'dataE'},
  // conditionF && { F: 'dataF'},
  //...

 ].reduce(( v1, v2 ) => ({ ...v1, ...v2 }))
}

Veya map()işlevi kullanarak

const obj = {
  X: 'dataX',
  Y: 'dataY',
  //...
}

const array = [
  true && { A: 'dataA'},
  false &&  { B: 'dataB'},
  'A' != 'B' && { C: 'dataC'},
  2000 < 100 && { D: 'dataD'},
  // conditionE && { E: 'dataE'},
  // conditionF && { F: 'dataF'},
  //...

 ].map(val => Object.assign(obj, val))

0

Bu, gelebileceğim en özlü çözüm:

var a = {};
conditionB && a.b = 5;
conditionC && a.c = 5;
conditionD && a.d = 5;
// ...

-1

Lodash kütüphaneyi kullanarak, kullanabilirsiniz _.merge

var a = _.merge({}, {
    b: conditionB ? 4 : undefined,
    c: conditionC ? 5 : undefined,
})
  1. ConditionB false& conditionC ise true, o zamana = { c: 5 }
  2. Her iki conditionB ve conditionC ise true,a = { b: 4, c: 5 }
  3. Her iki conditionB ve conditionC ise false,a = {}

Farklı bir sonuç alıyorum. Kullanıyorum lodash@^4.0.0. undefinedbenim durumuma dahil ediliyor.
JohnnyQ
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.