JohnBigs
JohnBigs

Reputation: 2811

What would be a more elegant way of using case class as accumulator in foldLeft?

I have a case class:

case class Nums(num1: Int, num2: Int)

and a list as follows:

val listOfNums = List(Nums(1,1), Nums(2,2))

I want to return Nums(3,3) which is the sum of all the relevant fields with each other. The way I achieved this was:

listOfNums.foldLeft(Nums(0,0))((acc,nums) => {
  acc.copy(
    num1 = acc.num1 + nums.num1,
    num2 = acc.num2 + nums.num2
  )
})

But this feels a bit clumsy, what would be the right way?

Upvotes: 0

Views: 643

Answers (2)

If you are open to using cats.
Then, you can simply define a Monoid for nums so you only need to write the boilerplate once, and then reuse it in multiple parts by using auxiliary functions like combineAll or foldMonoid

import cats.kernel.Monoid
final case class Nums(num1: Int, num2: Int)
object Nums {
  implicit final val NumsMonoid: Monoid[Nums] =
    new Monoid[Nums] {
      override final val empty: Nums =
        Nums(num1 = 0, num2 = 0)
      
      override def combine(n1: Nums, n2: Nums): Nums =
        Nums(num1 = n1.num1 + n2.num1, num2 = n1.num2 + n2.num2)
    }
}

Which then can be used like:

import cats.syntax.all._
val listOfNums = List(Nums(1,1), Nums(2,2))
val result = listOfNums.combineAll
// result: Nums = Nums(3, 3)

You can see the code running here.

Upvotes: 1

Tomer Shetah
Tomer Shetah

Reputation: 8529

Actually what you are looking for is Reduce, and not fold. You can try:

listOfNums.reduce((num1, num2) => Nums(num1.num1 + num2.num1, num1.num2 + num2.num2))

If you do want to use fold, you don't have to copy, you can just create a new Nums:

listOfNums.foldLeft(Nums(0,0))((acc,nums) => {
  Nums(
    num1 = acc.num1 + nums.num1,
    num2 = acc.num2 + nums.num2
  )
})

You can further read about difference between foldLeft and reduceLeft in Scala.

Code run can be found at Scastie.

As @Thilo mentioned in the comment, you can add a + to Nums:

case class Nums(num1: Int, num2: Int) {
  def +(that: Nums): Nums = {
    Nums(num1 + that.num1, num2 + that.num2)
  }
}

And then the fold and reduce usage become really obviuos:

listOfNums.reduce(_ + _)
listOfNums.foldLeft(Nums(0,0))(_ + _)

Code run for that in another Scastie

Upvotes: 4

Related Questions