2015-04-17 3 views
0

У меня есть два Map[String, T] s, где T является экземпляром подтипа Fruit. Мне нужно построить новую карту с двух карт, где ключ - это общие имена ключей из двух карт, а значение - Seq[Fruit], если значения из двух карт имеют один и тот же тип.scala: type равенство двух переменных

class Fruit 

case class Apple() extends Fruit 
case class Banana(num: Int) extends Fruit 
case class Orange() extends Fruit 

Например, если у меня есть следующие две карты:

val map1 = Map("first" -> Apple(), 
       "second" -> Banana(3), 
       "third" -> Orange()) 

val map2 = Map("first" -> Orange(), 
       "second" -> Banana(4), 
       "third" -> Orange()) 

Мне нужно результирующую карту, map3 который имеет следующие члены:

generateMap(map1: Map[String, Fruit], map2: Map[String, Fruit]): Map[String, Seq[Fruit]] 

=> results a map look like 

    Map("second" -> Seq(Banana(3), Banana(4)), 
     "third" -> Seq(Orange(), Orange()) 

Я не знаю, как написать функцию, generateMap. Может ли кто-нибудь помочь мне реализовать это? (используя Scala 2.11.x)

Обратите внимание, что определения классов (Фрукты и другие) фиксированы, поэтому я не могу их модифицировать.

ответ

1
scala> val r: Map[String, Seq[Fruit]] = (map1.toList ++ map2.toList). 
    groupBy(x => x._1). 
    mapValues(lst => lst.map(x => x._2)). 
    .filter { 
    case (key, lst) => lst.forall(x => 
      x.getClass == lst.head.getClass) 
    } 

r: Map[String, Seq[Fruit]] = Map(third -> List(Orange(), Orange()), 
    second -> List(Banana(3), Banana(4))) 
+0

спасибо, но результат отличается от того, что я ожидал. Поскольку map1 («first») имеет другой тип из map2 («first»), сгенерированный вывод не должен иметь «первый». – cinsk

+0

Хорошо, я отредактирую свой ответ, чтобы отфильтровать эти значения. – ipoteka

0

val m3 = (map1.toSeq ++ map2.toSeq). // Combine the maps 
 
    groupBy (x=>x._1). //Group by the original keys 
 
    map{case (k,lst)=> (k, lst.map(x=> x._2))}. //Strip the keys from the grouped sequences 
 
    filter{case (_, lst) => lst.forall(i => lst.head.getClass == i.getClass)}. //Filter out hetergeneous seqs 
 
    toMap // Make a map

0

Без FORALL:

(map1.toList ++ map2.toList).groupBy(_._1).mapValues(_.map(_._2)) 
    .filter(_._2.map(_.getClass).toSet.tail.isEmpty) 

Map(third -> List(Orange(), Orange()), second -> List(Banana(3), Banana(4))) 

Эта версия требует немного больше (но по-прежнему линейна внутри filter) процессора и памяти, чем версия с forall, так что вы должны используйте его только для небольших коллекций.

Смежные вопросы