Loop over a List getting an increasing number of elements each time

Viewed 367

Let's say I have a list that looks like

{A, B, C, D, E}  

And I want to loop over this list, getting an increasing number of elements each time, so each iteration would look like:

Iteration 1: {A}  
Iteration 2: {A, B}
Iteration 3: {A, B, C}  
Iteration 4: {A, B, C, D}
Iteration 5: {A, B, C, D, E}  

Currently I am accomplishing this with:

(1 to list.size).foreach( n => {
  val elements = list.take(n)
  // Do something with elements
})

But that feels messy. Is there a more 'scala' way of accomplishing this behavior?

3 Answers

You could use list.inits :

scala> List(1,2,3,4,5).inits.foreach(println)
List(1, 2, 3, 4, 5)
List(1, 2, 3, 4)
List(1, 2, 3)
List(1, 2)
List(1)
List()

To get your desired out put you would need to create a list from the iterator, reverse it and take the tail to omit the empty list:

scala> List(1,2,3,4,5).inits.toList.reverse.tail.foreach(println)
List(1)
List(1, 2)
List(1, 2, 3)
List(1, 2, 3, 4)
List(1, 2, 3, 4, 5)

You can use foldLeft with a linked list to accumulate the elements.

However, this will reverse the order, so you'll need to call the .reverse function if you really care about the order, which wouldn't be efficient.

list.foldLeft(Nil : List[String]){(l, n) => {
  val elements = n :: l
  println(elements)
  elements
}}

Output:

List(A)
List(B, A)
List(C, B, A)
List(D, C, B, A)
List(E, D, C, B, A)

Here's a version that preserves order but uses a ListBuffer, which isn't great

val elems = ListBuffer[String]()
list.foreach{ s => 
  elems += s
  println(elems)
}

The same, but with a fold

list.foldLeft(ListBuffer[String]()){(elems, s) => 
  elems += s
  println(elems)
  elems
}

Output:

ListBuffer(A)
ListBuffer(A, B)
ListBuffer(A, B, C)
ListBuffer(A, B, C, D)
ListBuffer(A, B, C, D, E)

Here is a recursive version. You will need to reverse the list first.

@tailrec
def doIt(l: List[Int], acc: List[List[Int]] = Nil): List[List[Int]] = l match {
  case Nil => acc
  case h :: t => doIt(t, List(h) :: acc.map(l => h :: l))
}


doIt(List(1,2,3).reverse).foreach(println)

// output
List(1)
List(1, 2)
List(1, 2, 3)
Related