Search code examples
scalafunctional-programmingsliding

Average for adjacent items in scala


I have a seq

val seq = Seq(1, 9, 5, 4, 3, 5, 5, 5, 8, 2)

I want to get an average for each adjacent (left and right) numbers, meaning in the above example to have the following calculations: [(1+9)/2, (1+9+5)/3, (9+5+4)/3, (5+4+3)/3, (4+3+5)/3, (3+5+5)/3, (5+5+5)/3, (5+5+8)/3, (5+8+2)/3, (8+2)/2]

The other examples are:

Seq() shouldBe Seq()
Seq(3) shouldBe Seq(3.0d)
Seq(1, 4) shouldBe Seq(2.5d, 2.5d)
Seq(1, 9, 5, 4, 3, 5, 5, 5, 8, 2) shouldBe Seq(5.0, 5.0, 6.0, 4.0, 4.0, 13.0 / 3, 5.0, 6.0, 5.0, 5.0)

I was able to get: numbers.sliding(2, 1).map(nums => nums.sum.toDouble / nums.length).toSeq. But it doesn't consider the previous value.
I tried to do it with foldLeft - it is also cumbersome.

Is there an easy way to do this? What am I missing?


Solution

  • Being honest, this is the kind of problems that I believe are easier to solve using a simple (albeit a bit long) tail-recursive algorithm.

    def adjacentAverage(data: List[Int]): List[Double] = {
      @annotation.tailrec
      def loop(remaining: List[Int], acc: List[Double], previous: Int): List[Double] =
        remaining match {
          case x :: y :: xs =>
            loop(
              remaining = y :: xs,
              ((previous + x + y).toDouble / 3.0d) :: acc,
              previous = x
            )
      
          case x :: Nil =>
            (((previous + x).toDouble / 2.0d) :: acc).reverse
        }
    
      data match {
        case x :: y :: xs => loop(remaining = y :: xs, acc = ((x + y).toDouble / 2.0d) :: Nil, previous = x)
        case x :: Nil     => x.toDouble :: Nil
        case Nil          => Nil
      }
    }
    

    You can see it running here.