Bagaimana Anda akan mengimplementasikan produk Cartesian dari beberapa larik di JavaScript?
Sebagai contoh,
cartesian([1, 2], [10, 20], [100, 200, 300])
harus kembali
[
[1, 10, 100],
[1, 10, 200],
[1, 10, 300],
[2, 10, 100],
[2, 10, 200]
...
]
d3.cross(a, b[, reducer])
pada bulan Februari. github.com/d3/d3-array#crossJawaban:
Pembaruan 2017: Jawaban 2 baris dengan vanilla JS
Semua jawaban di sini terlalu rumit , kebanyakan mengambil 20 baris kode atau bahkan lebih.
Contoh ini hanya menggunakan dua baris vanilla JavaScript , tanpa lodash, garis bawah, atau pustaka lainnya:
Memperbarui:
Ini sama seperti di atas tetapi ditingkatkan untuk mengikuti Panduan Gaya JavaScript Airbnb dengan ketat - divalidasi menggunakan ESLint dengan eslint-config-airbnb-base :
Terima kasih khusus kepada ZuBB karena telah memberi tahu saya tentang masalah linter dengan kode asli.
Contoh
Ini adalah contoh tepat dari pertanyaan Anda:
Keluaran
Ini adalah keluaran dari perintah itu:
Demo
Lihat demo di:
Sintaksis
Sintaks yang saya gunakan di sini bukanlah hal baru. Contoh saya menggunakan operator penyebaran dan parameter lainnya - fitur JavaScript yang ditentukan dalam standar ECMA-262 edisi ke-6 yang diterbitkan pada Juni 2015 dan dikembangkan jauh lebih awal, lebih dikenal sebagai ES6 atau ES2015. Lihat:
Itu membuat kode seperti ini begitu sederhana sehingga dosa untuk tidak menggunakannya. Untuk platform lama yang tidak mendukungnya secara native, Anda selalu dapat menggunakan Babel atau alat lain untuk mentranspilasinya ke sintaks yang lebih lama - dan sebenarnya contoh saya yang ditranspilasi oleh Babel masih lebih pendek dan sederhana daripada kebanyakan contoh di sini, tetapi tidak sangat penting karena keluaran transpilasi bukanlah sesuatu yang perlu Anda pahami atau pertahankan, itu hanya fakta yang menurut saya menarik.
Kesimpulan
Tidak perlu menulis ratusan baris kode yang sulit dipelihara dan tidak perlu menggunakan seluruh pustaka untuk hal yang sesederhana itu, ketika dua baris vanilla JavaScript dapat dengan mudah menyelesaikan pekerjaan. Seperti yang Anda lihat, sangat bermanfaat untuk menggunakan fitur-fitur modern dari bahasa tersebut dan dalam kasus di mana Anda perlu mendukung platform kuno tanpa dukungan asli dari fitur-fitur modern, Anda selalu dapat menggunakan Babel atau alat lain untuk memindahkan sintaks baru ke yang lama. .
Jangan membuat kode seperti tahun 1995
JavaScript berkembang dan melakukannya karena suatu alasan. TC39 melakukan pekerjaan luar biasa dalam desain bahasa dengan menambahkan fitur baru dan vendor browser melakukan pekerjaan luar biasa dalam mengimplementasikan fitur tersebut.
Untuk melihat status dukungan asli saat ini dari setiap fitur yang diberikan di browser, lihat:
Untuk melihat dukungan dalam versi Node, lihat:
Untuk menggunakan sintaks modern pada platform yang tidak mendukungnya secara native, gunakan Babel:
sumber
a
b
['a', 'b'], [1,2], [[9], [10]]
yang akan menghasilkan[ [ 'a', 1, 9 ], [ 'a', 1, 10 ], [ 'a', 2, 9 ], [ 'a', 2, 10 ], [ 'b', 1, 9 ], [ 'b', 1, 10 ], [ 'b', 2, 9 ], [ 'b', 2, 10 ] ]
sebagai hasilnya. Maksud saya tidak akan menyimpan jenis barang[[9], [10]]
....
, bukankah seharusnya[].concat(...[array])
menjadi sederhana[...array]
?Berikut adalah solusi fungsional untuk masalah (tanpa variabel yang bisa berubah !) Menggunakan
reduce
danflatten
, disediakan olehunderscore.js
:Catatan: Solusi ini terinspirasi oleh http://cwestblog.com/2011/05/02/cartesian-product-of-multiple-arrays/
sumber
flatten
adalah membuat perataan dangkal. Di sini wajib!true
dengan garis bawah dan gunakanfalse
dengan lodash untuk memastikan perataan dangkal.Berikut adalah versi modifikasi dari kode @ viebel dalam Javascript biasa, tanpa menggunakan pustaka apa pun:
sumber
.concat(y)
alih-alih.concat([ y ])
Sepertinya komunitas menganggap ini sepele dan atau mudah untuk menemukan implementasi referensi, setelah pemeriksaan singkat saya tidak bisa atau mungkin hanya saja saya suka menciptakan kembali roda atau menyelesaikan masalah pemrograman seperti kelas baik itu hari keberuntungan Anda :
implementasi referensi lengkap yang relatif efisien ... :-D
pada efisiensi: Anda bisa mendapatkan beberapa dengan mengeluarkan if dari loop dan memiliki 2 loop terpisah karena secara teknis konstan dan Anda akan membantu prediksi cabang dan semua kekacauan itu, tetapi poin itu semacam diperdebatkan di javascript
siapa saja, selamat menikmati -ck
sumber
reduce
fungsi array?result = result.concat(...)
dan dengan tidak menggunakanargs.slice(1)
. Sayangnya, saya tidak dapat menemukan cara untuk menyingkirkancurr.slice()
dan rekursi tersebut.Fungsi generator efisien berikut mengembalikan produk kartesius dari semua iterabel yang diberikan :
Ini menerima array, string, set, dan semua objek lain yang mengimplementasikan protokol iterable .
Mengikuti spesifikasi produk kartesian n-ary yang dihasilkannya
[]
jika satu atau lebih iterable yang diberikan kosong, misalnya[]
atau''
[[a]]
jika satu iterable yang berisi satu nilaia
diberikan.Semua kasus lainnya ditangani seperti yang diharapkan seperti yang ditunjukkan oleh kasus uji berikut:
Tampilkan cuplikan kode
sumber
function* cartesian(head, ...tail) { for (let h of head) { const remainder = tail.length > 0 ? cartesian(...tail) : [[]]; for (let r of remainder) yield [h, ...r] } }
Berikut adalah solusi rekursif langsung yang tidak mewah:
sumber
Berikut adalah cara rekursif yang menggunakan fungsi generator ECMAScript 2015 sehingga Anda tidak perlu membuat semua tupel sekaligus:
sumber
cartesian([[1],[2]],[10,20],[100,200,300])
.concat()
operator penyebaran bawaan terkadang mungkin menjadi licik.Berikut ini satu baris yang menggunakan ES2019 asli
flatMap
. Tidak perlu perpustakaan, cukup browser modern (atau transpiler):Ini pada dasarnya adalah versi modern dari jawaban viebel, tanpa lodash.
sumber
Menggunakan backtracking biasa dengan generator ES6,
Di bawah ini ada versi serupa yang kompatibel dengan browser lama.
Tampilkan cuplikan kode
sumber
Ini adalah solusi ES6 murni menggunakan fungsi panah
sumber
Versi coffeescript dengan lodash:
sumber
Pendekatan satu baris, untuk membaca lebih baik dengan lekukan.
Dibutuhkan satu larik dengan larik item kartesius yang diinginkan.
sumber
if (arr.length === 1) return arr[0].map(el => [el]);
Ini diberi tag pemrograman fungsional jadi mari kita lihat Monad List :
Nah itu terdengar seperti yang sempurna cocok untuk
cartesian
. JavaScript memberi kitaArray
dan fungsi pengikatan monadik adalahArray.prototype.flatMap
, jadi mari kita gunakan -Alih-alih di
loop
atas,t
dapat ditambahkan sebagai parameter kari -sumber
Beberapa jawaban di bawah topik ini gagal ketika salah satu larik input berisi item larik. Sebaiknya kau periksa itu.
Pokoknya tidak perlu garis bawah, lodash apa saja. Saya yakin yang satu ini harus melakukannya dengan JS ES6 murni, sefungsional yang didapatnya.
Potongan kode ini menggunakan peta yang dikurangi dan bersarang, hanya untuk mendapatkan produk kartesian dari dua larik, namun larik kedua berasal dari panggilan rekursif ke fungsi yang sama dengan satu larik yang lebih sedikit; karenanya..
a[0].cartesian(...a.slice(1))
sumber
Dalam pengaturan khusus saya, pendekatan "kuno" tampaknya lebih efisien daripada metode yang didasarkan pada fitur yang lebih modern. Di bawah ini adalah kode (termasuk perbandingan kecil dengan solusi lain yang diposting di utas ini oleh @rsp dan @sebnukem) seandainya itu terbukti berguna bagi orang lain juga.
Idenya mengikuti. Katakanlah kita sedang membangun produk luar dari
N
array, yanga_1,...,a_N
masing-masing memilikim_i
komponen. Produk luar dari array ini memilikiM=m_1*m_2*...*m_N
elemen dan kita dapat mengidentifikasi masing-masing denganN-
vektor dimensi yang komponennya adalah bilangan bulat positif dani
komponen -th dibatasi secara ketat dari atasm_i
. Misalnya, vektor(0, 0, ..., 0)
akan sesuai dengan kombinasi tertentu di mana seseorang mengambil elemen pertama dari setiap larik, sedangkan(m_1-1, m_2-1, ..., m_N-1)
diidentifikasikan dengan kombinasi di mana seseorang mengambil elemen terakhir dari setiap larik. Jadi untuk membangun semuaM
kombinasi, fungsi di bawah ini secara berurutan menyusun semua vektor tersebut dan untuk masing-masing vektor tersebut mengidentifikasi kombinasi yang sesuai dari elemen array input.dengan
node v6.12.2
, saya mendapatkan pengaturan waktu berikut:sumber
Bagi mereka yang membutuhkan TypeScript (reimplemented @ Danny jawaban)
sumber
Hanya untuk pilihan, implementasi sederhana yang nyata menggunakan array
reduce
:sumber
JavaScript modern hanya dalam beberapa baris. Tidak ada perpustakaan atau dependensi eksternal seperti Lodash.
sumber
Anda bisa
reduce
membuat array 2D. GunakanflatMap
pada array akumulator untuk mendapatkanacc.length x curr.length
jumlah kombinasi di setiap loop.[].concat(c, n)
digunakan karenac
adalah angka pada iterasi pertama dan array sesudahnya.(Ini berdasarkan jawaban Nina Scholz )
sumber
Pendekatan non-rekursif yang menambahkan kemampuan untuk memfilter dan memodifikasi produk sebelum benar-benar menambahkannya ke kumpulan hasil. Perhatikan penggunaan .map daripada .forEach. Di beberapa browser, .map berjalan lebih cepat.
sumber
Solusi sederhana "pikiran dan visual ramah".
sumber
Versi kode @ viebel yang sederhana dan dimodifikasi dalam Javascript biasa:
sumber
Implementasi yang lebih mudah dibaca
sumber
Ini untuk 3 larik.
Beberapa jawaban memberi jalan untuk sejumlah array.
Ini dapat dengan mudah berkontraksi atau berkembang menjadi lebih sedikit atau lebih array.
Saya membutuhkan kombinasi satu set dengan pengulangan, jadi saya bisa menggunakan:
tapi digunakan:
sumber
Saya perhatikan bahwa tidak ada yang memposting solusi yang memungkinkan suatu fungsi diteruskan untuk memproses setiap kombinasi, jadi inilah solusi saya:
Keluaran:
sumber
Pendekatan brute force JS biasa yang menggunakan array array sebagai input.
sumber
Baru saja mengubah jawaban @ dummersl dari CoffeScript ke JavaScript. Ini berhasil.
sumber
Implementasi lain. Bukan yang terpendek atau mewah, tapi cepat:
sumber
Tidak perlu perpustakaan! :)
Membutuhkan fungsi panah dan mungkin tidak seefisien itu. : /
sumber
Untuk catatan
Ini dia versi saya. Saya membuatnya menggunakan iterator javascript paling sederhana "for ()", jadi kompatibel pada setiap kasus dan memiliki kinerja terbaik.
Salam Hormat.
sumber