Dalam Javascript, bagaimana cara menambahkan anggota secara kondisional ke suatu objek?


387

Saya ingin membuat objek dengan anggota ditambahkan kondisional. Pendekatan sederhana adalah:

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

Sekarang, saya ingin menulis kode yang lebih idiomatis. Aku sedang mencoba:

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

Tapi sekarang, badalah anggota ayang nilainya undefined. Ini bukan hasil yang diinginkan.

Apakah ada solusi praktis?

Memperbarui

Saya mencari solusi yang dapat menangani kasus umum dengan beberapa anggota.

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
Ya ada; bit kode pertama.
Paolo Bergantino

6
Tidak yakin ada yang namanya JavaScript idiomatik ...
Michael Berkowski

Apakah itu penting? Jika Anda tidak pernah mendefinisikan a.b, mengambil a.bkembali akan undefinedtetap.
Teemu

6
@ Tuemu: Bisa jadi masalah ketika inoperator digunakan.

1
Tidak ada cara untuk memiliki properti bersyarat dalam objek literal untuk saat ini, tetapi saya berharap mereka menambahkannya dalam ES7, ini bisa sangat berguna terutama dalam pemrograman sisi server!
Ali

Jawaban:


118

Dalam Javascript murni, saya tidak bisa memikirkan hal yang lebih idiomatis daripada cuplikan kode pertama Anda.

Namun, jika menggunakan perpustakaan jQuery tidak keluar dari pertanyaan, maka $ .extend () harus memenuhi persyaratan Anda karena, seperti yang dikatakan dalam dokumentasi:

Properti yang tidak terdefinisi tidak disalin.

Karena itu, Anda dapat menulis:

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

Dan dapatkan hasil yang Anda harapkan (jika conditionBada false, maka btidak akan ada dalam a).


21
Ada metode yang jauh lebih baik yang tidak memerlukan jQuery. Lihatlah jawaban Jamie Hill.
Andrew Rasmussen

13
@Andrew, jawaban itu membutuhkan ES6, yang tidak ada pada saat saya menulis milik saya.
Frédéric Hamidi

apakah null bekerja dengan cara yang sama? atau harus tidak ditentukan?
Aous1000

Ini sebenarnya jawaban yang salah, karena menggunakan jQuery dan kondisi terner ini tidak akan menghapus properti dari objek, ini hanya akan menetapkan properti sebagai tidak terdefinisi. Lihat jawaban @ lagistos untuk cara yang benar untuk melakukan ini,
Alexander Kim

Yap periksa jawaban Jamie di bawah ini: stackoverflow.com/a/40560953/10222449
MadMac

873

Saya pikir @InspiredJW melakukannya dengan ES5, dan seperti yang ditunjukkan @trincot, menggunakan es6 adalah pendekatan yang lebih baik. Tetapi kita dapat menambahkan sedikit lebih banyak gula, dengan menggunakan operator penyebaran, dan evaluasi hubungan pendek DAN logis:

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

2
Saya tidak begitu yakin ini benar, usulannya menyatakan Null/Undefined Are Ignored, tidak dikatakan falsediabaikan. Transponder dapat memungkinkan hal ini sampai saat ini, tetapi apakah itu sesuai? Berikut ini seharusnya {...someCondition ? {b: 5} : null}tetapi tidak kompak.
Benjamin Dobell

24
Saya bertanya apakah ini berlaku untuk orang-orang yang membuat proposal spread dan mereka mengatakan ini baik-baik saja. github.com/tc39/proposal-object-rest-spread/issues/45 , cc @BenjaminDobell
김민준

