Luis
Luis

Reputation: 1465

how to map many strings to integer values in Scala?

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

Upvotes: 0

Views: 157

Answers (3)

B. D.
B. D.

Reputation: 11

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"

Upvotes: 1

Tim
Tim

Reputation: 27356

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.

Upvotes: 3

Mateusz Kubuszok
Mateusz Kubuszok

Reputation: 27545

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

Upvotes: 3

Related Questions