Can I yield or map one element into many in Scala? - scala

val inArray = Array("a", "b", "c", "d")
// ...
val outArray = Array("a1", "a2", "a3", "b1", "b2", "b3", "c1", "c2", "c3", "d1", "d2", "d3")
How to map inArray to outArray?
The idea is to iterate through inArray yielding 3 elements (by concatenating an index in this example) from each of its elements.

You can do that with flatMap.
inArray.flatMap(c => (1 to 3).map(c+))

This can look better using a for-comprehension
for {
s <- inArray
i <- Array(1, 2, 3) //or other traversable
}
yield s + i
This uses a combination of map and flatMap under the covers as described in detail in the SLS

Using for-comprehension.
scala> for {
| x <- Array("a", "b", "c", "d")
| n <- 1 to 3
| } yield x + n
res0: Array[java.lang.String] = Array(a1, a2, a3, b1, b2, b3, c1, c2, c3, d1, d2, d3)

Related

How to achieve the below sequence?

How to achieve result sequence with the given 2 sequences ?
val seq1 = Seq("A","B","C")
val seq2 = Seq(1,2,3)
val Result = Seq("A1","A2","A3","B1", "B2", "B3", "C1", "C2", "C3")
You can just use for comprehensions to find all combinations and concat the result into string:
val seq1 = Seq("A", "B", "C")
val seq2 = Seq(1, 2, 3)
val Result = for {
s1 <- seq1
s2 <- seq2
} yield s1 + s2

Scala - How to group elements by occurrence?

Is there a function in scala that groups all elements of a list by the number of these occurrences?
For example, I have this list:
val x = List("c", "b", "b", "c", "a", "d", "c")
And I want to get a new list like that:
x = List((3, "c"), (2, "b"), (1, "a"), (1, "d"))
You can first count the occurrences of each element and then reverse the resulting tuples:
List("c", "b", "b", "c", "a", "d", "c")
.groupBy(identity).mapValues(_.size) // Map(b -> 2, d -> 1, a -> 1, c -> 3)
.toList // List((b,2), (d,1), (a,1), (c,3))
.map{ case (k, v) => (v, k) } // List((2,b), (1,d), (1,a), (3,c))
You don't specifically mention a notion of order for the output, but if this was a requirement, this solution would need to be adapted.
Try this to get exactly what you want in the order you mentioned. (ie., order preserved in the List while taking counts):
x.distinct.map(v=>(x.filter(_==v).size,v))
In SCALA REPL:
scala> val x = List("c", "b", "b", "c", "a", "d", "c")
x: List[String] = List(c, b, b, c, a, d, c)
scala> x.distinct.map(v=>(x.filter(_==v).size,v))
res225: List[(Int, String)] = List((3,c), (2,b), (1,a), (1,d))
scala>

Behaviour of Options inside for comprehension is Scala

