Rafaó
Rafaó

Reputation: 599

How to use Map[List[String], String]?

I have a map:

val m: Map[List[String], String] = Map (
    List("banana", "melon", "apple") -> "fruit",
    List("chair", "table", "wardrobe") -> "furniture")

How to check, what is e.g. banana? This ofc doesn't work:

scala> m("banana")

I could split the lists into single entries String -> String, but there are lots of them, so I prefer List[String] -> String.

Upvotes: 0

Views: 123

Answers (4)

Zoltán
Zoltán

Reputation: 22206

I could split the lists into single entries String -> String, but there are lots of them, so I prefer List[String] -> String.

That doesn't sound like a very good reason to me. The whole point of using a map is easy and quick access to values. Using a list as the key type when you'll be looking up values by elements of that list just makes it hard to use the map, while giving almost no advantages.

I suggest you "flatten" your map, so one key maps to one value with possibly duplicate values:

val m: Map[List[String], String] = Map(
  List("banana", "melon", "apple") -> "fruit",
  List("chair", "table", "wardrobe") -> "furniture"
)

val flattenedMap = m.flatMap { case (keys, value) =>
  keys.map(_ -> value)
}

flattenedMap("banana")

If you're not yet comfortable with using flatMap, you can use a for-comprehension instead, which works very nicely on this example:

val m: Map[List[String], String] = Map(
  List("banana", "melon", "apple") -> "fruit",
  List("chair", "table", "wardrobe") -> "furniture"
)

val flattenedMap: Map[String, String] = for {
  (keys, value) <- m // for each list of keys and value in your map
  key <- keys        // and for each key from the list of keys
} yield (key, value) // create a pair of key and value 

This yields the same result as the example with flatMap above.

Or you could just initialize your Map as a "flattened" map immediately:

val flattenedMap: Map[String, String] = Map(
  "banana" -> "fruit",
  "melon" -> "fruit",
  "apple" -> "fruit",
  "chair" -> "furniture",
  "table" -> "furniture",
  "wardrobe" -> "furniture"
)

flattenedMap("banana")

Upvotes: 2

bobah
bobah

Reputation: 18864

If it's a one-off lookup, then any lazy (the one that does not create unnecessary container copies) combo would do (I normalized them all to return Option):

m.withFilter(_._1.contains("banana")).map(_._2).headOption
// or
m.find(_._1.contains("banana")).map(_._2)
// or
(for ((k, v) <- m.toStream if k.contains("banana")) yield v).headOption

Otherwise, if this query needs to run often you might want to change this data structure for faster lookups with

val mm: immutable.Map[String, String] = m.flatMap{ case (k,v) => k.map(_ -> v)}
...
print(mm["banana"])

Upvotes: 0

Arcturus
Arcturus

Reputation: 27065

You could also use this:

 map.keys.flatten.exists(s => s == "banana")

The flatten maps all lists of lists to a single list. exists checks if banana is in that list.

Upvotes: 0

Rafa&#243;
Rafa&#243;

Reputation: 599

Ok, got it. I can use foreach to check every key:

m foreach (x => println(x._1.contains("banana")))

Upvotes: 0

Related Questions