Jack
Jack

Reputation: 16718

Map with only certain keys

With regard to maps in Scala, if ms - (k, 1, m) returns the map containing all mappings of ms except for any mapping with the given keys, x, 1 and m.

Then, what statement will return a map of all mappings of ms with only the given keys, x, 1 and m. i.e. I'm looking for the subset of ms where only k, 1 and m are keys.

This works, but it is terrible:

scala> val originalMap = Map("age" -> "20", "name" -> "jack", "hobby" -> "jumping")
ms: scala.collection.immutable.Map[java.lang.String,java.lang.String] = Map(age -> 20, name -> jack, hobby -> jumping)

scala> val interestingKeys = List("name", "hobby")
interesting: List[java.lang.String] = List(name, hobby)

scala> val notInterestingMap = originalMap -- interestingKeys
notInterestingMap: scala.collection.immutable.Map[java.lang.String,java.lang.String] = Map(age -> 20)

scala> val interestingMap = originalMap -- notInterestingMap.keySet
interestingMap: scala.collection.immutable.Map[java.lang.String,java.lang.String] = Map(name -> jack, hobby -> jumping)

Upvotes: 8

Views: 4445

Answers (3)

Suma
Suma

Reputation: 34403

I think the original code is not that bad, and it can be easily transformed into a one-liner operating on key sets:

val interestingMap = originalMap -- (originalMap.keySet -- interestingKeys)

I find this quite readable.

Upvotes: 2

Travis Brown
Travis Brown

Reputation: 139038

Because filterKeys filters based on an arbitrary predicate, it has to consider every key in the map. This might be fine or not, depending on how large the map is, etc., but it's definitely not necessary for the operation you describe. I'd use something like the following:

interestingKeys.flatMap(k => originalMap.get(k).map((k, _))).toMap

This will be O(n) or O(n log m) depending on your map implementation (where n is the size of interestingKeys and m is the size of the map), instead of O(m log n) or O(mn).

If you really want your ~ operator, you can use the pimp-my-library pattern:

class RichMap[A, B](m: Map[A, B]) {
  def ~(ks: A*) = ks.flatMap(k => m.get(k).map((k, _))).toMap
}

implicit def enrichMap[A, B](m: Map[A, B]) = new RichMap(m)

Now originalMap ~ ("name", "hobby") returns Map(name -> jack, hobby -> jumping), as you'd expect.

Upvotes: 7

Nicolas
Nicolas

Reputation: 24769

filterKeyscan help:

scala> originalMap.filterKeys(interestingKeys.contains)
res0: scala.collection.immutable.Map[java.lang.String,java.lang.String] = Map(name -> jack, hobby -> jumping)

Upvotes: 10

Related Questions