Two newbie questions.
It seems that for comprehension knows about Options and can skip automatically None and unwrap Some, e.g.
val x = Map("a" -> List(1,2,3), "b" -> List(4,5,6), "c" -> List(7,8,9))
val r = for {map_key <- List("WRONG_KEY", "a", "b", "c")
map_value <- x get map_key } yield map_value
outputs:
r: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))
Where do the Options go? Can someone please shed some light on how does this work? Can we always rely on this behaviour?
The second things is why this does not compile?
val x = Map("a" -> List(1,2,3), "b" -> List(4,5,6), "c" -> List(7,8,9))
val r = for {map_key <- List("WRONG_KEY", "a", "b", "c")
map_value <- x get map_key
list_value <- map_value
} yield list_value
It gives
Error:(57, 26) type mismatch;
found : List[Int]
required: Option[?]
list_value <- map_value
^
Looking at the type of the first example, I am not sure why we need to have an Option here?
For comprehensions are converted into calls to sequence of map or flatMap calls. See here
Your for loop is equivalent to
List("WRONG_KEY", "a", "b", "c").flatMap(
map_key => x.get(map_key).flatMap(map_value => map_value)
)
flatMap in Option is defined as
#inline final def flatMap[B](f: A => Option[B]): Option[B]
So it is not allowed to pass List as argument as you are notified by compiler.
I think the difference is due to the way for comprehensions are expanded into map() and flatMap method calls within the Seq trait.
For conciseness, lets define some variables:
var keys = List("WRONG_KEY", a, b, c)
Your first case is equivalent to:
val r = keys.flatMap(x.get(_))
whereas your second case is equivalent to:
val r= keys.flatMap(x.get(_).flatMap{ case y => y })
I think the issue is that Option.flatMap() should return an Option[], which is fine in the first case, but is not consistent in the second case with what the x.get().flatMap is passed, which is a List[Int].
These for-comprehension translation rules are explained in further detail in chapter 7 of "Programming Scala" by Wampler & Payne.
Maybe this small difference, setting parenthesis and calling flatten, makes it clear:
val r = for {map_key <- List("WRONG_KEY", "a", "b", "c")
| } yield x get map_key
r: List[Option[List[Int]]] = List(None, Some(List(1, 2, 3)), Some(List(4, 5, 6)), Some(List(7, 8, 9)))
val r = (for {map_key <- List("WRONG_KEY", "a", "b", "c")
| } yield x get map_key).flatten
r: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))
That's equivalent to:
scala> List("WRONG_KEY", "a", "b", "c").map (x get _)
res81: List[Option[List[Int]]] = List(None, Some(List(1, 2, 3)), Some(List(4, 5, 6)), Some(List(7, 8, 9)))
scala> List("WRONG_KEY", "a", "b", "c").map (x get _).flatten
res82: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))
The intermediate value (map_key) vanished as _ in the second block.
You are mixing up two different monads (List and Option) inside the for statement. This sometimes works as expected, but not always. In any case, you can trasform options into lists yourself:
for {
map_key <- List("WRONG_KEY", "a", "b", "c")
list_value <- x get map_key getOrElse Nil
} yield list_value

Scala collections: array groupBy and return array indexes for each group

