Фильтрация Scala Multimap и вывод в виде списка кортежей

У меня есть карта, использующая свойство multimap, например так
val multiMap = new HashMap[Foo, Set[Bar]] with MultiMap[Foo, Bar]
Я хотел бы совместить фильтрацию этой карты по определенным значениям
multiMap.values.filter(bar => barCondition)
с объединением совпадающих результатов в список кортежей формы< br> val fooBarPairs: List[(Foo, Bar)]
Каким идиоматическим способом это можно было бы сделать? Я надеялся, что Scala может предоставить что-то вроде анаморфизма, чтобы сделать это без циклов, но как полный новичок я не уверен, какие у меня есть варианты.


person MilesHampson    schedule 22.07.2012    source источник
comment
Если foo отображается в набор баров, удовлетворяющих предикату фильтра, должен ли результирующий список содержать кортежи (foo, bar) для всех баров? Было бы полезно, если бы вы привели несколько иллюстрирующих примеров.   -  person Malte Schwerhoff    schedule 22.07.2012
comment
В моем вопросе «Результаты сопоставления» я хотел указать, что меня интересуют только слитки, соответствующие критериям фильтра, извините, если это было неясно.   -  person MilesHampson    schedule 22.07.2012


Ответы (3)


Вот пример:

import collection.mutable.{HashMap, MultiMap, Set}

val mm = new HashMap[String, Set[Int]] with MultiMap[String, Int]
mm.addBinding("One", 1).addBinding("One",11).addBinding("Two",22).
  addBinding("Two",222)
  // mm.type = Map(Two -> Set(22, 222), One -> Set(1, 11))

Я думаю, что самый простой способ получить то, что вы хотите, - это использовать for-expression:

for {
  (str, xs) <- mm.toSeq
  x         <- xs
  if x > 10
} yield (str, x)      // = ArrayBuffer((Two,222), (Two,22), (One,11))

Вам нужен .toSeq, иначе тип вывода будет Map, что будет означать, что каждое сопоставление перекрывается последующими элементами. Используйте toList в этом выводе, если вам нужен именно List.

person Luigi Plinge    schedule 22.07.2012
comment
Мне это нравится, понимание последовательности прекрасно подходит для этой проблемы. - person MilesHampson; 22.07.2012
comment
Нет необходимости в toSeq, если вы используете collection.breakOut. - person Debilski; 22.07.2012
comment
@Debilski Заинтригован. Можете ли вы использовать breakOut в for-выражениях? Или просто когда вы обезжириваете его до чего-то вроде ответа @mhs. - person Luigi Plinge; 22.07.2012
comment
(for {...} yield ())(breakOut) : List[(String, Int)] действительно должно работать. - person Debilski; 22.07.2012

Вот пример того, что, я думаю, вы хотите сделать:

scala> mm
res21: scala.collection.mutable.HashMap[String,scala.collection.mutable.Set[Int]] with scala.collection.mutable.MultiMap[String,Int]
= Map(two -> Set(6, 4, 5), one -> Set(2, 1, 3))

scala> mm.toList.flatMap(pair =>
         pair._2.toList.flatMap(bar =>
           if (bar%2==0)
             Some((pair._1, bar))
           else
             None))

res22: List[(String, Int)] = List((two,6), (two,4), (one,2))
person Kim Stebel    schedule 22.07.2012
comment
Это полезно, я не думал о разбивке на два уровня. Я использую только соответствующие типы, поэтому, думаю, мне придется снова фильтровать типы None с этим. Хотя это кажется хорошим общим решением. - person MilesHampson; 22.07.2012

Вот еще одно, немного более лаконичное решение:

import collection.mutable.{HashMap, MultiMap, Set}

val mm = new HashMap[String, Set[Int]] with MultiMap[String, Int]
val f = (i: Int) => i > 10

mm.addBinding("One", 1)
  .addBinding("One",11)
  .addBinding("Two",22)
  .addBinding("Two",222)
  /* Map(Two -> Set(22, 222), One -> Set(1, 11)) */

mm.map{case (k, vs) => vs.filter(f).map((k, _))}.flatten
  /* ArrayBuffer((Two,222), (Two,22), (One,11)) */
person Malte Schwerhoff    schedule 22.07.2012
comment
Фильтрация, а затем перестроение карты кажется хорошим способом решить эту проблему, я бы дал вам баллы, если бы мог. Однако мне нужен был результат в виде списка, а не итератор, заданный функцией flatten. - person MilesHampson; 22.07.2012