73
@AlanH operator spread seperti singkatan Object.assigndan memiliki prioritas lebih rendah dari operator &&. Ini mengabaikan nilai tanpa properti (boolean, null, undefined, number), dan menambahkan semua properti objek setelah ...di tempat. ingat &&operator mengembalikan nilai yang benar jika benar, atau salah jika sebaliknya. jadi jika someConditionbenar, {b : 5}akan diteruskan ke ...operator, sehingga menambah properti buntuk adengan nilai 5. is someConditionis false, falseakan diteruskan ke ...operator. sehingga tidak ada yang ditambahkan. itu pintar. Aku menyukainya.
Félix Brunet

11
Jawaban yang bagus, tetapi menempatkan kondisi dan objek yang dihasilkan menjadi tanda kurung akan sangat meningkatkan keterbacaan contoh ini. Tidak semua orang mengingat prioritas operator JS.
Neurotransmitter

6
Satu-satunya masalah lainnya adalah Anda tidak dapat menggunakan ini untuk boolean palsu.
ggb667

92

Dengan EcmaScript2015 Anda dapat menggunakan Object.assign:

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

Beberapa komentar:

  • Object.assign memodifikasi argumen pertama di tempat, tetapi juga mengembalikan objek yang diperbarui: sehingga Anda dapat menggunakan metode ini dalam ekspresi yang lebih besar yang selanjutnya memanipulasi objek.
  • Alih-alih nullAnda bisa lulus undefinedatau {}, dengan hasil yang sama. Anda bahkan bisa memberikan 0, karena nilai primitif dibungkus, dan Numbertidak memiliki properti enumerable sendiri .

Bahkan lebih ringkas

Mengambil titik kedua lebih lanjut, Anda bisa mempersingkat sebagai berikut (sebagai @Jamie memiliki keluar runcing), sebagai nilai-nilai falsy tidak memiliki sifat enumerable sendiri ( false, 0, NaN, null, undefined, '', kecuali document.all):

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


1
Saya lebih suka solusi awal: lebih mudah untuk mencari tahu apa yang terjadi - saya tidak akan yakin apa yang terjadi dengan nilai primitif (setidaknya tidak tanpa melihat spesifikasi).
Axel Rauschmayer

1
Saya suka tulisan cepat yang menyederhanakan logika terner. Inilah yang saya butuhkan. Terima kasih!
theUtherSide

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

Demo Langsung:

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

console.log(obj);


7
Sementara potongan kode ini dapat menyelesaikan pertanyaan, termasuk penjelasan sangat membantu untuk meningkatkan kualitas posting Anda. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, dan orang-orang itu mungkin tidak tahu alasan untuk saran kode Anda.
Ralf Stubner

1
Apa yang ditambahkan jawaban ini pada jawaban Jamie Hill dari 2 tahun sebelumnya ?
Dan Dascalescu

jika cond tidak cocok dari ini akan kembali tidak terdefinisi.
Mustkeem K

2
Ini berfungsi, dan memiliki sintaksis yang lebih bagus daripada jawaban IMO lainnya.
Seph Reed

1
Penjelasan singkatnya seperti ini: "..." operator spread mendekonstruksi objek literal dan menambahkannya ke "obj" misalnya dalam kasus ini ... (true) && {someprop: 42}, seluruh istilah yang akan didekonstruksi adalah "(true) && {someprop: 42}", dalam hal ini boolean benar dan istilahnya hanya menghasilkan {someprop: 42} yang kemudian didekonstruksi dan ditambahkan ke dalam obj. jika boolean itu gantinya palsu, maka istilah itu hanya akan salah, dan tidak ada yang akan didekonstruksi dan ditambahkan ke dalam objek
Qiong Wu

50

Menggunakan sintaks spread dengan boolean (seperti yang disarankan di sini) bukan sintaks yang valid. Spread hanya dapat digunakan dengan iterables .

Saya menyarankan yang berikut ini:

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

2
@ HossamMourad, seharusnya tidak, karena kode yang disarankan sudah digunakan dalam jawaban yang diposting lebih dari 2 tahun yang lalu. Dan komentar pertama salah. Ini adalah sintaks yang valid:{...false}
trincot

