I have a function that maps a value of type A to a pair (Seq[B], Seq[C]). I want to apply the function to a sequence of A, and return a pair of flattened Seq[B] and Seq[C]. Here is the code snippet:
val a: Seq[A]
val mapped: Seq[(Seq[B], Seq[C])] = a.map(f)
val (b, c) = mapped.unzip
val bc: (Seq[B], Seq[C]) = (b.flatten, c.flatten)
The solution is acceptable, but is there a more idiomatic way to do this? I though about for-comprehensions or flatMaps, but I cannot see how I can apply them to the pair.
shapeless 2.0.0-M1
is available, so you can map on tuples:
import shapeless._, syntax.std.tuple._, poly._
val l = Seq(Seq(1, 2, 3, 4, 5) -> Seq('a, 'b, 'c), Seq(101, 102, 103) -> Seq('d, 'e, 'f))
type SS[T] = Seq[Seq[T]]
object flatten extends (SS ~> Seq) {
def apply[T](ss: SS[T]) = ss.flatten
}
l.unzip.map(flatten) // l.unzip.hlisted.map(flatten).tupled for older versions of shapeless
// (Seq[Int], Seq[Symbol]) = (List(1, 2, 3, 4, 5, 101, 102, 103),List('a, 'b, 'c, 'd, 'e, 'f))
Actually it should be possible to convert polymorphic methods to polymorphic function automatically, but this code doesn't work:
def flatten[T](s: Seq[Seq[T]]) = s.flatten
l.unzip.map(flatten _)
//<console>:31: error: type mismatch;
// found : Seq[T]
// required: Seq[Seq[T]]
// l.unzip.map(flatten _)
// ^
I would probably use the Monoid
instance for Tuple2
, and Foldable
instance for List
(which comes into play via its Traverse
instance) to sum the resulting list.
scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._
scala> val a = List(11,222,3333,44444,555555)
a: List[Int] = List(11, 222, 3333, 44444, 555555)
scala> def f(a: Int): (List[String], List[Int]) = (a.toString.tails.toList, a.toString.tails.toList.map(_.size))
f: (a: Int)(List[String], List[Int])
scala> val mapped = a map f
mapped: List[(List[String], List[Int])] = List((List(11, 1, ""),List(2, 1, 0)), (List(222, 22, 2, ""),List(3, 2, 1, 0)), (List(3333, 333, 33, 3, ""),List(4, 3, 2, 1, 0)), (List(44444, 4444, 444, 44, 4, ""),List(5, 4, 3, 2, 1, 0)), (List(555555, 55555, 5555, 555, 55, 5, ""),List(6, 5, 4, 3, 2, 1, 0)))
scala> mapped.suml
res1: (List[String], List[Int]) = (List(11, 1, "", 222, 22, 2, "", 3333, 333, 33, 3, "", 44444, 4444, 444, 44, 4, "", 555555, 55555, 5555, 555, 55, 5, ""),List(2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0, 5, 4, 3, 2, 1, 0, 6, 5, 4, 3, 2, 1, 0))
UPD: If you really want Seq
, here's what you need to have in the scope to make it work:
implicit val isoTraverse: IsomorphismTraverse[Seq, List] = new IsomorphismTraverse[Seq, List] {
def G = Traverse[List]
def iso = new IsoFunctorTemplate[Seq, List] {
def to[A](sa: Seq[A]): List[A] = sa.toList
def from[A](la: List[A]): Seq[A] = la.toSeq
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With