Keduanya Object.assign dan Object spread hanya melakukan penggabungan yang dangkal.
Contoh masalah:
// No object nesting
const x = { a: 1 }
const y = { b: 1 }
const z = { ...x, ...y } // { a: 1, b: 1 }
Output adalah apa yang Anda harapkan. Namun jika saya coba ini:
// Object nesting
const x = { a: { a: 1 } }
const y = { a: { b: 1 } }
const z = { ...x, ...y } // { a: { b: 1 } }
Dari pada
{ a: { a: 1, b: 1 } }
Anda mendapatkan
{ a: { b: 1 } }
x sepenuhnya ditimpa karena sintaks penyebaran hanya berjalan satu tingkat dalam. Ini sama denganObject.assign()
.
Apakah ada cara untuk melakukan ini?
Jawaban:
Tidak.
sumber
Saya tahu ini sedikit masalah lama tapi solusi termudah di ES2015 / ES6 yang bisa saya buat sebenarnya cukup sederhana, menggunakan Object.assign (),
Semoga ini membantu:
Contoh penggunaan:
Anda akan menemukan versi yang tidak dapat diubah dari jawaban ini di bawah.
Perhatikan bahwa ini akan menyebabkan rekursi tak terbatas pada referensi melingkar. Ada beberapa jawaban bagus di sini tentang cara mendeteksi referensi melingkar jika Anda pikir Anda akan menghadapi masalah ini.
sumber
item !== null
seharusnya tidak diperlukan di dalamisObject
, karenaitem
sudah diperiksa kebenarannya di awal kondisiObject.assign(target, { [key]: {} })
kalau bisa begitu sajatarget[key] = {}
?target[key] = source[key]
bukannyaObject.assign(target, { [key]: source[key] });
target
. Misalnya,mergeDeep({a: 3}, {a: {b: 4}})
akan menghasilkanNumber
objek augmented , yang jelas tidak diinginkan. Juga,isObject
tidak menerima array, tetapi menerima jenis objek asli lainnya, sepertiDate
, yang tidak boleh disalin dalam.Anda dapat menggunakan gabungan Lodash :
sumber
{ 'a': [{ 'b': 2 }, { 'c': 3 }, { 'd': 4 }, { 'e': 5 }] }
?{ 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
benar, karena kami menggabungkan elemen array. Unsur0
dariobject.a
yaitu{b: 2}
, unsur0
dariother.a
adalah{c: 3}
. Ketika keduanya digabungkan karena mereka memiliki indeks array yang sama, hasilnya adalah{ 'b': 2, 'c': 3 }
, yang merupakan elemen0
dalam objek baru.Masalahnya adalah non-sepele ketika datang ke objek host atau segala jenis objek yang lebih kompleks daripada sekumpulan nilai
Hal lain yang perlu diingat: Objek grafik yang berisi siklus. Biasanya tidak sulit untuk ditangani - cukup simpan a
Set
objek sumber yang sudah dikunjungi - tetapi sering dilupakan.Anda mungkin harus menulis fungsi penggabungan-dalam yang hanya mengharapkan nilai-nilai primitif dan objek-objek sederhana - paling banyak tipe yang dapat ditangani oleh algoritma klon terstruktur - sebagai sumber penggabungan. Lempar jika menemukan sesuatu yang tidak dapat ditangani atau hanya ditugaskan dengan referensi alih-alih penggabungan yang mendalam.
Dengan kata lain, tidak ada algoritma satu ukuran untuk semua, Anda harus memutar sendiri atau mencari metode pustaka yang terjadi untuk menutupi kasus penggunaan Anda.
sumber
Berikut adalah versi jawaban @ Salakar yang tidak berubah (tidak mengubah input). Berguna jika Anda melakukan hal-hal jenis pemrograman fungsional.
sumber
key
sebagai nama properti, yang kemudian akan membuat "kunci" nama properti. Lihat: es6-features.org/#ComputedPropertyNamesisObject
Anda tidak perlu memeriksa&& item !== null
di akhir, karena garis dimulai denganitem &&
, bukan?mergedDeep
output (saya pikir). Misalnya,const target = { a: 1 }; const source = { b: { c: 2 } }; const merged = mergeDeep(target, source);
merged.b.c; // 2
source.b.c = 3;
merged.b.c; // 3
apakah ini masalah? Itu tidak bermutasi input, tetapi mutasi ke input di masa depan dapat memutasi output, dan sebaliknya w / mutasi ke output input bermutasi. Namun, untuk apa nilainya, ramdaR.merge()
memiliki perilaku yang sama.Karena masalah ini masih aktif, inilah pendekatan lain:
sumber
prev[key] = pVal.concat(...oVal);
keprev[key] = [...pVal, ...oVal].filter((element, index, array) => array.indexOf(element) === index);
reduce
tidak.Saya tahu sudah ada banyak jawaban dan banyak komentar yang berpendapat itu tidak akan berhasil. Satu-satunya konsensus adalah bahwa itu sangat rumit sehingga tidak ada yang membuat standar untuk itu . Namun, sebagian besar jawaban yang diterima di SO mengekspos "trik sederhana" yang banyak digunakan. Jadi, bagi kita semua seperti saya yang bukan ahli tetapi ingin menulis kode yang lebih aman dengan memahami sedikit lebih banyak tentang kompleksitas javascript, saya akan mencoba menjelaskan.
Sebelum membuat tangan kita kotor, izinkan saya mengklarifikasi 2 poin:
Object.assign
halnya.Jawaban dengan
for..in
atauObject.keys
menyesatkanMembuat salinan yang dalam tampaknya praktik yang sangat mendasar dan umum sehingga kami berharap dapat menemukan satu garis atau, paling tidak, kemenangan cepat melalui rekursi sederhana. Kami tidak berharap kami perlu perpustakaan atau menulis fungsi khusus 100 baris.
Ketika saya pertama kali membaca jawaban Salakar , saya benar-benar berpikir saya bisa melakukan yang lebih baik dan lebih sederhana (Anda dapat membandingkannya dengan
Object.assign
dix={a:1}, y={a:{b:1}}
). Kemudian saya membaca jawaban the8472 dan saya pikir ... tidak ada jalan keluar dengan mudah, meningkatkan jawaban yang sudah diberikan tidak akan membuat kita jauh.Mari kita salin yang dalam dan mengesampingkan rekursif sesaat. Perhatikan saja (salah) orang mengurai properti untuk menyalin objek yang sangat sederhana.
Object.keys
akan menghilangkan properti non-enumerable sendiri, memiliki properti key-keyed dan semua properti prototipe. Ini mungkin baik-baik saja jika objek Anda tidak memilikinya. Tetapi perlu diingat bahwaObject.assign
menangani properti enumerable key-keyed sendiri. Jadi salinan kustom Anda hilang mekar.for..in
akan memberikan properti dari sumber, prototipe dan rantai prototipe lengkap tanpa Anda inginkan (atau menyadarinya). Target Anda mungkin berakhir dengan terlalu banyak properti, mencampur properti prototipe dan properti sendiri.Jika Anda sedang menulis fungsi tujuan umum dan Anda tidak menggunakan
Object.getOwnPropertyDescriptors
,Object.getOwnPropertyNames
,Object.getOwnPropertySymbols
atauObject.getPrototypeOf
, Anda yang paling mungkin melakukannya salah.Hal yang perlu dipertimbangkan sebelum menulis fungsi Anda
Pertama, pastikan Anda memahami apa itu objek Javascript. Dalam Javascript, sebuah objek dibuat dari propertinya sendiri dan objek prototipe (induk). Objek prototipe pada gilirannya terbuat dari propertinya sendiri dan objek prototipe. Dan seterusnya, mendefinisikan rantai prototipe.
Properti adalah sepasang kunci (
string
atausymbol
) dan deskriptor (value
atauget
/set
accessor, dan atribut sepertienumerable
).Akhirnya, ada banyak jenis objek . Anda mungkin ingin menangani objek objek secara berbeda dari objek Date atau objek Function.
Jadi, menulis salinan Anda yang dalam, Anda harus menjawab setidaknya pertanyaan-pertanyaan itu:
Sebagai contoh saya, saya menganggap bahwa hanya
object Object
s yang dalam , karena objek lain yang dibuat oleh konstruktor lain mungkin tidak layak untuk tampilan yang mendalam. Disesuaikan dari SO ini .Dan saya membuat
options
objek untuk memilih apa yang akan disalin (untuk tujuan demo).Fungsi yang diusulkan
Anda dapat mengujinya di plunker ini .
Itu bisa digunakan seperti ini:
sumber
Saya menggunakan lodash:
sumber
_cloneDeep(value1).merge(value2)
Berikut ini adalah implementasi TypeScript:
Dan Unit Tes:
sumber
Berikut ini adalah solusi ES6 lainnya, yang bekerja dengan objek dan array.
sumber
Paket deeppmge npm tampaknya menjadi pustaka yang paling banyak digunakan untuk memecahkan masalah ini: https://www.npmjs.com/package/deepmerge
sumber
Saya ingin menyajikan alternatif ES5 yang cukup sederhana. Fungsi mendapat 2 parameter -
target
dansource
itu harus bertipe "objek".Target
akan menjadi objek yang dihasilkan.Target
menyimpan semua properti aslinya tetapi nilainya dapat dimodifikasi.kasus:
target
tidak punyasource
properti,target
dapatkan;target
memang memilikisource
properti dantarget
&source
bukan keduanya objek (3 kasus dari 4),target
properti akan ditimpa;target
memang memilikisource
properti dan keduanya adalah objek / array (1 kasing sisa), maka rekursi terjadi menggabungkan dua objek (atau gabungan dua array);pertimbangkan juga hal berikut :
Dapat diprediksi, mendukung tipe primitif serta array dan objek. Juga karena kita dapat menggabungkan 2 objek, saya pikir kita dapat menggabungkan lebih dari 2 melalui fungsi pengurangan .
lihat sebuah contoh (dan mainkan dengan itu jika Anda mau) :
Ada batasan - panjang tumpukan panggilan browser. Peramban modern akan membuat kesalahan pada tingkat rekursi yang sangat dalam (pikirkan ribuan panggilan bersarang). Anda juga bebas untuk memperlakukan situasi seperti array + objek dll sesuai keinginan dengan menambahkan kondisi baru dan ketik cek.
sumber
Jika Anda menggunakan ImmutableJS, Anda dapat menggunakan
mergeDeep
:sumber
Jika pustaka npm dapat digunakan sebagai solusi, object-merge-advanced dari milik Anda benar-benar memungkinkan untuk menggabungkan objek secara mendalam dan menyesuaikan / menimpa setiap aksi penggabungan menggunakan fungsi panggilan balik yang umum. Gagasan utama lebih dari sekadar penggabungan dalam - apa yang terjadi dengan nilai ketika dua kunci sama ? Pustaka ini menangani hal itu - ketika dua kunci berbenturan,
object-merge-advanced
menimbang jenisnya, bertujuan untuk menyimpan data sebanyak mungkin setelah penggabungan:Kunci argumen input pertama ditandai # 1, argumen kedua - # 2. Bergantung pada masing-masing jenis, satu dipilih untuk nilai kunci hasil. Dalam diagram, "suatu objek" berarti objek sederhana (bukan array, dll).
Ketika kunci tidak berbenturan, mereka semua memasukkan hasilnya.
Dari cuplikan contoh Anda, jika Anda biasa
object-merge-advanced
menggabungkan cuplikan kode Anda:Algoritme ini secara rekursif menelusuri semua kunci objek input, membandingkan dan membangun serta mengembalikan hasil yang digabungkan.
sumber
Fungsi berikut membuat salinan objek yang dalam, mencakup penyalinan primitif, array serta objek
sumber
Solusi sederhana dengan ES5 (menimpa nilai yang ada):
sumber
Sebagian besar contoh di sini tampaknya terlalu kompleks, saya menggunakan satu di TypeScript yang saya buat, saya pikir itu harus mencakup sebagian besar kasus (saya menangani array sebagai data biasa, hanya menggantinya).
Hal yang sama di JS polos, untuk jaga-jaga:
Berikut ini adalah contoh pengujian saya untuk menunjukkan bagaimana Anda dapat menggunakannya
Tolong beri tahu saya jika Anda pikir saya kehilangan beberapa fungsi.
sumber
Jika Anda ingin memiliki satu liner tanpa memerlukan perpustakaan besar seperti lodash, saya sarankan Anda menggunakan deepmerge . (
npm install deepmerge
)Lalu, Anda bisa melakukannya
mendapatkan
Yang menyenangkan adalah ia datang dengan mengetik untuk TypeScript segera. Ini juga memungkinkan untuk menggabungkan array . Solusi serba nyata ini.
sumber
Kita bisa menggunakan $ .extend (true, object1, object2) untuk penggabungan yang dalam. Value true menunjukkan menggabungkan dua objek secara rekursif, memodifikasi yang pertama.
$ extended (true, target, object)
sumber
jQuery.isPlainObject()
. Itu memperlihatkan kompleksitas menentukan apakah sesuatu itu objek biasa atau tidak, yang sebagian besar jawaban di sini terlewatkan begitu saja. Tebak bahasa apa jQuery ditulis?Di sini lurus ke depan, solusi sederhana yang berfungsi seperti
Object.assign
hanya deeep, dan bekerja untuk sebuah array, tanpa modifikasi apa punContoh
sumber
Saya mengalami masalah ini saat memuat kondisi redux yang di-cache. Jika saya hanya memuat keadaan cache, saya akan mengalami kesalahan untuk versi aplikasi baru dengan struktur keadaan yang diperbarui.
Sudah disebutkan, bahwa lodash menawarkan
merge
fungsi, yang saya gunakan:sumber
Banyak jawaban menggunakan puluhan baris kode, atau perlu menambahkan perpustakaan baru ke proyek, tetapi jika Anda menggunakan rekursi, ini hanya 4 baris kode.
Penanganan array: Versi di atas menimpa nilai array lama dengan yang baru. Jika Anda ingin menyimpan nilai array lama dan menambahkan yang baru, cukup tambahkan
else if (current[key] instanceof Array && updates[key] instanceof Array) current[key] = current[key].concat(updates[key])
blok di ataselse
statament dan Anda siap.sumber
Ini satu lagi yang baru saja saya tulis yang mendukung array. Itu menyadarkan mereka.
sumber
Gunakan fungsi ini:
sumber
Ramda yang merupakan pustaka fungsi javascript yang bagus telah mergeDeepLeft dan mergeDeepRight. Semua ini bekerja dengan baik untuk masalah ini. Silakan lihat dokumentasi di sini: https://ramdajs.com/docs/#mergeDeepLeft
Untuk contoh spesifik yang dimaksud, kita dapat menggunakan:
sumber
Tes unit:
sumber
Saya hanya menemukan solusi 2 baris untuk mendapatkan penggabungan dalam javascript. Biarkan saya tahu cara kerjanya bagi Anda.
Objek temp akan mencetak {a: {b: 'd', e: 'f', x: 'y'}}
sumber
merge({x:{y:{z:1}}}, {x:{y:{w:2}}})
. Il juga akan gagal memperbarui nilai yang ada di obj1 jika obj2 memilikinya juga, misalnya denganmerge({x:{y:1}}, {x:{y:2}})
.Kadang-kadang Anda tidak perlu penggabungan dalam, bahkan jika Anda berpikir demikian. Sebagai contoh, jika Anda memiliki konfigurasi default dengan objek bersarang dan Anda ingin memperluasnya dengan konfigurasi Anda sendiri, Anda bisa membuat kelas untuk itu. Konsepnya sangat sederhana:
Anda dapat mengonversinya menjadi fungsi (bukan konstruktor).
sumber
Ini adalah penggabungan murah yang menggunakan kode sesedikit yang saya bisa pikirkan. Setiap sumber menimpa properti sebelumnya ketika ada.
sumber
Saya menggunakan fungsi pendek berikut untuk objek penggabungan dalam.
Ini sangat bagus untuk saya.
Penulis sepenuhnya menjelaskan cara kerjanya di sini.
sumber