Reputation: 8807
Given:
Seq(1,2,3) map {
case 1 => 11
case 2 => Seq(12,13,14)
case 3 => 15
}
How can I elegantly flatten this to a Seq[Int]
containing Seq(11,12,13,14,15)
?
Upvotes: 1
Views: 7190
Reputation: 7461
If you already have this list, you may do flatMap
to achieve the required result:
val list = Seq(11, Seq(12, 13, 14), 15)
val flattened = list.flatMap {
case x: Int => Seq(x)
case y: Seq[Int] => y
}
To avoid warnings (although, it'll not help you in making this code type-safe) you may use following:
val flattened = list.flatMap {
case x: Int => Seq(x)
case y: Seq[_] => y.asInstanceOf[Seq[Int]]
}
Upvotes: 0
Reputation: 12091
When you have Seq(1,2,3)
you have a Seq[Int]
.
after your map operation you have a problem though
val mapped = Seq(1,2,3) map {
case 1 => 11
case 2 => Seq(12,13,14)
case 3 => 15
}
What is the type of mapped
? A reasonable answer to this could be that it doesn't have a type. That's close to the truth. The resulting type is Seq[Any]
. Unfortunately, this is a completely useless type. It's not something you could do anything useful with in a typesafe way, and a good point could be made that Scala shouldn't have allowed this type to be inferred in the first place.
The solution is not to let it get that far in the first place, but map to something that does have a sensible type. The solution shown by Simon is a reasonable approach:
val mapped = Seq(1,2,3) map {
case 1 => Seq(11)
case 2 => Seq(12,13,14)
case 3 => Seq(15)
case _ => throw new Exception("uh oh, didn't account for this to happen!")
}
Now mapped is a Seq[Seq[Int]]
, and we can do more useful things with it, for example flatten it to a Seq[Int]
with mapped.flatten
But we can get there in one go. There is an operation called flatMap
on Seq[A]
that takes a function A => Seq[A]
as it's argument, and returns a single Seq[A]
.
val flatmapped = Seq(1,2,3) flatMap {
case 1 => Seq(11)
case 2 => Seq(12,13,14)
case 3 => Seq(15)
case _ => throw new Exception("uh oh, didn't account for this to happen!")
}
flatmapped
is now Seq(11, 12, 13, 14, 15)
Aside
It turns out, by the way, that it's very useful to have this operation on all sorts of parameterized types: (F[A], A => F[A]) => F[A]
For example:
Option[A].flatMap(a: A => Option[A]): Option[A]
def squareroot(x: Double): Option[Double] = if (x >= 0) Some(Math.sqrt(x))
else None
Some(4.0).flatMap(squareroot) == Some(2.0)
Some(-1.0).flatMap(squareroot) == None
None.flatMap(squareroot) == None
or
Future[A].flatMap(a: A => Future[A]): Future[A]
This operation is sometimes called flatMap
, and sometimes called bind
, and sometimes represented as =>>
, and if a type (call it F[A]
) that support this operation, and support another operation that can create an F[A]
from an A
as well (sometimes called point
and sometimes called return
), and follow some conditions on how these operations compose, F[A]
forms a Monad
Upvotes: 1
Reputation: 16308
Here's another way to do it:
implicit def unitSeq[T](x: T): Seq[T] = Seq(x)
Seq(1, 2, 3) flatMap {
case 1 => 11
case 2 => Seq(12, 13, 14)
case 3 => 15
}
res0: Seq[Int] = List(11, 12, 13, 14, 15)
Upvotes: 1
Reputation: 6353
Here's one way to do it:
Seq(1,2,3) flatMap {
case 1 => Seq(11)
case 2 => Seq(12,13,14)
case 3 => Seq(15)
}
res0: Seq[Int] = List(11, 12, 13, 14, 15)
Upvotes: 7