val map1 = Map(1 -> 9 , 2 -> 20)
val map2 = Map(1 -> 100, 3 -> 300)
Saya ingin menggabungkan mereka, dan menjumlahkan nilai-nilai kunci yang sama. Jadi hasilnya adalah:
Map(2->20, 1->109, 3->300)
Sekarang saya punya 2 solusi:
val list = map1.toList ++ map2.toList
val merged = list.groupBy ( _._1) .map { case (k,v) => k -> v.map(_._2).sum }
dan
val merged = (map1 /: map2) { case (map, (k,v)) =>
map + ( k -> (v + map.getOrElse(k, 0)) )
}
Tapi saya ingin tahu apakah ada solusi yang lebih baik.
map1 ++ map2
Jawaban:
Scalaz memiliki konsep Semigroup yang menangkap apa yang ingin Anda lakukan di sini, dan mengarah pada solusi terpendek / terbersih:
Secara khusus, operator biner untuk
Map[K, V]
menggabungkan kunci peta, melipatV
operator semigroup atas nilai duplikat apa pun. Semigroup standar untukInt
menggunakan operator tambahan, sehingga Anda mendapatkan jumlah nilai untuk setiap kunci duplikat.Sunting : Sedikit lebih detail, sesuai permintaan pengguna482745.
Secara matematis sebuah semi -grup hanyalah satu set nilai, bersama dengan operator yang mengambil dua nilai dari set itu, dan menghasilkan nilai lain dari set itu. Jadi bilangan bulat yang ditambahkan adalah semigroup, misalnya -
+
operator menggabungkan dua int untuk membuat int lainnya.Anda juga dapat mendefinisikan sebuah semi grup dari set "semua peta dengan tipe kunci dan tipe nilai yang diberikan", asalkan Anda dapat membuat beberapa operasi yang menggabungkan dua peta untuk menghasilkan yang baru yang entah bagaimana merupakan kombinasi dari keduanya. input.
Jika tidak ada kunci yang muncul di kedua peta, ini sepele. Jika kunci yang sama ada di kedua peta, maka kita perlu menggabungkan dua nilai yang dipetakan oleh kunci tersebut. Hmm, bukankah kita baru saja menggambarkan operator yang menggabungkan dua entitas dari jenis yang sama? Inilah sebabnya mengapa dalam Scalaz, sebuah semi-grup untuk
Map[K, V]
ada jika dan hanya jika semi-grup untukV
ada - semi-grupV
digunakan untuk menggabungkan nilai-nilai dari dua peta yang ditugaskan untuk kunci yang sama.Jadi karena
Int
jenis nilai di sini, "tabrakan" pada1
kunci diselesaikan dengan penambahan integer dari dua nilai yang dipetakan (seperti yang dilakukan oleh operator semi-grup Int), karenanya100 + 9
. Jika nilainya adalah String, tabrakan akan menghasilkan rangkaian string dari dua nilai yang dipetakan (sekali lagi, karena itulah yang dilakukan oleh operator semi-grup untuk String).(Dan yang menarik, karena rangkaian string tidak komutatif - yaitu,
"a" + "b" != "b" + "a"
- operasi semigroup yang dihasilkan juga tidak. Jadimap1 |+| map2
berbeda darimap2 |+| map1
dalam kasus String, tetapi tidak dalam kasus Int.)sumber
scalaz
masuk akal.A
danOption[A]
) sangat besar, saya tidak percaya mereka benar-benar tipe yang sama. Saya baru saja mulai melihat Scalaz. Saya tidak yakin saya cukup pintar ...Jawaban terpendek yang saya tahu hanya menggunakan perpustakaan standar
sumber
++
menggantikan sembarang (k, v) dari peta di sisi kiri++
(di sini map1) dengan (k, v) dari peta sisi kanan, jika (k, _) sudah ada di sebelah kiri peta sisi (di sini map1), misalnyaMap(1->1) ++ Map(1->2) results in Map(1->2)
for
map1 ++ (untuk ((k, v) <- map2) menghasilkan k -> (v + map1.getOrElse (k, 0 ))).
lebih diutamakan daripada++
; Anda bacamap1 ++ map2.map{...}
sebagaimap1 ++ (map2 map {...})
. Jadi satu cara Anda memetakanmap1
elemen, dan sebaliknya Anda tidak.Solusi cepat:
sumber
Nah, sekarang di perpustakaan scala (setidaknya di 2,10) ada sesuatu yang Anda inginkan - fungsi gabungan . TAPI itu disajikan hanya di HashMap bukan di Peta. Agak membingungkan. Tanda tangannya juga rumit - tidak bisa membayangkan mengapa saya perlu kunci dua kali dan ketika saya harus menghasilkan pasangan dengan kunci lain. Namun demikian, ini bekerja dan jauh lebih bersih daripada solusi "asli" sebelumnya.
Juga di scaladoc disebutkan itu
sumber
MergeFunction
.private type MergeFunction[A1, B1] = ((A1, B1), (A1, B1)) => (A1, B1)
Ini dapat diimplementasikan sebagai Monoid dengan Scala biasa. Berikut ini adalah contoh implementasi. Dengan pendekatan ini, kita dapat menggabungkan bukan hanya 2, tetapi juga daftar peta.
Implementasi berbasis peta dari sifat Monoid yang menggabungkan dua peta.
Sekarang, jika Anda memiliki daftar peta yang perlu digabung (dalam hal ini, hanya 2), itu bisa dilakukan seperti di bawah ini.
sumber
sumber
Saya menulis posting blog tentang ini, lihat:
http://www.nimrodstech.com/scala-map-merge/
pada dasarnya menggunakan scalaz semi grup Anda dapat mencapainya dengan cukup mudah
akan terlihat seperti:
sumber
Anda juga bisa melakukannya dengan Kucing .
sumber
import cats.implicits._
,. Imporimport cats.instances.map._ import cats.instances.int._ import cats.syntax.semigroup._
tidak lebih verbose ...import cats.implicits._
Mulai
Scala 2.13
, solusi lain hanya berdasarkan pustaka standar terdiri dalam menggantigroupBy
bagian dari solusi Anda dengangroupMapReduce
yang (seperti namanya) adalah setara dengangroupBy
diikuti olehmapValues
dan langkah pengurangan:Ini:
Menggabungkan dua peta sebagai urutan tupel (
List((1,9), (2,20), (1,100), (3,300))
). Untuk keringkasan,map2
yang secara implisit dikonversi keSeq
beradaptasi dengan jenismap1.toSeq
- tapi Anda bisa memilih untuk membuatnya eksplisit dengan menggunakanmap2.toSeq
,group
Elemen berdasarkan pada bagian tupel pertama mereka (bagian grup dari grup MapReduce),map
s nilai yang dikelompokkan ke bagian tupel kedua mereka (bagian peta dari grup Mengurangi Peta ),reduce
s memetakan nilai (_+_
) dengan menjumlahkannya (kurangi bagian dari groupMap Reduce ).sumber
Inilah yang akhirnya saya gunakan:
sumber
Jawaban Andrzej Doyle berisi penjelasan yang bagus tentang semi-grup yang memungkinkan Anda menggunakan
|+|
operator untuk bergabung dengan dua peta dan menjumlahkan nilai-nilai untuk kunci yang cocok.Ada banyak cara sesuatu dapat didefinisikan sebagai turunan dari typeclass, dan tidak seperti OP Anda mungkin tidak ingin menjumlahkan kunci Anda secara khusus. Atau, Anda mungkin ingin melakukan operasi pada serikat daripada persimpangan. Scalaz juga menambahkan fungsi ekstra
Map
untuk tujuan ini:https://oss.sonatype.org/service/local/repositories/snapshots/archive/org/scalaz/scalaz_2.11/7.3.0-SNAPSHOT/scalaz_2.11-7.3.0-SNAPSHOT-javadoc.jar/!/ index.html # scalaz.std.MapFunctions
Anda dapat melakukan
sumber
Cara tercepat dan paling sederhana:
Dengan cara ini, masing-masing elemen segera ditambahkan ke peta.
Cara kedua
++
adalah:Berbeda dengan cara pertama, Dalam cara kedua untuk setiap elemen dalam peta kedua, Daftar baru akan dibuat dan digabungkan ke peta sebelumnya.
The
case
ekspresi implisit menciptakan Daftar baru menggunakanunapply
metode.sumber
Inilah yang saya temukan ...
sumber
Menggunakan pola typeclass, kita bisa menggabungkan semua tipe Numeric:
Pemakaian:
Menggabungkan urutan peta:
sumber
Saya punya fungsi kecil untuk melakukan pekerjaan itu, ada di perpustakaan kecil saya untuk beberapa fungsi yang sering digunakan yang tidak ada dalam lib standar. Ini harus bekerja untuk semua jenis peta, bisa berubah dan tidak berubah, tidak hanya HashMaps
Ini adalah penggunaannya
https://github.com/jozic/scalax-collection/blob/master/README.md#mergedwith
Dan inilah tubuhnya
https://github.com/jozic/scalax-collection/blob/master/src%2Fmain%2Fscala%2Fcom%2Fdaodecode%2Fscalax%2Fcollection%2Fextensions%2Fpackage.scala#L190
sumber