DarqMoth
DarqMoth

Reputation: 603

Scala: Map keys with wildcards?

Is it possible to use keys with wildcards for Scala Maps? For example tuples of the form (x,_)? Example:

scala> val t1 = ("x","y")
scala> val t2 = ("y","x")
scala> val m = Map(t1 -> "foo", t2 -> "foo")

scala> m(("x","y"))
res5: String = foo

scala> m(("x",_))
<console>:11: error: missing parameter type for expanded function ((x$1) => scala.Tuple2("x", x$1))
              m(("x",_))
                     ^

It would be great if there was a way to retrieve all (composite_key, value) pares where only some part of composite key is defined. Other ways to get the same functionality in Scala?

Upvotes: 2

Views: 1795

Answers (4)

wingedsubmariner
wingedsubmariner

Reputation: 13667

Think about what is happening under the hood of the Map. The default Map in Scala is scala.collection.immutable.HashMap, which stores things based on their hash codes. Do ("x", "y") and ("x", "y2") have hash codes that relate to each other in anyway? No, they don't, and their is no efficient way to implement wildcards with this map. The other answers provide solutions, but these will iterate over key/value pair in the entire Map, which is not efficient.

If you expect you are going to want to do operations like this, use a TreeMap. This doesn't use a hash table internally, put instead puts elements into a tree based on an ordering. This is similar to the way a relational database uses B-Trees for its indices. Your wildcard query is like using a two-column index to filter on the first column in the index.

Here is an example:

import scala.collection.immutable.TreeMap

val t1 = ("x","y")
val t2 = ("x","y2")
val t3 = ("y","x")
val m = TreeMap(t1 -> "foo1", t2 -> "foo2", t3 -> "foo3")

// "" is < than all other strings
// "x\u0000" is the next > string after "x"
val submap = m.from(("x", "")).to(("x\u0000", ""))

submap.values.foreach(println) // prints foo1, foo2

Upvotes: 0

elm
elm

Reputation: 20405

Using comprehensions like this:

for ( a @ ((k1,k2), v) <- m  if k1 == "x" ) yield a

Upvotes: 1

Ashalynd
Ashalynd

Reputation: 12563

In general, you can do something like

m.filter(m => (m._1 == "x"))

but in your particular example it will still return only one result, because a Map has only one entry per key. If your key itself is composite then it will indeed make more sense:

scala>  Map((1,2)->"a", (1,3)->"b", (3,4)->"c").filter(m => (m._1._1 == 1))
res0: scala.collection.immutable.Map[(Int, Int),String] = Map((1,2) -> a, (1,3) -> b)

Upvotes: 1

jilen
jilen

Reputation: 5763

How about use collect

Map( 1 -> "1" -> "11", 2 -> "2" -> "22").collect { case (k@(1, _), v ) => k -> v }

Upvotes: 2

Related Questions