Idiomatic flattening pairs in Scala

I have a function that maps a value of type A to a pair (Seq [B], Seq [C]). I want to apply a function to sequence 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, for example, pro-considerations or flatMaps, but I do not see how I can apply them to a pair.

+4
source share
3 answers

shapeless 2.0.0-M1 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)) 

It should actually be possible to convert polymorphic methods to a polymorphic function automatically, but this code does not 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 _) // ^ 
+3
source

I would probably use the Monoid instance for Tuple2 and the Foldable for List (which comes into play through the Traverse instance ) to sum as a result of the 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 is what you need to have in the area in order 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 } } 
+3
source

I don't think your code is particularly non-idiomatic, but here is what I would write. Note that I do not believe that my code will be better than yours, I'm just a fan of folding.

 // For testing purposes only, replace with your own code. val b = List((List(1, 2), List(5, 6)), (List(3, 4), List(7, 8))) val (l, r) = b.foldLeft((List[Int](), List[Int]())) {(acc, a) => (acc._1 ::: a._1, acc._2 ::: a._2) } // Prints 'List(1, 2, 3, 4)' println(l) // Prints 'List(5, 6, 7, 8)' println(r) 
+1
source

Source: https://habr.com/ru/post/1502817/


All Articles