I'm new to Scala and trying to use Scala 3 enums as ADTs, and I have a collection I'm trying to "reduce" down in the following way:
enum Stats:
case MaximumHealth(val x: Float)
case HealthRegen(val x: Float)
def combineStats: Array[Stats] =
val startWith = Array(Stats.MaximumHealth(20), Stats.MaximumHealth(10), Stats.HealthRegen(5))
// how can I iterate/recurse over 'startWith' and "combine" multiple stats
// (such as MaximumHealth) entries into one?
// (ie, producing endWith below)?
val endWith = Array(Stats.MaximumHealth(30), Stats.HealthRegen(5))
endWith
How can I do this? GroupBy is often suggested but I don't know how to group by just the enum case without the values of the parameters making Stats.MaximumHealth(20) and Stats.MaximumHealth(10) different groups.
This seems like a case for groupMapReduce, but first, I would prepare some methods on your enum to make life easier:
enum Stats:
// override is needed because of def x below
case MaximumHealth(override val x: Float)
case HealthRegen(override val x: Float)
// the common getter for x
def x: Float
// combinator function for reducing
def +(that: Stats): Stats = this match
case MaximumHealth(x) => MaximumHealth(x + that.x)
case HealthRegen(x) => HealthRegen(x + that.x)
Now, we can do this:
def combineStats: Array[Stats] =
val startWith = Array(Stats.MaximumHealth(20), Stats.MaximumHealth(10), Stats.HealthRegen(5))
val reduced: Map[Int, Stats] =
startWith.groupMapReduce(_.ordinal)(identity)(_ + _)
val endWith = reduced.values.toArray
endWith
Alternative solution
In above solution, I tried to keep your data structur as unchanged as possible. If I were to face that task, I would probably change it like this:
enum StatsType:
case MaximumHealth, HealthRegen
case class Stats(statsType: StatsType, x: Float)
Then one could do it like this:
def combineStats(stats: List[Stats]): List[Stats] =
val reduced: Map[StatsType, Float] =
stats.groupMapReduce(_.statsType)(_.x)(_ + _)
reduced.map{case (statsType, x) => Stats(statsType, x)}.toList
Additional hint
Not directly related to your question, but: if you want to keep your data structure as an enum as in your question, I'd consider making the x
a member of the enum itself (as a simpler solution for a "common getter"):
enum Stats(val value: Float):
case MaximumHealth(x: Float) extends Stats(x)
case HealthRegen(x: Float) extends Stats(x)