Processing Set of Sets and return a flat Iterable
val input=Set(Set("a","b"),Set("b","c"))
I开发者_运维知识库 want this:
Map("a"->1,"b"->2,"c"->1)
What is the best functional approach for implementing such functionality? Using yield keyword results in nested Iterables:
output = for(firstlevel<-input) yield for(item<-firstlevel) yield item
update: incorporated the suggestion to use input.toSeq.flatten
instead of input.toSeq flatMap { _.toSeq }
convert to a single sequence of values...
input.toSeq.flatten
...group values that match...
input.toSeq.flatten groupBy { identity }
...and count
input.toSeq.flatten groupBy { identity } mapValues { _.size }
If you want to use for-comprehension and yield:
output = for{
(set,idx) <- input.zipWithIndex
item <- set
} yield (item -> idx)
The code in your last line can be simplified (but does not what you want):
output = for{
set <- input
item <- set
} yield item
Oh boy, that's so ugly...
input.foldLeft(Map[String,Int]())((m,s) =>
s.foldLeft(m)((n,t) => n + (t -> (1 + n.getOrElse(t,0)))))
[Edit]
The Collection-API needs really a method for "merging" two Maps (or did I just overlook it???), e.g.
def merge[A,B](m1: Map[A,B], m2:Map[A,B])(f: (B,B)=>B):Map[A,B] =
m1.foldLeft(m2)((m,t) =>
m + (t._1 -> m.get(t._1).map(k => f(k,t._2)).getOrElse(t._2)))
With this you could write something like:
input.map(_.map(x => x -> 1).toMap).reduceLeft(merge(_,_)(_+_))
[Edit2]
With Kevin's idea merge could be written as
def merge[A,B](m1: Map[A,B], m2:Map[A,B])(f: (B,B)=>B):Map[A,B] =
m1.keys ++ m2.keys map {k => k ->
List(m1.get(k), m2.get(k)).flatten.reduceLeft(f)} toMap
Seems like my Scala-Fu is still too weak. What's the best way to express
(o1,o2) match {
case (Some(x),Some(y)) => Some(f(x,y))
case (Some(x), _) => Some(x)
case (_, Some(y)) => Some(y)
case => error("crack in the time-space-continuum")
}
?
精彩评论