What combinator to use? scanLeft but not every iteration

Given a list of integers

Seq(2,4,6,1,2,2,3,7,4,4,4)

produce a new list where all consecutive even numbers are summed, and intervening odd numbers are kept unchanged.

Seq(2,4,6,1,2,2,3,7,4,4,4).fn(???) = Seq(12,1,4,3,7,12)
// ((2 + 4 + 6) = 12, 1, (2 + 2) = 4, 3, 7, (4 + 4 + 4) = 12)

It seems to be a mix of scanning and takeWhile, except the scan combinators take snapshots every iteration while we need to only take one snapshot per group of consecutive even numbers.

Here’s a take at it doing the recursion explicitly:

1 Like

I don’t think there’s one specific combinator for this use case. You could probably use foldleft or foldRight, but it will look just like @anqit’s solution without the explicit recursion.

With span you could simplify your custom combinator a bit:

def combine(list: List[Int]): List[Int] = {
  val (evens, rest) = list.span(_ % 2 == 0)
  if (evens.nonEmpty) evens.sum :: combine(rest)
  else if (rest.nonEmpty) rest.head :: combine(rest.tail)
  else Nil
}
1 Like

TIL List#span, cool!
I tried a solution with foldLeft/Right, but the issue I had was not knowing when you were at the last element of the list (in either direction) and being able to handle adding the last element.

That is indeed a problem. It’s probably not worth the effort to try to fit it in foldLeft.

1 Like

FWIW, had a go at this approach:

def f(list: List[Int]) = LazyList.unfold(list){
  case Nil => None
  case x::xs if x % 2 == 1 => Some(x, xs)
  case xs => xs.span(_ % 2 == 0) match { case (even, rest) => Some(even.sum, rest) }
}.toList

val ints = List(2,4,6,1,2,2,3,7,4,4,4)

assert(f(ints) == List(12,1,4,3,7,12))