How to subtract two consecutive element in a list in Scala?

Viewed 957

I would like to subtract two consecutive element in a list with numbers in Scala.

For example : I have this list :

val sortedList = List(4,5,6)

I would like to have an output list like diffList =(1, 1) where 5-4 = 1 and 6-5 = 1.

I tried the following code:

var sortedList = List[Int]()
var diffList = List[Int]()

for (i <- 0 to (sortedList.length - 1) ;j <- i + 1 to sortedList.length - 1) 
{
    val diff = (sortedList(j) - sortedList(i))
    diffList = diffList :+ diff
}

I have the following result for diffList =(1, 2, 1) but I want diffList = (1,1).

It's because of the for loop. it does not iterate over the two variables (i and j) at once.

4 Answers

You do not mutability nor imperative programming to solve this problem, functional programming got you covered.

def consecutiveDifferences(data: List[Int]): List[Int] =
  if (data.isEmpty) List.empty
  else data.lazyZip(data.tail).map {
    case (x, y) => y - x
 }

As I always say, the Scaladoc is your friend.
(Also, as an advice, the best way to learn functional programming is to forbid yourself from mutability)

You can use the sliding method, which according to the docs:

/** Groups elements in fixed size blocks by passing a "sliding window"
 *  over them (as opposed to partitioning them, as is done in `grouped`.)
 *
 *  An empty collection returns an empty iterator, and a non-empty
 *  collection containing fewer elements than the window size returns
 *  an iterator that will produce the original collection as its only
 *  element.
 *  @see [[scala.collection.Iterator]], method `sliding`
 *
 *  @param size the number of elements per group
 *  @return An iterator producing ${coll}s of size `size`, except for a
 *          non-empty collection with less than `size` elements, which
 *          returns an iterator that produces the source collection itself
 *          as its only element.
 *  @example `List().sliding(2) = empty iterator`
 *  @example `List(1).sliding(2) = Iterator(List(1))`
 *  @example `List(1, 2).sliding(2) = Iterator(List(1, 2))`
 *  @example `List(1, 2, 3).sliding(2) = Iterator(List(1, 2), List(2, 3))`
 */

Then, solving your query is pretty straight forward:

diffList = sortedList.sliding(2).collect {
  case Seq(a, b) =>
    b - a
}.toList

Which results in List(1,1)

Code run at Scastie.

for(i <- 0 until (sortedList.size - 1)) yield sortedList(i + 1) - sortedList(i)

yield Vector(1,1) which can be converted to list with toList

That's can be also achieved with the following function:

  val sortedList = List(4,5,7)
  @tailrec
  def findDiffs(xs: List[Int])(seed: List[Int]): List[Int] = {
    if(xs.isEmpty || xs.size == 1) seed.reverse
    else {
      val currDiff = xs(1) - xs(0)
      findDiffs(xs.tail)(currDiff :: seed)
    }
  }
  val res = findDiffs(sortedList)(Nil)
  println(res)

Or just easily with zip:

sortedList.drop(1) zip sortedList map { case (x,y) => x - y } 

Sliding (see answer by @Tomer Shetah) over a list delivers an iterator, which may prove convenient for very large collections to avoid/reduce the amount of intermediate structures in the processing. Another approach includes the zipping of the list with itself shifted by one (see answers by @Luis Miguel Mejía Suárez and @Zvi Mints); in this regard another approach to shifting and then zipping is by dropping the first element as in

xs.drop(1) zip xs map {case(a,b) => b-a}

This can be generalised by dropping any number n so that we subtract the first and the n-th elements, then the second and the n+1-th elements, and so forth.

Related