I have an array, something like that:
val a = Array("a", "c", "c", "z", "c", "b", "a")
and I want to get a map with keys of all different values of this array and values with a collection of relevant indexes for each such group, i.e. for a given array the answer would be:
Map(
"a" -> Array(0, 6),
"b" -> Array(5),
"c" -> Array(1, 2, 4),
"z" -> Array(3)
)
Surprisingly, it proved to be somewhat more complicated that I've anticipated. The best I've came so far with is:
a.zipWithIndex.groupBy {
case(cnt, idx) => cnt
}.map {
case(cnt, arr) => (cnt, arr.map {
case(k, v) => v
}
}
which is not either concise or easy to understand. Any better ideas?
Your code can be rewritten as oneliner, but it looks ugly.
as.zipWithIndex.groupBy(_._1).mapValues(_.map(_._2))
Another way is to use mutable.MultiMap
import collection.mutable.{ HashMap, MultiMap, Set }
val as = Array("a", "c", "c", "z", "c", "b", "a")
val mm = new HashMap[String, Set[Int]] with MultiMap[String, Int]
and then just add every binding
as.zipWithIndex foreach (mm.addBinding _).tupled
//mm = Map(z -> Set(3), b -> Set(5), a -> Set(0, 6), c -> Set(1, 2, 4))
finally you can convert it mm.toMap if you want immutable version.
Here's a version with foldRight. I think it's reasonably clear.
val a = Array("a", "c", "c", "z", "c", "b", "a")
a
.zipWithIndex
.foldRight(Map[String, List[Int]]())
{case ((e,i), m)=> m updated (e, i::m.getOrElse(e, Nil))}
//> res0: scala.collection.immutable.Map[String,List[Int]] = Map(a -> List(0, 6)
//| , b -> List(5), c -> List(1, 2, 4), z -> List(3))
Another version using foldLeft and an immutable Map with default value:
val a = Array("a", "c", "c", "z", "c", "b", "a")
a.zipWithIndex.foldLeft(Map[String, List[Int]]().withDefaultValue(Nil))( (m, p) => m + ((p._1, p._2 +: m(p._1))))
// res6: scala.collection.immutable.Map[String,List[Int]] = Map(a -> List(6, 0), c -> List(4, 2, 1), z -> List(3), b -> List(5))
Starting in Scala 2.13, we can use the new groupMap which (as its name suggests) is a one-pass equivalent of a groupBy and a mapping over grouped items:
// val a = Array("a", "c", "c", "z", "c", "b", "a")
a.zipWithIndex.groupMap(_._1)(_._2)
// Map("z" -> Array(3), "b" -> Array(5), "a" -> Array(0, 6), "c" -> Array(1, 2, 4))
This:
zips each item with its index, giving (item, index) tuples
groups elements based on their first tuple part (_._1) (group part of groupMap)
maps grouped values to their second tuple part (_._2 i.e. their index) (map part of groupMap)

Functional way to create a union of all keys from a Seq of Maps in Scala

I've a Sequence of Maps
Seq[Map[String,Int]]
I want to create a Seq/Set that is the Union of all keys in each of the Map.
unionallkeys = ( "a1", "a2", "a3", "b1", "b2", "b3", "c1", "c2", "c3" )
in the example below.
scala> val a = Map( ("a1", 1), ("a2", 2), ("a3", 3) )
a: scala.collection.immutable.Map[String,Int] = Map(a1 -> 1, a2 -> 2, a3 -> 3)
scala> val b = Map( ("b1", 1), ("b2", 2), ("b3", 3) )
b: scala.collection.immutable.Map[String,Int] = Map(b1 -> 1, b2 -> 2, b3 -> 3)
scala> val c = Map( ("c1", 1), ("c2", 2), ("c3", 3) )
c: scala.collection.immutable.Map[String,Int] = Map(c1 -> 1, c2 -> 2, c3 -> 3)
scala> val misc = Map( ("a1", 1), ("b2", 2), ("c3", 3) )
misc: scala.collection.immutable.Map[String,Int] = Map(a1 -> 1, b2 -> 2, c3 -> 3)
^
scala> val rows = List(a,b,c,misc)
rows: List[scala.collection.immutable.Map[String,Int]] = List(Map(a1 -> 1, a2 -> 2, a3 -> 3), Map(b1 -> 1, b2 -> 2, b3 -> 3), Map(c1 -> 1, c2 -> 2, c3 -> 3), Map(a1 -> 1, b2 -> 2, c3 -> 3))
You can write this as a fairly clear one-liner:
scala> val keys: Set[String] = rows.flatMap(_.keySet).toSet
keys: Set[String] = Set(c3, b2, b3, c2, b1, c1, a3, a1, a2)
Let's break this down step by step. First of all, the keySet method on a map will give you a set of the map's keys. If you wanted a list of sets of keys, you could just write the following:
scala> val keySets: List[Set[String]] = rows.map(_.keySet)
keySets: List[Set[String]] = List(Set(a1, a2, a3), Set(b1, b2, b3), ...
You could then flatten the nested structure:
scala> val keyList: List[String] = keySets.flatten
keyList: List[String] = List(a1, a2, a3, b1, b2, b3, c1, c2, c3, a1, b2, c3)
Note that the result has the type of the outer collection—i.e. it's a list, not a set. You can convert it to a set with toSet:
scala> val keys: Set[String] = keyList.toSet
keys: Set[String] = Set(c3, b2, b3, c2, b1, c1, a3, a1, a2)
And you're done. The first line above just does all of these steps at once, and combines the map and flatten calls into a single flatMap.
Travis solution is immutable which is better in term of design.
Let me just provide a solution using mutable Set:
def keysUnion[A,B](maps: Seq[Map[A,B]]): scala.collection.Set[A] = {
val allKeys = scala.collection.mutable.Set[A]()
maps.foreach( m => allKeys ++= m.keySet )
allKeys
}
// test with some random input data
import scala.util.Random
val maps = (1 to 10).map( i => (1 to i).map( k => k -> k ).toMap )
keysUnion(maps)
I don't know if the mutable.Set is efficient. And most probably the .toSet of the immutable solution does the same. So for simple case it should be very similar, but it should be better if the flatten list of keys is (really) big.
Note:
when using mutable collection, it's better to hide it inside method that output a trait with immutable API. This is why the solution I provided is in the form of a function which output scala.collection.Set.