1
@ Itai, ini tidak benar; nilai-nilai primitif dibungkus ketika digunakan dengan sintaks spread. {...false}adalah sintaks yang valid.
trincot

@trincot, bisakah Anda memberikan referensi?
Itai Noam

3
Spesifikasi Bahasa EcmaScript 2018, bagian 12.2.6 (.8) menentukan yang CopyDataPropertiesdilakukan pada nilai ( falsedalam hal ini). Ini melibatkan tinju primitif (bagian 7.3.23, 7.1.13). Tautan yang Anda miliki ke MDN menyebutkan "pengecualian" ini dalam tanda kurung.
trincot

Sebagai referensi, lihat juga posting ini di mana saya memperluas subjek yang sama ini.
trincot

26

Bagaimana dengan menggunakan Properti Objek yang Ditingkatkan dan hanya mengatur properti jika itu benar, misalnya:

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

Jadi jika kondisinya tidak terpenuhi, itu tidak menciptakan properti yang disukai dan dengan demikian Anda dapat membuangnya. Lihat: http://es6-features.org/#ComputedPropertyNames

UPDATE: Bahkan lebih baik untuk mengikuti pendekatan Axel Rauschmayer dalam artikel blognya tentang menambahkan entri dalam kondisi literal dan array objek ( http://2ality.com/2017/04/conditional-literal-entries.html ):

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

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

Cukup banyak membantu saya.


1
Itu hampir akan berhasil. Masalahnya adalah itu akan menambah falsekunci tambahan . Misalnya, {[true && 'a']: 17, [false && 'b']: 42}adalah{a:17, false: 42}
Viebel

3
Saya menemukan cara yang lebih ringkas: ...isConditionTrue() && { propertyName: 'propertyValue' }
Dimitri Reifschneider

Cara yang lebih baik: ... (isConditionTrue ()? {Key: 'value'}: {})
Dimitri Reifschneider

Tautan blog Axel Rauschmayer membuat jawaban ini. Contoh "... insertIf (cond, 'a')" di artikel persis seperti yang saya cari. Terima kasih
Joseph Simpson

21

lebih disederhanakan,

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

5

Jika tujuannya adalah agar objek tampak mandiri dan berada dalam satu set kawat gigi, Anda dapat mencoba ini:

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

    if (conditionC)
        this.c = 5;

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

5

Jika Anda ingin melakukan sisi server ini (tanpa jquery), Anda dapat menggunakan lodash 4.3.0:

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

Dan ini berfungsi menggunakan lodash 3.10.1

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

Tidak perlu untuk Lodash di ES6.
Dan Dascalescu

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

Saya harap ini adalah cara yang lebih efisien untuk menambahkan entri berdasarkan kondisinya. Untuk info lebih lanjut tentang cara menambahkan entri secara kondisional ke dalam objek literal.


3
[...condition?'':['item']]ini akan menambahkan item string ke dalam array
Wayou

Bagaimana jawaban ini lebih baik daripada jawaban Jamie Hill dari tahun sebelumnya ?
Dan Dascalescu

1
@DanDascalescu Jawaban Jamie Hill lebih baik daripada jawaban saya, saya tidak berpikir seperti itu dan saya dulu lebih seperti orang operator ternary.
Madhankumar

4

Ini sudah lama dijawab, tetapi melihat ide-ide lain saya menemukan beberapa turunan yang menarik:

Tetapkan nilai yang tidak terdefinisi ke properti yang sama dan hapus setelahnya

Buat objek Anda menggunakan konstruktor anonim dan selalu tetapkan anggota yang tidak ditentukan untuk anggota boneka yang sama yang Anda hapus di bagian paling akhir. Ini akan memberi Anda satu baris (saya harap tidak terlalu rumit) per anggota + 1 baris tambahan di akhir.

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

Anda dapat menambahkan semua nilai yang tidak ditentukan tanpa kondisi dan kemudian gunakan JSON.stringifyuntuk menghapus semuanya:

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

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

// Contents of cleaned:

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

2

Saya akan melakukan ini

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

Diedit dengan satu versi kode baris


jika kondisinya salah, a ditentukan, yang tidak benar.
bingjie2680

@ bingjie2680 Tidak jelas apa yang seharusnya ketika someCondition salah. Saya hanya berasumsi a = undefined. Itu dapat dengan mudah diubah dengan return { b: undefined };: /
jwchang

@ bingjie2680 Kemudian itu harus return {};di elsebagian
jwchang

1
Itu tidak benar-benar apa yang akan Anda lakukan ... itu? Maksud saya bahkan jika Anda membuat seluruh sintaksis literal bersyarat seperti yang Anda miliki, mengapa Anda tidak menggunakan operator kondisional? a = condition ? {b:5} : undefined;

3
Selamat, Anda baru saja mengubah tiga garis mati-sederhana menjadi fungsi 7 garis tanpa hasil. Tidak, menggunakan fungsi anonim tidak bermanfaat.

2

Menggunakan perpustakaan lodash, Anda dapat menggunakan _.omitBy

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

Hasil ini berguna ketika Anda memiliki permintaan yang opsional

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

0

Saya pikir pendekatan pertama Anda untuk menambahkan anggota dengan syarat baik-baik saja. Aku tidak benar-benar setuju dengan tidak ingin memiliki anggota bdari adengan nilai undefined. Ini cukup sederhana untuk menambahkan undefinedcek dengan penggunaan forloop dengan inoperator. Tapi bagaimanapun, Anda dapat dengan mudah menulis fungsi untuk menyaring undefinedanggota.

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

Anda juga dapat menggunakan deleteoperator untuk mengedit objek di tempatnya.


0

Bungkus menjadi sebuah objek

Sesuatu seperti ini agak lebih bersih

 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)

