0

I have the following use case. User is providing two kinds of strings type and growth I need to convert the value of each into an integer from a known map and return the sum of both value.

What I did to map the strings is:

object Types extends Enumeration {

  val T1 = 5
  val T2 = 6
  // list of values is really long!

  val TYPE1 = "type1"
  val TYPE2 = "type2"
}


object Growth extends Enumeration {

  val G1 = 1
  val G2 = 10
  // list of values is really long!

  val GROWTH1 = "growth1"
  val GROWTH2 = "growth2"
}

Now consider user is calling sum("type1", "growth2") the expected output is 15 because 5+10=15

I wrote this skeleton function but I'm having trouble utilizing the enumeration objects. How can I do this without having so many if/else?

   def sum(type: String, growth: String) : Int = {
      var sum:Int = 0
      ????
      return sum
   }

if my approach is flawed I'm happy to try another

5
  • I'd recommend using Scala 3 enums or libs like enumeratum. Scala 2's Enumeration are broken. Commented Jan 16, 2023 at 20:22
  • 3
    Can you just create a single Map[String, Int] that gives the value for each of the possible strings? Then a simple foldLeft could be used to compute the total for a list of strings. Commented Jan 16, 2023 at 20:30
  • @GaëlJ I'm on Scala 2 so it's not an option Commented Jan 16, 2023 at 20:34
  • @Tim that is an option but I'm still not clear on how to map the string into the matching integer value Commented Jan 16, 2023 at 20:36
  • Just use Map. Enumeration is kinda broken in scala 2, and isn't the right tool for this use case anyhow. Commented Jan 17, 2023 at 13:58

3 Answers 3

3

scala.Enumeration doesn't let you use syntax like this

object MyEnum extends Enumeration { 
  val value = "string"
}

It actually requires you to do something more like

object MyEnum extends Enumeration {
  val value = Value("string")
}

and then you are passing not String but MyEnum.Value.

Since you are giving up on passing around Strings (at least in Scala 2, in Scala 3 you have an option to use type MyEnum = "value1" | "value2" and/or opaque type MyEnum), you create a sealed hierarchy instead

sealed abstract class Type(
  val name: String,
  val number: Int
) extends Product with Serializable
object Type {
  case object Type1 extends Type("type1", 5)
  case object Type2 extends Type("type2", 6)
}

sealed abstract class Growth(
  val name: String,
  val number: Int
) extends Product with Serializable
object Growth {
  case object Growth1 extends Growth("growth1", 1)
  case object Growth2 extends Growth("growth2", 10)
}

def sum(`type`: Type, growth: Growth) : Int =
  `type`.number + growth.number

If you needed to construct a collection of all possible values, instead of scala.Enumeration you can use Enumeratum library instead

import enumeratum.values._

// IntEnumEntry and IntEnum specialize for Int
// and it allows to eliminate boxing

// The Int has to be defined as `val value: Int`

// findValues has to called so that the compiler
// can find all instances in the compile-time and
// populate the `values` collection

// Enumeratum then generate a lot of useful methods out of
// this list e.g. `withName` to find enum by its name

sealed abstract class Type(
  val value: Int
) extends IntEnumEntry
object Type extends IntEnum[Type] {
  case object Type1 extends Type(5)
  case object Type2 extends Type(6)

  val values = findValues
}

sealed abstract class Growth(
  val value: Int
) extends IntEnumEntry
object Growth extends IntEnum[Growth] {
  case object Growth1 extends Growth(1)
  case object Growth2 extends Growth(10)

  val values = findValues
}

def sum(`type`: Type, growth: Growth) : Int =
  `type`.value + growth.value
Sign up to request clarification or add additional context in comments.

Comments

3

This can be done by create a Map that gives the score for each String:

val scoreMap = Map(
  "type1" -> 5,
  "type2" -> 6,
  "growth1" -> 1,
  "growth2" -> 10,
)

The score can then be computed using foldLeft:

def score(strings: List[String]) =
    strings.foldLeft(0){ case (sum, str) => sum + scoreMap.getOrElse(str, 0) }

score(List("type1", "growth2")) // 15

Values that are missing from the Map have the default value 0.

2 Comments

A fancier way to compute score: add .withDefault(_ => 0) to scoreMap definition, and then: def score(strings: List[String]) = strings.iterator.map(scoreMap).sum
this is really nice but it doesn't preserve the function signature I have def sum(type: String, growth: String) which is important. I guess it can be done by split he scoreMap
1

You can extend the class Val of Enumeration class.

Example:

object Types extends Enumeration {
    final case class Pairing (label: String, value: Int) extends Val (value, label)

    val TYPE1 = Pairing ("type1", 5)
    val TYPE2 = Pairing ("type2", 6)
}

object Growths extends Enumeration {
    final case class Pairing (label: String, value: Int) extends Val (value, label)

    val GROWTH1 = Pairing ("growth1", 1)
    val GROWTH2 = Pairing ("growth2", 10)
}

type Type = Types.Pairing
type Growth = Growths.Pairing

def sum(t: Type, g: Growth) = t.id + g.id

// usage:
import Types._
import Growths._

println(sum(TYPE1, GROWTH2)) // => 15

If you wish to use the operator + and reuse the val names :

object Types extends Enumeration {
    final case class Entry (value: Int) extends Val (value) {
        def +(g: Growth) = id + g.id
    }

    val type1 = Entry (5)
    val type2 = Entry (6)
}

object Growths extends Enumeration {
    final case class Entry (value: Int) extends Val (value) {
        def +(t: Type) = t.id + id
    }

    val growth1 = Entry (1)
    val growth2 = Entry (10)
}

type Type = Types.Entry
type Growth = Growths.Entry

// usage:
import Types._
import Growths._

println(type1 + growth2) // => 15
println(growth1 + type2) // => 7

println(growth1) // => "growth1"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.