Reputation: 2069
I have this working code to create a Map between the characters in a String, and a List containing the indexes.
scala> "Lollipop".zipWithIndex.foldLeft(Map[Char, List[Int]]())((acc, t) => acc + (t._1 -> (acc.getOrElse(t._1, List[Int]()) :+ t._2)))
res122: scala.collection.immutable.Map[Char,List[Int]] = Map(i -> List(4), L -> List(0), l -> List(2, 3), p -> List(5, 7), o -> List(1, 6))
But the use of acc.getOrElse
looks imperative.
Is there a more functional way that hides this from the user?
Upvotes: 0
Views: 104
Reputation: 62835
Alternative is to use default value for your map (rewritten code a little bit to be more explicit):
val empty = Map.empty[Char, List[Int]].withDefaultValue(List.empty)
"Lollipop".zipWithIndex.foldLeft(empty) {
case (acc, (char, position)) => {
val positions = acc(char) :+ position
acc + (char -> positions)
}
}
Upvotes: 2
Reputation: 38045
for {
(c, l) <- "Lollipop".zipWithIndex.groupBy{ _._1 }
} yield c -> l.map{ _._2 }
// Map(i -> Vector(4), L -> Vector(0), l -> Vector(2, 3), p -> Vector(5, 7), o -> Vector(1, 6))
After groupBy{ _._1 }
you'll get a Map[Char, Seq[(Char, Int)]]
. So you have to convert pairs (Char, Int)
to Int
, using p => p._2
or just _._2
.
You could use mapValues
like this:
"Lollipop".zipWithIndex.groupBy{ _._1 }.mapValues{ _.map{_._2} }
But mapValues
creates a lazy collection, so you could get a performance issue in case of multiple access to the same element by key.
Upvotes: 5