'Scala: grouped from right?

In Scala, grouped works from left to right.

val list = List(1,2,3,4,5)
list.grouped(2).toList
=> List[List[Int]] = List(List(1, 2), List(3, 4), List(5))

But what if I want:

=> List[List[Int]] = List(List(1), List(2, 3), List(4, 5))

?

Well I know this works:

list.reverse.grouped(2).map(_.reverse).toList.reverse

It seems not efficient, however.



Solution 1:[1]

Then you could implement it by yourself:

def rgrouped[T](xs: List[T], n: Int) = {
   val diff = xs.length % n
   if (diff == 0) xs.grouped(n).toList else {
     val (head, toGroup) = xs.splitAt(diff)
     List(head, toGroup.grouped(n).toList.head)
   }
}

Quite ugly, but should work.

Solution 2:[2]

Here is my attempt:

def rightGrouped[T](ls:List[T], s:Int) = {
  val a = ls.length%s match { 
    case 0 => ls.grouped(s) 
    case x => List(ls.take(x)) ++ ls.takeRight(ls.length-x).grouped(s)
  }
  a.toList
}

Usage:

scala> rightGrouped(List(1,2,3,4,5),3)
res6: List[List[Int]] = List(List(1, 2), List(3, 4, 5))

I initially tried without pattern matching, but it was wrong when the list was "even"

val ls = List(1,2,3,4,5,6)
val s = 3
val x = ls.length % s
List(ls.take(x)) ++ ls.takeRight(ls.length-x).grouped(s)

produced:

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

Solution 3:[3]

val l =List(list.head)::(list.tail grouped(2) toList) 

EDIT:

After @gzm0 pointed out my mistake I have fixed the solution, though it works only for n=2

def group2[T](list: List[T]) ={
    (list.size % 2 == 0) match {
      case true => list.grouped(2).toList
      case false => List(list.head) :: (list.tail grouped(2) toList)

    }
  }

println(group2(List())) 
println(group2(List(1,2,3,4,5)))
println(group2(List(1,2,3,4,5,6)))

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

Solution 4:[4]

Staying consistent with idiomatic use of the Scala Collections Library such that it also works on things like String, here's an implementation.

def groupedRight[T](seqT: Seq[T], width: Int): Iterator[Seq[T]] =
  if (width > 0) {
    val remainder = seqT.length % width
    if (remainder == 0)
      seqT.grouped(width)
    else
      (seqT.take(remainder) :: seqT.drop(remainder).grouped(width).toList).iterator
  }
  else
    throw new IllegalArgumentException(s"width [$width] must be greater than 0")

val x = groupedRight(List(1,2,3,4,5,6,7), 3).toList
// => val x: List[Seq[Int]] = List(List(1), List(2, 3, 4), List(5, 6, 7))
val sx = groupedRight("12345", 3).toList
// => val sx: List[Seq[Char]] = List(12, 345)
val sx = groupedRight("12345", 3).toList.map(_.mkString)
// => val sx: List[String] = List(12, 345)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 om-nom-nom
Solution 2 Sailesh
Solution 3
Solution 4 chaotic3quilibrium