user468587
user468587

Reputation: 5031

scala map get the value by key with key case insensitive

I have a map of Map[String, Info], it contains keys which can be either uppercase or lowercase, like this:

person1: PersonInfo1
person2: PersonInfo2
PERSON1: PersonInfo1

i want to get the value for key 'person1', if nothing found I'll try with key of 'PERSON1', I tried this code:

val info = map.get(key) match {
  case Some(personInfo) => personInfo
  case None =>
    map.get(key.toUpperCase()) match {
      case Some(personInfo) => personInfo
      case None             => None
    }
}

but this return info as type of Product with Serializable, how can I have info returned as type of PersonInfo? Is there a way in Scala that allow me to get value from map by key and ignore cases of the key?

Upvotes: 5

Views: 3909

Answers (4)

Anoukh
Anoukh

Reputation: 113

You can do a find instead of a get but you may want to consider performance when doing this.

map.find(k => k._1.equalsIgnoreCase(key)) match {
  case Some =>
  case None =>
}

Upvotes: 4

Krzysztof Atłasik
Krzysztof Atłasik

Reputation: 22595

You could chain gets with orElse. I would create an extension method for that:

implicit class CaseInsensitiveGetMap[V] (m: Map[String,V]) {
   def iget (key: String): Option[V] = m.get(key)
      .orElse(m.get(key.toUpperCase())) //you can add more orElse in chain
}

Then you can just use it like:

map.iget("person2")

Upvotes: 7

jwvh
jwvh

Reputation: 51271

The reason you're getting Product with Serializable is because your code is trying to return either a String (if key is good) or an Option (i.e. None if key not found). Those two types are not compatible. You should decide if you want String (maybe an empty string if key not found) or Option (i.e. Some[String] or None).

See if this works for you. It returns an Option[String].

map.get(key).fold(pm.get(key.toUpperCase))(Some(_))

The 1st get() returns an Option. The fold()() unwraps the Option and either tries a 2nd get() with upper case key value, or, if the 1st get returned a value, the value is re-wrapped in an Option so that the types match up.

If, on the other hand, you want to return a String instead, you might do this.

map.getOrElse(key, pm.getOrElse(key.toUpperCase, ""))

Upvotes: 4

Gábor Bakos
Gábor Bakos

Reputation: 9100

There are comparators for sorted maps which allow getting from the map case insensitively. Example: https://scastie.scala-lang.org/PfHTh16CROag7PNrknx1sQ

val map = scala.collection.immutable.SortedMap("key1" -> 45, "Key2" -> 43, "KEY3" -> 42)(scala.math.Ordering.comparatorToOrdering(String.CASE_INSENSITIVE_ORDER))
map.get("key1") // Some(45)
map.get("key2") // Some(43)
map.get("key3") // Some(42)
map.get("key4") // None

Your actual problem can be fixed if you return Options on all cases, for example:

      val info = map.get(key) match {
        case somePi@Some(personInfo) => somePi
        case None => map.get(key.toUpperCase()) match {
          case Some(personInfo) => Some(personInfo)
          case None => None
        }
      }

Note the somePi@ => somePi parts for referring the expression or the Some(personInfo).

Probably worth explaining why you got this error message. I assume personInfo is a case class which implements Product and Serializable, just like None. The common type for them is Product with Serializable.

Upvotes: 8

Related Questions