2013-04-28 13 views
9

Quale sarebbe un modo funzionale per comprimere due dizionari in Scala?Zip due HashMaps (o dizionari)

map1 = new HashMap("A"->1,"B"->2) 
map2 = new HashMap("B"->22,"D"->4) // B is the only common key 

zipper(map1,map2) dovrebbe dare qualcosa di simile a

Seq(("A",1,0), // no A in second map, so third value is zero 
     ("B",2,22), 
     ("D",0,4)) // no D in first map, so second value is zero 

Se non è funzionale, qualsiasi altro stile è apprezzato anche

+2

Datakap di Haskell ha un meraviglioso combinatore chiamato 'unionWith' che renderebbe questo incredibilmente facile. Esso e la sua controparte di intersezione sono straordinariamente utili, e mi dispiace che siano disponibili solo in 'IntMap' e' LongMap' di Scala (soprattutto perché sono tradotti da Haskell, mi aspetto). – copumpkin

+0

@copumpkin: Haskell è incredibile! Verificherò unionWith.Io ho appena controllato IntMap e ora è sostituito da HashMap da 2.8 su – RAbraham

+0

Non è in realtà superato da 'HashMap', nonostante ciò che il commento della documentazione suggerisce. Sono strutture differenti e 'IntMap' ha proprietà diverse. A volte non si desidera realmente la funzione di hashing, in particolare quando si desidera mantenere l'ordine dei dati. 'IntMap' potrebbe implementare per lo più' SortedMap', ma non credo che lo faccia adesso. Un pasticcio è che segue un ordinamento "non firmato", ma non è difficile farlo comportarsi come un segno firmato se è quello che ti serve. – copumpkin

risposta

13
def zipper(map1: Map[String, Int], map2: Map[String, Int]) = { 
    for(key <- map1.keys ++ map2.keys) 
    yield (key, map1.getOrElse(key, 0), map2.getOrElse(key, 0)) 
} 


scala> val map1 = scala.collection.immutable.HashMap("A" -> 1, "B" -> 2) 
map1: scala.collection.immutable.HashMap[String,Int] = Map(A -> 1, B -> 2) 

scala> val map2 = scala.collection.immutable.HashMap("B" -> 22, "D" -> 4) 
map2: scala.collection.immutable.HashMap[String,Int] = Map(B -> 22, D -> 4) 

scala> :load Zipper.scala 
Loading Zipper.scala... 
zipper: (map1: Map[String,Int], map2: Map[String,Int])Iterable[(String, Int, Int)] 

scala> zipper(map1, map2) 
res1: Iterable[(String, Int, Int)] = Set((A,1,0), (B,2,22), (D,0,4)) 

Nota usando get è probabilmente preferibile getOrElse in questo caso. None viene utilizzato per specificare che non esiste un valore invece di utilizzare 0.

+1

codice bello :) – RAbraham

+0

C'è un modo per ottenere lo 'zero' di un parametro di tipo? Zipper potrebbe essere reso generico a parte quelli fastidiosi '0' –

+0

Non sono sicuro che sarebbe la direzione giusta per generalizzare, ma si potrebbe guardare a 'Monoid' che ha un'operazione (associativa) e uno zero. Uno zero da solo non avrebbe molto senso, poiché ciò che lo rende uno zero è il modo in cui interagisce con altre cose. Come ho suggerito nel mio altro commento, penso 'unionWith [A, B] (x: Map [A, B], y: Map [A, B]) (f: (B, B) => B): Map [A, B] 'è l'approccio ideale qui. La funzione di combinazione viene utilizzata solo in caso di sovrapposizione, quindi non è nemmeno necessario un concetto di zero. Più generale: 'unionWith [K, A, B, C]) (x: Mappa [K, A], y: Mappa [K, B]) (l: A => C, r: B => C, b : (A, B) => C): Mappa [K, C] '. –