-1

I've below Map

Object DataMapping
 val myMap = Map(DataGroup.REAL-> SEQ(A,B,C),
                 DataGroup.PEAL-> SEQ(H,J,K,L,M),
                 DataGroup.CEAL-> SEQ(X,Y))

And this snippet is being used to find in above map. Struggling to understand what's _._2 and _._1 is doing in this code, how its behaving and what will be the output for this ?

DataMapping.myMap.find(_._2.contains(Y)).map(_._1).get

I'm new in scala and tried reading online too but couldn't understand. Can anyone please share ?

Julie
  • 9
  • 4
  • 1
    [This here](https://stackoverflow.com/a/8001132/2707792) contains answers to both parts of your question. You're looking for "Anonymous function placeholder parameter" and "Part of a method name, such as tuple getters". – Andrey Tyukin Jul 27 '22 at 18:25

1 Answers1

5

The two underscores mean different things. First off, a bare underscore as part of a function argument will get turned into a lambda expression (see What are all the uses of an underscore in Scala). So your code

DataMapping.myMap.find(_._2.contains(Y)).map(_._1).get

is equivalent to

DataMapping.myMap.find(x => x._2.contains(Y)).map(x => x._1).get

Now, all of the sequencing functions, such as find and map, act on 2-tuples (K, V) when applied to a Map[K, V]. We're iterating over pairs, where the first element of the pair is the key and the second is the value. In Scala, we can access the Nth element (1-indexed) of a tuple with _N, so if we have a pair val myPair = ("a", "b"), then myPair._1 is "a" and myPair._2 is "b".

The .find call is checking whether the value (i.e. the second element of the pair) contains Y. .find returns an Option[(K, V)], since we were iterating over a sequence of (K, V). Now Option is a container like any other, just with the restriction that it can contain at most one element. So Option.map has the net effect of applying a function to the inside of the Option (if it exists) and doing nothing if given None. In our case, we've got an Option[(K, V)] and we just care about the first part, so we want an Option[K]. If there's something in the Option, we do x => x._1 to get the first element of that tuple. If there's None, then we just keep the value as None.

Option.get is an assertion that the Option is non-empty. It throws an exception if that's wrong.

In summary, that code takes the map DataMapping.myMap, finds the first element of your mapping whose value contains Y, and then gets the corresponding key, throwing an exception if we can't find any such element.

Since we do a .get right after, the .map is sort of pointless in this case. I would probably have written this code as

DataMapping.myMap.find(_._2.contains(Y)).get._1

And, personally, I'm not super fond of the _N syntax to begin with, so I might have gone one step further.

val (key, _) = DataMapping.myMap.find((_, v) => v.contains(Y)).get

or, in Scala 2 (before we had automatic case destructuring)

val (key, _) = DataMapping.myMap.find { case (_, v) => v.contains(Y) }.get
Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
  • x._2 is value and x._1 key of Map ? – Julie Jul 27 '22 at 18:29
  • 2
    It probably should be noted that the `find((_, v) => ...)` part works in 3.x, but wouldn't work in 2.x, you'd need a `{ case (_, v) => ... }` there. Also, `get` is a code smell; If you want to process the `Option` properly, you probably want to handle it in a `for-yield`, so you can do the pattern-matching inside of the `for` as well. – Andrey Tyukin Jul 27 '22 at 18:30
  • 1
    @AndreyTyukin Oops! I've been spoiled by Scala 3 for so long now I've already forgotten what it used to be. Thanks! – Silvio Mayolo Jul 27 '22 at 18:41