Scala - How to identify a map where the value depends on the key?

Is there a way to define a card where the value of the card depends on its key, for example

Map(key -> f(key), key2 -> f(key2), ...). 
+6
source share
4 answers

Suppose you have your keys in a list like this, and you want to convert it with squares as values.

 scala> val keyList = ( 1 to 10 ).toList keyList: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) scala> val doSquare = ( x: Int ) => x * x doSquare: Int => Int = <function1> // Convert it to the list of tuples - ( key, doSquare( key ) ) scala> val tupleList = keyList.map( key => ( key, doSquare( key ) ) ) tuple: List[(Int, Int)] = List((1,1), (2,4), (3,9), (4,16), (5,25), (6,36), (7,49), (8,64), (9,81), (10,100)) val keyMap = tuple.toMap keyMap: scala.collection.immutable.Map[Int,Int] = Map(5 -> 25, 10 -> 100, 1 -> 1, 6 -> 36, 9 -> 81, 2 -> 4, 7 -> 49, 3 -> 9, 8 -> 64, 4 -> 16) 

Or do it in one line

 ( 1 to 10 ).toList.map( x => ( x, x * x ) ).toMap 

Or ... if you have only a few keys ... then you can write specific code

 Map( 1 -> doSquare( 1 ), 2 -> doSquare( 2 ) ) 
+4
source

You look at it wrong ...

A Map[K,V] also an instance of Partialfunction[K,V] . Therefore, wherever you use the Map type (vals, params method, etc.), you change the PartialFunction .

Then you can directly work with f or supply Map[K,V] as an instance wherever you have a simple algebraic relationship between keys and values.

eg.

 def methodUsingMapping(x: PartialFunction[Int,Boolean]) = ... //then val myMap = Map(1->true, 2->true, 3->false) methodUsingMapping(myMap) //or val isEven = PartialFunction(n: Int => n % 2 == 0) methodUsingMapping(isEven) //or //note: case statements in a block is the smart way // to define a partial function // In this version, the result isn't even defined for odd numbers val isEven: PartialFunction[Int,Boolean] = { case n: Int if n % 2 == 0 => true } methodUsingMapping(isEven) 

You can also consider using (K) => Option[V] , in which case you can provide an instance of a type using the lift method, which displays inheritance from PartialFunction

eg.

 def methodUsingMapping(x: (Int)=>Option[Boolean]) = ... //then val myMap = Map(1->true, 2->true, 3->false) methodUsingMapping(myMap.lift) //or def isEven(n: Int) = Some(n % 2 == 0) methodUsingMapping(isEven) //or def isEven(n: Int) = n % 2 == 0 methodUsingMapping(x => Some(isEven(x))) 
+10
source

Since you only need to define 4 methods for implementing the implementation of the Map attribute, you can simply collapse your own:

 trait MapWithRelationship[K, +V] extends Map[K, V] { self => def pred: (K, Any) => Boolean def underlying: Map[K, V] def get(key: K): Option[V] = underlying.get(key) def iterator: Iterator[(K, V)] = underlying.iterator def + [V1 >: V](kv: (K, V1)): MapWithRelationship[K, V1] = { val (k, v) = kv if (pred(k, v)) { new MapWithRelationship[K, V1] { val pred = self.pred val underlying = self.underlying + kv } } else { throw new Exception(s"Key-value pair $kv failed MapWithRelationship predicate") } } def -(key: K): MapWithRelationship[K, V] = new MapWithRelationship[K, V] { val pred = self.pred val underlying = self.underlying - key } } object MapWithRelationship { def apply[K, V](rule: (K, Any) => Boolean)(pairs: (K, V)*) = { val empty = new MapWithRelationship[K, V] { def pred = rule def underlying = Map.empty[K, V] } pairs.foldLeft(empty)(_ + _) } } 

Then you can use as such:

 scala> val x = MapWithRelationship[Int, Int]((k, v) => v == k * k)() x: MapWithRelationship[Int,Int] = Map() scala> val x2 = x + (1 -> 1) x2: MapWithRelationship[Int,Int] = Map(1 -> 1) scala> val x3 = x + (5 -> 25) x3: MapWithRelationship[Int,Int] = Map(5 -> 25) scala> val x4 = x + (6 -> "foo") java.lang.Exception: Key-value pair (6,foo) failed MapWithRelationship predicate at MapWithRelationship$class.$plus(<console>:21) at MapWithRelationship$$anon$3.$plus(<console>:33) ... 32 elided 
+4
source

You can make an infinite map of squares using:

 val mySquareMap = Map.empty[Int, Int].withDefault(d => d * d) 

This map will still have + , get , iterator and other methods that won't work as desired, but if you need a read-only map that returns squares, this will work.

Of course, it would be more efficient and probably more straightforward to just use:

 val mySquare = (d:Int) => d * d 

as a function. However, the above Map may be useful if you need to use some API that requires this type.

To have a more complete solution for this, you might be better off creating your own class that extends Map[Int, Int] , which overrides get to return the square of its argument.

+1
source

Source: https://habr.com/ru/post/982677/


All Articles