Bungkus menjadi sebuah array

Atau jika Anda ingin menggunakan metode Jamie Hill dan memiliki daftar syarat yang sangat panjang maka Anda harus menulis ...sintaks beberapa kali. Untuk membuatnya sedikit lebih bersih, Anda bisa membungkusnya menjadi sebuah array, lalu gunakan reduce()untuk mengembalikannya sebagai objek tunggal.

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

Atau menggunakan map()fungsi

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

Ini adalah solusi paling ringkas yang bisa saya buat:

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

0

Tetapkan var dengan letdan hanya menetapkan properti baru

let msg = {
    to: "hito@email.com",
    from: "hifrom@email.com",
    subject: "Contact form",    
};

if (file_uploaded_in_form) { // the condition goes here
    msg.attachments = [ // here 'attachments' is the new property added to msg Javascript object
      {
        content: "attachment",
        filename: "filename",
        type: "mime_type",
        disposition: "attachment",
      },
    ];
}

Sekarang msgmenjadi

{
    to: "hito@email.com",
    from: "hifrom@email.com",
    subject: "Contact form",
    attachments: [
      {
        content: "attachment",
        filename: "filename",
        type: "mime_type",
        disposition: "attachment",
      },
    ]
}

Menurut saya ini adalah solusi yang sangat sederhana dan mudah.


-1

Menggunakan perpustakaan lodash, Anda bisa menggunakan _.merge

var a = _.merge({}, {
    b: conditionB ? 4 : undefined,
    c: conditionC ? 5 : undefined,
})
  1. Jika conditionB adalah false& conditionC adalahtrue , makaa = { c: 5 }
  2. Jika keduanya conditionB & conditionC adalah true, makaa = { b: 4, c: 5 }
  3. Jika kedua conditionB & conditionC adalah false, makaa = {}

Saya mendapat hasil berbeda. Saya menggunakan lodash@^4.0.0. undefinedsedang dimasukkan dalam kasus saya.
JohnnyQ
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.