How does find function in Map work in Scala? - scala

I am new to Scala. This is the code that I have written.
object Main extends App {
val mp: Map[String, String] = Map[String, String]("a"->"a", "b"->"b", "c"->"c", "d"->"d")
val s: Option[(String, String)] = mp.find((a: String, b: String) => {
if(a == "c" && b == "c") {
true
}
else {
false
}
})
println(s)
}
I am getting the following error.
error: type mismatch;
found : (String, String) => Boolean
required: ((String, String)) => Boolean
What am I doing wrong?

You need to change
mp.find((a: String, b: String) =>
to either
mp.find(((a: String, b: String)) =>
or
mp.find( case (a: String, b: String) =>
What you have coded is a function expecting two parameters, but you will only be passing in one, which is a Pair (also called Tuple2). The extra braces and the case keyword are ways of specifying that you are only passing in the one parameter, which is an instance of a Pair.

The problem is that find expects a function that takes a single argument, a Tuple2 in this case and returns a Boolean: ((String, String)) => Boolean. However, what you have there is a function that takes two args a and b, not a tuple (brackets matter): (String, String) => Boolean.
Here is one way to fix it. In this case I use pattern matching to extract arguments:
object Main extends App {
val mp: Map[String, String] = Map[String, String]("a"->"a", "b"->"b", "c"->"c", "d"->"d")
val s: Option[(String, String)] = mp.find{ case(a, b) => a == "c" && b == "c" }
println(s)
}
alternatively you could also do:
val s: Option[(String, String)] = mp.find(t => t._1 == "c" && t._2 == "c")
Either would print:
Some((c,c))

Related

Is it possible to use Option with spark UDF

I'd like to use Option as input type for my functions.
udf((oa: Option[String], ob: Option[String])) => …
to handle null values in a more functional way.
Is there a way to do that ?
As far as I know it is not directly possible. Nothing stops you wrapping arguments with Options:
udf((oa: String, ob: String) => (Option(oa), Option(ob)) match {
...
})
using Dataset encoders:
val df = Seq(("a", None), ("b", Some("foo"))).toDF("oa", "ob")
df.as[(Option[String], Option[String])]
or adding some implicit conversions:
implicit def asOption[T](value: T) : Option[T] = Option(value)
def foo(oa: Option[String], ob: Option[String]) = {
oa.flatMap(a => ob.map(b => s"$a - $b"))
}
def wrap[T, U, V](f: (Option[T], Option[U]) => V) =
(t: T, u: U) => f(Option(t), Option(u))
val foo_ = udf(wrap(foo))
df.select(foo_($"oa", $"ob"))

Type of "foreach" in Scala

I have example code:
def test = {
val l : Seq[(String, String)] = Seq()
val foo : (String, String) => Unit = {case (a, b)=>}
l.foreach[Unit](foo)
}
It gives me the next error:
Error:(8, 21) type mismatch;
found : (String, String) => Unit
required: ((String, String)) => Unit
l.foreach[Unit](foo)
As far as I understand foreach has a type (A=>U)=>Unit where:
A is template parameter of my collection and
U is template parameter of foreach itself
My question is why is ((String, String)) => Unit required? Where do the extra brackets come from?
If you check in the REPL, you'll see that foo is a <function2>, that is, a function that takes 2 argument:
scala> val foo : (String, String) => Unit = {case (a, b)=>}
foo: (String, String) => Unit = <function2>
.foreach however expects a function that takes a single arguement (of type A), which in your case is a Tuple2.
If you set foo to be a <function1>, then it works:
scala> val foo : ((String, String)) => Unit = {case (a, b)=>}
foo: ((String, String)) => Unit = <function1>
scala> val l : Seq[(String, String)] = Seq()
l: Seq[(String, String)] = List()
scala> l.foreach[Unit](foo)
scala>
Let's start from top.
You have
val l: Seq[(String, String)]
that is a Seq of tuples of two strings. So each of the elements of l is of type (String, String) which is a syntactic sugar for Tuple2[String, String].
Now you have
val foo: (String, String) => Unit
that is a function of two arguments each being String. Again, the type signature above is syntactic sugar for Function2[String, String, Unit].
Considering the above your code can be rewritten as follows:
def test = {
val l: Seq[Tuple2[String, String]] = Seq()
val foo: Function2[String, String, Unit] = {case (a, b)=>}
l.foreach[Unit](foo)
}
foreach expects a single argument function, i.e. a Function1[T, R], thus the type mismatch.
The type definition foo: (String, String) => Unit says that foo is a function which takes two parameters of type String. However, your sequence l: Seq[(String, String)] contains tuples of type type t = (String, String). Thus, calling l.foreach expects a function which is applicable to the tuple type t. Thus, it expects a function of type t => Unit, which is equivalent to ((String, String)) => Unit.

Scala type erasure in pattern matching Map[String, Int]

In Scala 2.10, the compiler warns the given code:
private def getStrFromOpt[T](opt: Option[T]): String = opt match {
case Some(s: String) => s
case Some(i: Int) => i.toString()
case Some(l: Long) => l.toString()
case Some(m: Map[String, Int]) => m map ({ case (k, v) =>
"(" + k + ", " + v + ")" }) mkString ("(", ", ", ")")
case _ => ""
}
with the message non-variable type argument String in type pattern Map[String,Int] is unchecked since it is eliminated by erasure: case Some(m: Map[String, Int]) ....
How can I get rid of this warning? What if I will have a Map[String, MyObj] which I want to include as a case in this matching - how can I distinguish the two cases with parameterised Maps?
you can use Scala annotation #unchecked to suppress the warning,
for your second question, I suggest you use Scala reflection -- TypeTag. I am using Scala 2.11.4, here is the sample code, FYI.
import scala.reflect.runtime.{ universe => ru }
import scala.reflect.runtime.universe.{ typeTag, TypeTag }
object MapType extends App {
def getTypeTag[T: TypeTag](t: T) = typeTag[T].tpe
def getTypeTag[T: TypeTag] = ru.typeOf[T]
// context bound T: ru.TypeTag cause typeOf
// requires implicit parameter
def getStrFromOpt[T: TypeTag](opt: Option[T]): String = {
opt match {
case Some(s: String) => s
case Some(i: Int) => i.toString()
case Some(l: Long) => l.toString()
case Some(m: Map[String, Int] # unchecked)
if getTypeTag[T] =:= getTypeTag[Map[String, Int]] => "Int Map"
case Some(m: Map[String, String] # unchecked)
if getTypeTag[T] =:= getTypeTag[Map[String, String]] => "String Map"
case _ => ""
}
}
// "Int Map"
println(getStrFromOpt(Some(Map("a" -> 2, "b" -> 3))))
// "String Map"
println(getStrFromOpt(Some(Map("a" -> "2", "b" -> "3"))))
}
Actually, Scala uses the erasure model of generics just like Java does. So no information about type arguments is maintained at runtime.
def isIntMap(x: Any) = x match {
case m: Map[Int, Int] => true
case _ => false
}
For the code above, Scala compiler can not decide whether m is Map[Int, Int] or not. Therefore, isIntMap(Map("a"-> "b")) return true, which seems to be non-intuitive.
To alert you the runtime behavior, Scala compiler emitted that un-checked message.
However, Array is an Exception, its element type is stored with element value.
def isIntArray(x: Any) = x match {
case a: Array[String] => "yes"
case _ => "no"
}
scala> isIntArray(Array(3))
res1: String = no
scala> isIntArray(Array("1"))
res2: String = yes

Why does my pattern match on a collection fail in Scala?

My code is as follows
val hash = new HashMap[String, List[Any]]
hash.put("test", List(1, true, 3))
val result = hash.get("test")
result match {
case List(Int, Boolean, Int) => println("found")
case _ => println("not found")
}
I would expect "found" to be printed but "not found" is printed. I'm trying to match on any List that has three elements of type Int, Boolean, Int
You are checking for a list containing the companion objects Int and Boolean. These are not the same as the classes Int and Boolean.
Use a Typed Pattern instead.
val result: Option[List[Any]] = ...
result match {
case Some(List(_: Int, _: Boolean, _: Int)) => println("found")
case _ => println("not found")
}
Scala Reference, Section 8.1 describes the different patterns you can use.
The first problem is that the get method returns an Option:
scala> val result = hash.get("test")
result: Option[List[Any]] = Some(List(1, true, 3))
So you'd need to match against Some(List(...)), not List(...).
Next, you are checking if the list contains the objects Int, Boolean and Int again, not if it contains objects whose types are Int, Boolean and Int again.
Int and Boolean are both types and object companions. Consider:
scala> val x: Int = 5
x: Int = 5
scala> val x = Int
x: Int.type = object scala.Int
scala> val x: Int = Int
<console>:13: error: type mismatch;
found : Int.type (with underlying type object Int)
required: Int
val x: Int = Int
^
So the correct match statement would be:
case Some(List(_: Int, _: Boolean, _: Int)) => println("found")
The following also works for Scala 2.8
List(1, true, 3) match {
case List(a:Int, b:Boolean, c:Int) => println("found")
case _ => println("not found")
}

Defining a Map from String to Function in Scala

I am trying to define a Map literal with key: String, value: (Any)=>String. I tried the following, but get a syntax error:
def foo(x: Int): String = /...
def bar(x: Boolean): String = /...
val m = Map[String, (Any) => String]("hello" -> foo, "goodbye" -> bar)
Funny that no one actually gave a type that would work. Here's one such:
def foo(x: Int): String = x.toString
def bar(x: Boolean): String = x.toString
val m = Map[String, (Nothing) => String]("hello" -> foo, "goodbye" -> bar)
The reason why it works this way is because Function1 is contra-variant on the input, so (Nothing) => String is a superclass of (Int) => String. It is also co-variant on the output, so (Nothing) => Any would be a superclass to any other Function1.
Of course, you can't use it like that. Without manifests, you can't even uncover what the original type of Function1 is. You could try something like this, though:
def f[T : Manifest](v: T) = v -> manifest[T]
val m = Map[String, ((Nothing) => String, Manifest[_])]("hello" -> f(foo), "goodbye" -> f(bar))
val IntManifest = manifest[Int]
val BooleanManifest = manifest[Boolean]
val StringManifest = manifest[String]
m("hello")._2.typeArguments match {
case List(IntManifest, StringManifest) =>
m("hello")._1.asInstanceOf[(Int) => String](5)
case List(BooleanManifest, StringManifest) =>
m("hello")._1.asInstanceOf[(Boolean) => String](true)
case _ => "Unknown function type"
}
Int => String is not a subclass of Any => String, rather, the contrary. You can't put (replace) an Int => String function when a code expects Any => String, since that code can apply the function with, say, "hi".
#Ben suggestion works, but how is it useful? you can't invoke the function once you get it from the Map.
If you really want to do this, maybe define foo as a partial function:
val foo: PartialFunction[Any, String] = {case i: Int => ....}
Obviously, this will fail at runtime if you pass it a string, but you can always test if the function is ok for use with your parameter by using isDefinedAt. (another alternative may be manifests, but I don't see the value here)
If I let the compiler infer it I seem to get an illegal type:
scala> val m = Map("hello" -> foo _, "goodbye" -> bar _)
m: scala.collection.immutable.Map[java.lang.String,(Boolean with Int) => String] =
Map((hello,<function1>), (goodbye,<function1>))
scala> m("hello")(8)
<console>:9: error: type mismatch;
found : Int(8)
required: Boolean with Int
m("hello")(8)
scala> var q = new Boolean with Int
<console>:5: error: illegal inheritance from final class Boolean
var q = new Boolean with Int
Anyway, what you want is not the type Any but a generic of "any type" which is _:
scala> val mm = Map[String, (_) => String]("hello" -> foo _, "goodbye" -> bar _)
mm: scala.collection.immutable.Map[String,Function1[_, String]] =
Map((hello,<function1>), (goodbye,<function1>))
I just posted a question about how to invoke such functions because I don't actually know.
Trait Function1 is contravariant for parameter, so def foo(x: Int): String is not a (Any) => String. So the following would work:
scala> def baz(x: Any): String = "baz"
baz: (x: Any)String
scala> val m2 = Map[String, (String) => String]("hello" -> baz)
m2: scala.collection.immutable.Map[String,(String) => String] = Map((hello,<function1>))
This is how I did it to fulfill a similar requirement.
object MapToMethods {
private def increment(x: Int): Int = x+1
private def decrement(x: Int): Int = x-1
val m: Map[String, Int => Int] =Map("increment" -> increment, "decrement" ->decrement)
println(m("increment")(2)) //prints 3
println(m("decrement")(3)) //prints 2
}