Tuples as Key in Map, how to retrieve value? - scala

Imagine the following (pseudo) class:
class Board(rows: Int, cols: Int)
{
val fields = Map[(Int, Int), Field] // Position is a tuple
def getField(position: (Int, Int)): Field
{
fields(position)
}
}
How could I do this? I could create a type like:
type Position = (Int, Int)
and then val fields = Map[Position, Field]
Could that work?

Your code has syntax errors, but once you fix them, it should work. Using String instead of Field (since I don't know the definiton of Field), you can write this in Scala REPL and see it work:
scala> val fields: Map[(Int, Int), String] = Map((1,2) -> "a", (3,4) -> "b")
fields: Map[(Int, Int),String] = Map((1,2) -> a, (3,4) -> b)
scala> def getField(position: (Int, Int)) = fields(position)
getField: (position: (Int, Int))String
scala> getField((1,2))
res1: String = a

Related

Invoke method based on list

I have object in scala, where I have defined functions. I have Seq where all the functions are listed as the elements as below:
object ABC {
def abc(a: Int, b: Int): Int = a+b
def pqr(p: Int, q: Int): Int = p - q
val listFunction: Seq[(Int, Int) => Int] = Seq(abc, pqr)
}
Now, I want to call the list which I defined as string and execute all the functions inside it, by taking the parameters. Similar to the below:
val listName = "listFunction"
val method = example.ABC.getClass.getMethod(listName,1,2)
Can someone help me, how to invoke the listFunction list and execute all the functions inside it.
To call the methods do this:
val result: Seq[Int] = ABC.listFunction.map(_(1, 2))
This calls each function in turn and puts the result in a new Seq.
If you want multiple lists indexed by name, use a Map like this:
object ABC {
def abc(a: Int, b: Int): Int = a+b
def pqr(p: Int, q: Int): Int = p - q
private val functions = Map(
"listFunction" -> Seq(abc _, pqr _),
"listReverse" -> Seq(pqr _, abc _),
)
def apply(name: String) = functions(name)
}
val listName = "listFunction"
val result: Seq[Int] = ABC(listName).map(_(1, 2))
Try
val listName = "listFunction"
example.ABC.getClass.getMethod(listName).invoke(example.ABC).asInstanceOf[Seq[(Int, Int) => Int]].map(_.apply(1, 2)) // List(3, -1)

Scala: passing function parameters as tuple

Let's say I have:
def foo(i: Int, s: String)
and have:
val tuple: (Int, String) = (1, "s")
can I pass tuple to foo without adding a wrapper for foo?
Yes, its possible. Using .tupled one can convert the lambda into accepting the tuple as argument.
Scala REPL
scala> def foo(i: Int, s: String): Int = i
foo: (i: Int, s: String)Int
scala> (foo _).tupled
res3: ((Int, String)) => Int = scala.Function2$$Lambda$226/234698513#45984654
scala> val tuple: (Int, String) = (1, "s")
tuple: (Int, String) = (1,s)
scala> (foo _).tupled(tuple)
res5: Int = 1
foo(tuple._1, tuple._2) should work.
If you want something more maintainable, I would recommend following:
type MyTuple = (Int, String)
def foo(t:MyTuple) = ??? // some code
val tuple = (1, "s")
foo(tuple) // works
Also inside foo, the best way to unwrap the tuple would be
val (int, string) = t

Tuple with Int member not recognized where Long is required

I've got a function that expects a Tuple2, where one item must be a long. When I attempt to pass pairs with literal numbers using the -> implicit, they are being interpreted as Int instead of Long.
Here, the problem is demonstrated:
scala> val x: (Long, String) = (5, "test")
x: (Long, String) = (5,test)
scala> val x: (Long, String) = 5L -> "test"
x: (Long, String) = (5,test)
scala> val x: (Long, String) = 5 -> "test"
<console>:43: error: type mismatch;
found : (Int, String)
required: (Long, String)
val x: (Long, String) = 5 -> "test"
I suppose the problem is that when the implicit is applied, type inference locks in on Int, but in the first example, type inference is nice enough to consider the number a Long.
But my DSL works much better if I can use -> and omit the L suffix. Is there a way to achieve this?
Unfortunately, Int is not formally a subtype of Long. Thus, an (Int, String) can not be passed where a (Long, String) is expected, despite the fact that Tuple2 is covariant in its type parameters.
However, Int is viewable as Long. So, you can design a function that will upconvert an Int to a Long the following way:
scala> def goodFunc[LongLike <% Long](arg: (LongLike, String)) = { "whatevs" }
goodFunc: [LongLike](arg: (LongLike, String))(implicit evidence$1: LongLike => Long)String
scala> goodFunc(5 -> "test")
res4: String = whatevs
Or, better yet, use:
def goodFunc[LongLike](arg: (LongLike, String))(implicit ev: LongLike => Long) = { "whatevs" }
, since <% is deprecated.

How to use scala.collection.immutable.Map's API sum directly?

I want to sum the Map("one" -> 2, "two" -> 3, "three" -> 4)'s values 2+3+4, of course I can use the following methods:
Map("one" -> 2, "two" -> 3, "three" -> 4).foldLeft(0)(_ + _._2)
Map("one" -> 2, "two" -> 3, "three" -> 4).values.sum()
I found Map has a another more direct API sum: def sum: A, but, I don't search any example about this API, How to use it?
For sum[B >: (A, B)](implicit num: Numeric[B]): B, as you see, it needs the implicit parameter and the parameter type is Numeric[B] type.
Scala Numeric is trait that defined a series of math operation.
http://www.scala-lang.org/api/current/scala/math/Numeric.html
for your case, the type is Map[String, Int], so you need to implement the implicit for Numeric[(String, Int)] for sum method, like:
trait MyTupleNumeric extends Numeric[(String, Int)] {
def plus(x: (String, Int), y: (String, Int)) = ("", x._2 + y._2)
override def minus(x: (String, Int), y: (String, Int)): (String, Int) = ("", x._2 - x._2)
override def times(x: (String, Int), y: (String, Int)): (String, Int) = ("", x._2 * y._2)
override def negate(x: (String, Int)): (String, Int) = ("", -x._2)
override def fromInt(x: Int): (String, Int) = ("", x)
override def toInt(x: (String, Int)): Int = x._2
override def toLong(x: (String, Int)): Long = x._2.toLong
override def toFloat(x: (String, Int)): Float = x._2.toFloat
override def toDouble(x: (String, Int)): Double = x._2.toDouble
override def compare(x: (String, Int), y: (String, Int)): Int = x._2 - y._2
}
implicit object MyTupleNumericImplicit extends MyTupleNumeric
val f = implicitly[Numeric[(String, Int)]] // implicitly is used find implicits base on the type
println(f.plus(("one", 2), ("two", 3)))
val r = Map("one" -> 2, "two" -> 3, "three" -> 4)
println(r.sum._2)
println(r.sum(MyTupleNumericImplicit))
As the above code, we have implement own Numeric type with (String, Int), and implements methods.
And we implicit this into our scope, so we can use implicitly to get the function and call.
And the sum method also could find the implicit parameter for Numeric[(String, Int)]
The Map.sum method doesn't do what you want -- your functions look good as they are.
The reason why Map has a sum method is that all TraversableOnce classes have a sum method, which only works if the collection type is numeric. However, a Map[K, V] is a TraversableOnce[(K, V)], so this approach won't work (the key-value tuple is not a numeric type).

Applying implicit conversion to map

I tried implicit conversions in the following example:
val m: Map[Int, Int] = Map(10 -> "asd") //fine
val mm: Map[Int, Int] = Map("asd" -> 20) //type mismatch; found: (String, Int)
//required: (Int, Int)
implicit def stringToInt(str: String): Int = 10
Why can't we apply implicit conversions to map keys? Is there a way to work around this?
It doesn't work because you're using -> which is an (inline) operator:
implicit final class ArrowAssoc[A](self : A) extends scala.AnyVal {
#scala.inline
def ->[B](y : B) : scala.Tuple2[A, B] = { /* compiled code */ }
def →[B](y : B) : scala.Tuple2[A, B] = { /* compiled code */ }
}
You can see that by the time B is evaluated, A is already "fixed". Let's just say that you can only (implicitly) convert the right hand side of a tuple when using -> operator:
implicit def stringToInt(str: String): Int = 10
implicit def intToStr(str: Int): String = "a"
val a: Map[Int, Int] = Map(10 -> "asd") //fine
val b: Map[Int, Int] = Map("asd" -> 20) // error! cant change left side
val c: Map[String, String] = Map("asd" -> 20) // fine
val d: Map[String, String] = Map(10 -> "asd") // error! cant change left side
Because of similar compiler quirks related to using operator ->, #Jorg's solution works in one direction, but not the other:
implicit def tupleIntifier(t: (String, Int)) = (10, 10)
implicit def tupleIntifier2(t: (Int, String)) = (10, 10)
val a: Map[Int, Int] = Map("asd" -> 20) // uses tupleIntifier
val b: Map[Int, Int] = Map(10 -> "asd") // fails!!
However, if you avoid using -> operator altogether and simply use (key, value) syntax, it will work:
val a: Map[Int, Int] = Map((10, "asd"))
val b: Map[Int, Int] = Map(("asd", 20))
implicit def stringToInt(str: String): Int = 15
println(a) // prints Map(10 -> 15)
println(b) // prints Map(15 -> 20)
Please, look at the error message you are getting:
error: type mismatch;
found : (String, Int)
required: (Int, Int)
val mm: Map[Int, Int] = Map("asd" -> 20)
^
The error message is not about String instead of Int, it is about (String, Int) instead of (Int, Int). So, you are simply converting the wrong thing:
implicit def tupleIntifier[T](t: (String, T)) = (10, t._2)
val mm: Map[Int, Int] = Map("asd" -> 20)
//=> mm: Map[Int,Int] = Map(10 -> 20)
Voila! It works.
If you were to add such a general implicit conversion, you would lose the type-safety that Scala is enforcing, because any String would become an Int as needed, anywhere, without intervention from the programmer.
In reality, when you want to create that map from other data, you probably already know the data types of that other data. So if the keys are known to be integers, convert them to Int and use them like that. Otherwise, use strings.
Your example is highly artificial. Which concrete problem are you trying to solve?