Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Split a scala list into n interleaving lists

Given a List such as

List(1, 2, 3, 4, 5, 6, 7)

what is the best way to split it into n sub-lists, putting items into each list in a round-robin fashion?

e.g. if n = 3, the result should be

List(List(1, 4, 7), List(2, 5), List(3, 6))

I thought there would be a method in the collections API to do this, but I can't seem to find it.

Bonus points for classy one-liners ;)

like image 594
Chris B Avatar asked Jun 21 '12 06:06

Chris B


2 Answers

scala> def round[T](l: List[T], n: Int) = (0 until n).map{ i => l.drop(i).sliding(1, n).flatten.toList }.toList
round: [T](l: List[T], n: Int)List[List[T]]

scala> round((1 to 7).toList, 3)
res4: List[List[Int]] = List(List(1, 4, 7), List(2, 5), List(3, 6))
like image 95
senia Avatar answered Oct 06 '22 13:10

senia


Here is a simple one-liner:

scala> List.range(1, 10)
res11: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)

scala> res11.grouped(3).toList.transpose
res12: List[List[Int]] = List(List(1, 4, 7), List(2, 5, 8), List(3, 6, 9))

Unfortunately it won't work when your list isn't transposeable.

scala> List.range(1, 8).grouped(3).toList.transpose
java.lang.IllegalArgumentException: transpose requires all collections have the 
same size

You can use the following method to make it transposeable, and then apply the above approach.

scala> def extend[A](xs: List[A], c: Int): List[Option[A]] = {
     |   val n = Stream.iterate(c)(c +).find(_ >= xs.length).get
     |   xs.map(Some(_)).padTo(n, None)
     | }
extend: [A](xs: List[A], c: Int)List[Option[A]]

scala> List.range(1, 8)
res13: List[Int] = List(1, 2, 3, 4, 5, 6, 7)

scala> extend(res13, 3).grouped(3).toList.transpose.map(_.flatten)
res14: List[List[Int]] = List(List(1, 4, 7), List(2, 5), List(3, 6))

Put together:

scala> def round[A](xs: List[A], c: Int) = {
     |   val n = Stream.iterate(c)(c +).find(_ >= xs.length).get
     |   val ys = xs.map(Some(_)).padTo(n, None)
     |   ys.grouped(c).toList.transpose.map(_.flatten)
     | }
round: [A](xs: List[A], c: Int)List[List[A]]

scala> round(List.range(1, 10), 3)
res16: List[List[Int]] = List(List(1, 4, 7), List(2, 5, 8), List(3, 6, 9))

scala> round(List.range(1, 8), 3)
res17: List[List[Int]] = List(List(1, 4, 7), List(2, 5), List(3, 6))
like image 33
missingfaktor Avatar answered Oct 06 '22 15:10

missingfaktor