4

I am trying to model (in my Scala application) a list of options presented in my web page and am scratching my head coming up with a solution for mapping a String value posted from the client to it's corresponding object in the backend.

eg. Let's say it is a list of Animals and the user can choose 1 which gets posted to the backend.

Animals
   Polar Bear
   Rabbit
   Great White Shark

When a request comes in, I want to convert the Great White Shark String to an Animal but not sure on how best to match the String to the appropriate type in the backend.

So far I have this.

sealed abstract class Animal(val name: String)

case object GreatWhite extends Animal("Great White Shark")
case object PolarBear extends Animal("Polar Bear")

Which allows me to do this to match the String from the UI to it's corresponding case object in my Scala application.

def matcher(animal: String) = animal match {
    case GreatWhite.name => GreatWhite
    case PolarBear.name => PolarBear
}

Problem

If the List of Animal's grows long however, this matcher is going to be very cumbersome since I need to have a case expression for every Animal.

I would much appreciate any experienced Scala guys giving me a pointer on a more elegant solution.

2 Answers2

6

It's looks like what you need is simply have a hash table of String to Animal. Such approach gives you ability to get result in constant time O(1) even with extensivly growing list.

val mapping = Map[String, Animal]("Rabbit" -> Rabbit, "Polar Bear" -> PolarBear /* ... */ )

// matcher
mapping.get(animal)

UPD. Some useful comments below.

  sealed abstract class Animal(val name: String)

  case object GreatWhite extends Animal("Great White Shark")
  case object PolarBear extends Animal("Polar Bear")

  val mapping: Map[String, Animal] = Seq(GreatWhite, PolarBear).map(x => x.name -> x).toMap
  mapping
vvg
  • 6,325
  • 19
  • 36
  • 1
    To avoid typos, maybe `Rabbit.name -> Rabbit, PolarBear.name -> PolarBear` instead of replicated string literals. – Thilo May 05 '16 at 09:31
  • 1
    Is there a way to do `getAllSealedClassObjects.map( _.name -> name)` ? – Thilo May 05 '16 at 09:32
  • Thanks - I like that, and thanks for the O notation note. I may go with this but let me see what other solutions come in before I mark it as answer. – IShaggedTeraPatrick May 05 '16 at 09:33
  • If there is no getAllObjects then maybe `val mapping = Seq(Rabbit, PolarBear).map( _.name -> name)` – Thilo May 05 '16 at 09:36
  • 1
    @Thilo looks like yes: http://stackoverflow.com/questions/12078366/can-i-get-a-compile-time-list-of-all-of-the-case-objects-which-derive-from-a-sea – vvg May 05 '16 at 09:36
  • @Thilo, can you help me out understanding your latter suggestion please? `val mapping = Seq(Rabbit, PolarBear).map( _.name -> name)` This generates a new Map with the mapping for the `name` to -> the Object right? I'm currently getting not recognized for the last `name` though so I guess the bit of you example that I need to finish I dont quite understand. – IShaggedTeraPatrick May 05 '16 at 10:12
  • 1
    Seq(GreatWhite, PolarBear).map(x => x.name -> x) – vvg May 05 '16 at 10:17
  • @rumoku, thanks for that but I don't get how the resulting List of Sequences is useful since to match I need to do something like this right? `mapping.find(_ == (animal, GreatWhite))` ie. I need to explicitly state the Type of Animal I'm looking for. No doubt it's cause I'm a Scala newbie. – IShaggedTeraPatrick May 05 '16 at 10:56
0

Have you looked at Enums? If they are usable for you, Enums have a .withName method http://yefremov.net/blog/scala-enum-by-name/

pedrorijo91
  • 7,635
  • 9
  • 44
  • 82