Oliver Schonrock - 5 months ago 21

Scala Question

Here is the challenge (Scala 2.11):

`(for (i <- -3 to 3) yield (f(i), i)).toMap`

f(): Int => Int is an unknown argument here, which is passed into my code. The resulting map will be the "inverse" of the function over the bounded space.

This will work fine if

`f(i: Int) = i * 2`

but it will

`f(i: Int) = i * i`

because, eg f(-2) = 4 and f(2) = 4 which makes duplicate keys and the second overwrites the first

My question is: How can I throw and IllegalArgumentException when there are duplicate keys...(ie telling the user that his/her function is not "invertable" over the bounded space)

I know I could use a mutable.Map and write a loop, as opposed to a for comprehension (eg using tail recursion), which does a "map.get" on each iteration and if already exists, throw...., otherwise add to map.

Is there an immutable way of doing it?

For extra points I also need to check that f(i) is within the bounds (-3..3) and ignore if not...

Answer

Breaking it down into smaller steps, it becomes clear that the for-comprehension returns a vector. `toMap`

is what creates the map. however, in your scenario `foldLeft`

is more appropriate than `toMap`

.

```
val f = (i:Int) => i * i
val pairs = for (i <- -3 to 3) yield f(i) -> i
val result = pairs.foldLeft(Map.empty[Int, Int]) { case (map, (k, v)) =>
if(map contains k)
map //or throw IllegalArgumentException here
else
map + (k -> v)
}
println(result)
```

here's a bit more stepped out (but functionally identical) answer:

```
val f = (i:Int) => i * i
val pairs = for (i <- -3 to 3) yield f(i) -> i
val zero = Map.empty[Int, Int]
def putIfAbsent(map:Map[Int, Int], pair:(Int, Int)):Map[Int, Int] =
pair match { case (k, v) =>
if(map contains k)
map
else
map + (k -> v)
}
val result = pairs.foldLeft(zero)(putIfAbsent)
println(result)
```