Apa cara paling efisien untuk mengelompokkan objek dalam array?
Misalnya, diberikan array objek ini:
[
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
]
Saya menampilkan informasi ini dalam sebuah tabel. Saya ingin mengelompokkan berdasarkan metode yang berbeda, tetapi saya ingin menjumlahkan nilainya.
Saya menggunakan Underscore.js untuk fungsi groupby, yang membantu, tetapi tidak melakukan seluruh trik, karena saya tidak ingin mereka "berpisah" tetapi "digabung", lebih seperti group by
metode SQL .
Apa yang saya cari akan dapat total nilai spesifik (jika diminta).
Jadi jika saya melakukan groupby Phase
, saya ingin menerima:
[
{ Phase: "Phase 1", Value: 50 },
{ Phase: "Phase 2", Value: 130 }
]
Dan jika saya melakukan groupy Phase
/ Step
, saya akan menerima:
[
{ Phase: "Phase 1", Step: "Step 1", Value: 15 },
{ Phase: "Phase 1", Step: "Step 2", Value: 35 },
{ Phase: "Phase 2", Step: "Step 1", Value: 55 },
{ Phase: "Phase 2", Step: "Step 2", Value: 75 }
]
Apakah ada skrip yang membantu untuk ini, atau haruskah saya tetap menggunakan Underscore.js, dan kemudian mengulangi objek yang dihasilkan untuk melakukan total sendiri?
var groupBy = function<TItem>(xs: TItem[], key: string) : {[key: string]: TItem[]} { ...
Menggunakan objek Peta ES6:
Tentang Peta: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
sumber
get()
metode? yang saya ingin output ditampilkan tanpa melewati tombolconsole.log(grouped.entries());
dalam contoh jsfiddle ia mengembalikan iterable yang berperilaku seperti array kunci + nilai. Bisakah Anda mencobanya dan melihat apakah itu membantu?console.log(Array.from(grouped));
Array.from(groupBy(jsonObj, item => i.type)).map(i => ( {[i[0]]: i[1].length} ))
dengan ES6:
sumber
...result
. Sekarang saya tidak bisa tidur karena itu.Meskipun jawaban LINQ menarik, itu juga cukup berat. Pendekatan saya agak berbeda:
Anda dapat melihatnya beraksi di JSBin .
Saya tidak melihat apa pun di Underscore yang melakukan apa
has
, meskipun saya mungkin melewatkannya. Ini hampir sama dengan_.contains
, tetapi menggunakan_.isEqual
daripada===
untuk perbandingan. Selain itu, sisanya adalah masalah khusus, meskipun dengan upaya untuk menjadi generik.Sekarang
DataGrouper.sum(data, ["Phase"])
kembaliDan
DataGrouper.sum(data, ["Phase", "Step"])
kembaliTetapi
sum
hanya satu fungsi potensial di sini. Anda dapat mendaftarkan orang lain sesuka Anda:dan sekarang
DataGrouper.max(data, ["Phase", "Step"])
akan kembaliatau jika Anda mendaftarkan ini:
maka menelepon
DataGrouper.tasks(data, ["Phase", "Step"])
akan membuat AndaDataGrouper
itu sendiri adalah suatu fungsi. Anda dapat menyebutnya dengan data Anda dan daftar properti yang ingin Anda kelompokkan. Ini mengembalikan array yang elemennya adalah objek dengan dua properti:key
adalah kumpulan properti yang dikelompokkan,vals
adalah array objek yang berisi properti yang tersisa yang tidak ada dalam kunci. Sebagai contoh,DataGrouper(data, ["Phase", "Step"])
akan menghasilkan:DataGrouper.register
menerima fungsi dan membuat fungsi baru yang menerima data awal dan properti untuk dikelompokkan berdasarkan. Fungsi baru ini kemudian mengambil format output seperti di atas dan menjalankan fungsi Anda terhadap masing-masing, mengembalikan array baru. Fungsi yang dihasilkan disimpan sebagai propertiDataGrouper
sesuai dengan nama yang Anda berikan dan juga dikembalikan jika Anda hanya ingin referensi lokal.Nah itu banyak penjelasan. Kode ini cukup mudah, saya harap!
sumber
Saya akan memeriksa grup lodash oleh . Sepertinya ini melakukan persis apa yang Anda cari. Ini juga sangat ringan dan sangat sederhana.
Contoh biola: https://jsfiddle.net/r7szvt5k/
Asalkan nama array Anda adalah
arr
groupBy dengan lodash hanya:sumber
Ini mungkin lebih mudah dilakukan dengan
linq.js
, yang dimaksudkan sebagai implementasi sebenarnya dari LINQ dalam JavaScript ( DEMO ):hasil:
Atau, lebih sederhana menggunakan penyeleksi berbasis string ( DEMO ):
sumber
GroupBy(function(x){ return x.Phase; })
Anda dapat membuat ES6
Map
dariarray.reduce()
.Ini memiliki beberapa keunggulan dibandingkan solusi lain:
_.groupBy()
)Map
daripada objek (mis. Dikembalikan oleh_.groupBy()
). Ini memiliki banyak manfaat , termasuk:Map
adalah hasil yang lebih berguna daripada array. Tetapi jika Anda menginginkan array array, Anda dapat memanggilArray.from(groupedMap.entries())
(untuk array[key, group array]
berpasangan) atauArray.from(groupedMap.values())
(untuk array array sederhana).Sebagai contoh dari poin terakhir, bayangkan saya memiliki array objek yang ingin saya gabungkan (dangkal) oleh id, seperti ini:
Untuk melakukan ini, saya biasanya memulai dengan mengelompokkan berdasarkan id, dan kemudian menggabungkan setiap array yang dihasilkan. Sebagai gantinya, Anda bisa melakukan penggabungan langsung di
reduce()
:sumber
a.reduce(function(em, e){em.set(e.id, (em.get(e.id)||[]).concat([e]));return em;}, new Map())
, kurang-lebih)Dari: http://underscorejs.org/#groupBy
sumber
Anda dapat melakukannya dengan pustaka JavaScript Alasql :
Coba contoh ini di jsFiddle .
BTW: Pada array besar (100000 catatan dan lebih banyak lagi) Alasql lebih cepat daripada Linq. Lihat tes di jsPref .
Komentar:
sumber
sumber
MDN memiliki contoh ini dalam
Array.reduce()
dokumentasi mereka .sumber
Meskipun pertanyaan memiliki beberapa jawaban dan jawaban terlihat sedikit lebih rumit, saya sarankan untuk menggunakan Javascript vanilla untuk grup-by dengan bersarang (jika perlu)
Map
.sumber
Tanpa mutasi:
sumber
Solusi ini mengambil fungsi sembarang (bukan kunci) sehingga lebih fleksibel daripada solusi di atas, dan memungkinkan fungsi panah , yang mirip dengan ekspresi lambda yang digunakan dalam LINQ :
CATATAN: apakah Anda ingin memperpanjang
Array
prototipe terserah Anda.Contoh yang didukung di sebagian besar browser:
Contoh menggunakan fungsi panah (ES6):
Kedua contoh di atas kembali:
sumber
let key = 'myKey'; let newGroupedArray = myArrayOfObjects.reduce(function (acc, val) { (acc[val[key]] = acc[val[key]] || []).push(val); return acc;});
Saya ingin menyarankan pendekatan saya. Pertama, pengelompokan dan agregasi terpisah. Mari kita mendeklarasikan fungsi prototipe "grup demi". Dibutuhkan fungsi lain untuk menghasilkan string "hash" untuk setiap elemen array untuk dikelompokkan.
ketika pengelompokan selesai Anda dapat mengumpulkan data berapa yang Anda butuhkan, dalam kasus Anda
secara umum itu bekerja. saya telah menguji kode ini di konsol chrome. dan merasa bebas untuk meningkatkan dan menemukan kesalahan;)
sumber
map[_hash(key)].key = key;
menjadimap[_hash(key)].key = _hash(key);
.Bayangkan Anda memiliki sesuatu seperti ini:
[{id:1, cat:'sedan'},{id:2, cat:'sport'},{id:3, cat:'sport'},{id:4, cat:'sedan'}]
Dengan melakukan ini:
const categories = [...new Set(cars.map((car) => car.cat))]
Anda akan mendapatkan ini:
['sedan','sport']
Penjelasan: 1. Pertama, kami membuat Set baru dengan melewatkan array. Karena Atur hanya memungkinkan nilai unik, semua duplikat akan dihapus.
Setel Dokumen: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set Spread OperatorDoc: https://developer.mozilla.org/en-US/docs/Web/JavaScript / Referensi / Operator / Spread_syntax
sumber
Jawaban yang diperiksa - hanya pengelompokan yang dangkal. Sangat bagus untuk memahami pengurangan. Pertanyaan juga memberikan masalah perhitungan agregat tambahan.
Berikut ini adalah REAL GROUP BY untuk Array of Objects oleh beberapa bidang dengan 1) nama kunci dihitung dan 2) solusi lengkap untuk cascading pengelompokan dengan memberikan daftar kunci yang diinginkan dan mengonversi nilai-nilai uniknya ke kunci root seperti SQL GROUP BY tidak.
Bermain dengan
estKey
- Anda dapat mengelompokkan lebih dari satu bidang, menambahkan agregasi tambahan, perhitungan, atau pemrosesan lainnya.Anda juga dapat mengelompokkan data secara rekursif. Misalnya awalnya dikelompokkan berdasarkan
Phase
, lalu olehStep
bidang dan sebagainya. Selain itu meniupkan data sisa lemak.sumber
Output array yang satu ini.
sumber
Berdasarkan jawaban sebelumnya
dan itu sedikit lebih baik untuk melihat dengan sintaks penyebaran objek, jika lingkungan Anda mendukung.
Di sini, peredam kami mengambil nilai pengembalian yang dibentuk sebagian (dimulai dengan objek kosong), dan mengembalikan objek yang terdiri dari anggota yang tersebar dari nilai pengembalian sebelumnya, bersama dengan anggota baru yang kuncinya dihitung dari nilai iteree saat ini di
prop
dan yang nilainya adalah daftar semua nilai untuk properti itu bersama dengan nilai saat ini.sumber
sumber
Inilah solusi yang sulit dan sulit dibaca menggunakan ES6:
Bagi mereka bertanya bagaimana ini bahkan bekerja, inilah penjelasan:
Dalam keduanya
=>
Anda memiliki gratisreturn
The
Array.prototype.reduce
fungsi memakan waktu sampai 4 parameter. Itu sebabnya parameter kelima ditambahkan sehingga kita dapat memiliki deklarasi variabel murah untuk grup (k) di tingkat deklarasi parameter dengan menggunakan nilai default. (ya, ini sihir)Jika grup kami saat ini tidak ada pada iterasi sebelumnya, kami membuat array kosong baru
((r[k] || (r[k] = []))
Ini akan mengevaluasi ekspresi paling kiri, dengan kata lain, array yang ada atau array kosong , ini sebabnya ada segerapush
setelah ekspresi itu, karena bagaimanapun Anda akan mendapatkan sebuah array.Ketika ada
return
,,
operator koma akan membuang nilai paling kiri, mengembalikan grup sebelumnya yang telah diubah untuk skenario ini.Versi yang lebih mudah dipahami yang melakukan hal yang sama adalah:
sumber
Mari kita menghasilkan
Array.prototype.groupBy()
alat generik . Hanya untuk variasi, mari gunakan ES6 fanciness operator spread untuk beberapa pola Haskellesque yang cocok dengan pendekatan rekursif. Juga mari kita buat kitaArray.prototype.groupBy()
untuk menerima panggilan balik yang mengambil item (e
) indeks (i
) dan array yang diterapkan (a
) sebagai argumen.sumber
Jawaban Ceasar baik, tetapi hanya berfungsi untuk properti bagian dalam elemen di dalam array (panjang untuk string).
implementasi ini lebih berfungsi seperti: tautan ini
semoga ini membantu...
sumber
Dari @mortb, @jmarceli menjawab dan dari pos ini ,
Saya mengambil keuntungan dari
JSON.stringify()
menjadi identitas untuk NILAI PRIMITIF beberapa kolom grup oleh.Tanpa pihak ketiga
Dengan Lodash pihak ketiga
sumber
reduce
Versi versi berbasis ES6 dengan dukungan fungsiiteratee
.Bekerja seperti yang diharapkan jika
iteratee
fungsi tidak disediakan:Dalam konteks pertanyaan OP:
Versi ES6 lain yang membalikkan pengelompokan dan menggunakan
values
askeys
dan thekeys
sebagaigrouped values
:Catatan: fungsi diposting dalam bentuk singkat (satu baris) untuk singkatnya dan hanya untuk menghubungkan ide. Anda dapat mengembangkannya dan menambahkan pemeriksaan kesalahan tambahan, dll.
sumber
Saya akan memeriksa deklaratif-js
groupBy
tampaknya melakukan persis apa yang Anda cari. Itu juga:sumber
sumber
Ini adalah versi ES6 yang tidak akan merusak anggota null
sumber
Hanya untuk menambah jawaban Scott Sauyet , beberapa orang bertanya di komentar bagaimana menggunakan fungsinya untuk mengelompokkan nilai1, nilai2, dll., Alih-alih mengelompokkan hanya satu nilai.
Yang diperlukan hanyalah mengedit fungsi penjumlahannya:
membiarkan yang utama (DataGrouper) tidak berubah:
sumber
Dengan fitur sortir
Sampel:
sumber