Folding by case classes

I have a situation where I have a couple of case classes where all of their variables are optional.

Let's say I have:

case class Size(width: Option[Int], height: Option[Int])
case class Foo(a: Option[String], b: Option[Boolean], c: Option[Char])

Given a collection of the same type of case class, I would like to add them up by comparing the option values ​​and preserving the values ​​that are defined. That is, for Size:

values.foldLeft(x) { (a, b) =>
  Size(a.width.orElse(b.width), a.height.orElse(b.height))
}

I would like to do this in a more general way for any of the case classes similar to the ones above. I am thinking of doing something with help unapply(_).get, etc. Does anyone know a smart way to solve this problem?

+3
source share
3 answers

Ok, consider the following:

def foldCase[C,T1](unapply: C => Option[Option[T1]], apply: Option[T1] => C)
  (coll: Seq[C]): C = {
  coll.tail.foldLeft(coll.head) { case (current, next) =>
    apply(unapply(current).get orElse unapply(next).get)
  }
}

case class Person(name: Option[String])

foldCase(Person.unapply, Person.apply)(List(Person(None), Person(Some("Joe")), Person(Some("Mary"))))

foldCase, , , f arity. case. , , . , .

def foldCase[C,T1,T2](unapply: C => Option[(Option[T1], Option[T2])], apply: (Option[T1], Option[T2]) => C)
  (coll: Seq[C]): C = {
  def thisOrElse(current: (Option[T1], Option[T2]), next: (Option[T1], Option[T2])) =
    apply(current._1 orElse next._1, current._2 orElse next._2)
  coll.tail.foldLeft(coll.head) { case (current, next) =>
    thisOrElse(unapply(current).get, unapply(next).get)
  }
}

val list = Person(None, None) :: Person(Some("Joe"), None) :: Person(None, Some(20)) :: Person(Some("Mary"), Some(25)) :: Nil

def foldPerson = foldCase(Person.unapply, Person.apply) _

foldPerson(list)

, :

object Folder {
  def foldCase[C,T1](unapply: C => Option[Option[T1]], apply: Option[T1] => C)
    (coll: Seq[C]): C = {
    coll.tail.foldLeft(coll.head) { case (current, next) =>
      apply(unapply(current).get orElse unapply(next).get)
    }
  }

  def foldCase[C,T1,T2](unapply: C => Option[(Option[T1], Option[T2])], apply: (Option[T1], Option[T2]) => C)
    (coll: Seq[C]): C = {
    def thisOrElse(current: (Option[T1], Option[T2]), next: (Option[T1], Option[T2])) =
      apply(current._1 orElse next._1, current._2 orElse next._2)
    coll.tail.foldLeft(coll.head) { case (current, next) =>
      thisOrElse(unapply(current).get, unapply(next).get)
    }
  }
}

, apply unapply :

case class Question(answer: Option[Boolean])
val list2 = List(Question(None), Question(Some(true)), Question(Some(false)))
Folder.foldCase(Question.unapply _, Question.apply _)(list2)

, , -, . # scala , , , .

+2

[ ]

, "arity":

abstract class Foldable2[A,B](val a:Option[A], val b:Option[B]) {
  def orElse[F <: Foldable2[A,B]](that: F)(implicit ev: this.type <:< F) = 
    getClass.getConstructor(classOf[Option[A]], classOf[Option[B]]).newInstance(
      this.a.orElse(that.a), this.b.orElse(that.b)
    )
}

case class Size(w: Option[Int], h: Option[Int]) extends Foldable2(w, h) 

println(Size(Some(1),None).orElse(Size(Some(2),Some(42))))
//--> Size(Some(1),Some(42))

, <:< , case .

" " , .

+2

You can use productElementor productIterator(in scala.Product ) to retrieve / repeat the elements of the case classes (and tuples), but they are printed as "Any", so there will be some pain.

+1
source

Source: https://habr.com/ru/post/1776980/


All Articles