I'm trying to make a tail recursive method but i'm using Map and i don't know how to use Pattern Matching to check if Map is empty/null and get head/tail:
def aa(a:Map[String, Seq[Operation]]): Map[String, (Seq[Operation], Double)] = {
def aaRec(xx:Map[String, Seq[Operation]],
res:Map[String, (Seq[Operation], Double)],
acc:Double = 0): Map[String, (Seq[Operation], Double)] = xx match {
case ? =>
res
case _ =>
val head = xx.head
val balance = head._2.foldLeft(acc)(_ + _.amount)
aaRec(xx.tail, res + (head._1 -> (head._2, balance)), balance)
}
aaRec(a, Map[String, (Seq[Operation], Double)]())
}
}
What is the correct syntax on case empty map and case h :: t?
Thank's in advance
Map has no order so it has no head or tail. It also has no unapply/unapplySeq method so you can't do pattern matching on a Map.
I think going with a foldLeft might be your best option.
I'm not sure if it's possible to pattern match on a map, but this code could be rewritten using basic combinator methods:
def aa(a:Map[String, Seq[Operation]]): Map[String, (Seq[Operation], Double)] =
a.mapValues(seq => (seq, seq.map(_.amount).sum))
Related
I am trying some basic logic using scala . I tried the below code but it throws error .
scala> val data = ("HI",List("HELLO","ARE"))
data: (String, List[String]) = (HI,List(HELLO, ARE))
scala> data.flatmap( elem => elem)
<console>:22: error: value flatmap is not a member of (String, List[String])
data.flatmap( elem => elem)
Expected Output :
(HI,HELLO,ARE)
Could some one help me to fix this issue?
You are trying to flatMap over a tuple, which won't work. The following will work:
val data = List(List("HI"),List("HELLO","ARE"))
val a = data.flatMap(x => x)
This will be very trivial in scala:
val data = ("HI",List("HELLO","ARE"))
println( data._1 :: data._2 )
what exact data structure are you working with?
If you are clear about you data structure:
type rec = (String, List[String])
val data : rec = ("HI",List("HELLO","ARE"))
val f = ( v: (String, List[String]) ) => v._1 :: v._2
f(data)
A couple of observations:
Currently there is no flatten method for tuples (unless you use shapeless).
flatMap cannot be directly applied to a list of elements which are a mix of elements and collections.
In your case, you can make element "HI" part of a List:
val data = List(List("HI"), List("HELLO","ARE"))
data.flatMap(identity)
Or, you can define a function to handle your mixed element types accordingly:
val data = List("HI", List("HELLO","ARE"))
def flatten(l: List[Any]): List[Any] = l.flatMap{
case x: List[_] => flatten(x)
case x => List(x)
}
flatten(data)
You are trying to flatMap on Tuple2 which is not available in current api
If you don't want to change your input, you can extract the values from Tuple2 and the extract the values for second tuple value as below
val data = ("HI",List("HELLO","ARE"))
val output = (data._1, data._2(0), data._2(1))
println(output)
If that's what you want:
val data = ("HI",List("HELLO,","ARE").mkString(""))
println(data)
>>(HI,HELLO,ARE)
I want to update a sequence in Scala, I have this code :
def update(userId: Long): Either[String, Int] = {
Logins.findByUserId(userId) map {
logins: Login => update(login.id,
Seq(NamedParameter("random_date", "prefix-" + logins.randomDate)))
} match {
case sequence : Seq(Nil, Int) => sequence.foldLeft(Right(_) + Right(_))
case _ => Left("error.logins.update")
}
}
Where findByUserId returns a Seq[Logins] and update returns Either[String, Int] where Int is the number of updated rows,
and String would be the description of the error.
What I want to achieve is to return an String if while updating the list an error happenes or an Int with the total number of updated rows.
The code is not working, I think I should do something different in the match, I don't know how I can check if every element in the Seq of Eithers is a Right value.
If you are open to using Scalaz or Cats you can use traverse. An example using Scalaz :
import scalaz.std.either._
import scalaz.std.list._
import scalaz.syntax.traverse._
val logins = Seq(1, 2, 3)
val updateRight: Int => Either[String, Int] = Right(_)
val updateLeft: Int => Either[String, Int] = _ => Left("kaboom")
logins.toList.traverseU(updateLeft).map(_.sum) // Left(kaboom)
logins.toList.traverseU(updateRight).map(_.sum) // Right(6)
Traversing over the logins gives us a Either[String, List[Int]], if we get the sum of the List we get the wanted Either[String, Int].
We use toList because there is no Traverse instance for Seq.
traverse is a combination of map and sequence.
We use traverseU instead of traverse because it infers some of the types for us (otherwise we should have introduced a type alias or a type lambda).
Because we imported scalaz.std.either._ we can use map directly without using a right projection (.right.map).
You shouldn't really use a fold if you want to exit early. A better solution would be to recursively iterate over the list, updating and counting successes, then return the error when you encounter one.
Here's a little example function that shows the technique. You would probably want to modify this to do the update on each login instead of just counting.
val noErrors = List[Either[String,Int]](Right(10), Right(12))
val hasError = List[Either[String,Int]](Right(10), Left("oops"), Right(12))
def checkList(l: List[Either[String,Int]], goodCount: Int): Either[String, Int] = {
l match {
case Left(err) :: xs =>
Left(err)
case Right(_) :: xs =>
checkList(xs, (goodCount + 1))
case Nil =>
Right(goodCount)
}
}
val r1 = checkList(noErrors, 0)
val r2 = checkList(hasError, 0)
// r1: Either[String,Int] = Right(2)
// r2: Either[String,Int] = Left(oops)
You want to stop as soon as an update fails, don't you?
That means that you want to be doing your matching inside the map, not outside. Try is actually a more suitable construct for this purpose, than Either. Something like this, perhaps:
def update(userId: Long): Either[String, Int] = Try {
Logins.findByUserId(userId) map { login =>
update(login.id, whatever) match {
case Right(x) => x
case Left(s) => throw new Exception(s)
}
}.sum
}
.map { n => Right(n) }
.recover { case ex => Left(ex.getMessage) }
BTW, a not-too-widely-known fact about scala is that putting a return statement inside a lambda, actually returns from the enclosing method. So, another, somewhat shorter way to write this would be like this:
def update(userId: Long): Either[String, Int] =
Logins.findByUserId(userId).foldLeft(Right(0)) { (sum,login) =>
update(login.id, whatever) match {
case Right(x) => Right(sum.right + x)
case error#Left(s) => return error
}
}
Also, why in the world does findUserById return a sequence???
There is a snippet of scala code,which I think quite easy
val m1 = Map("age"->60,"name"->"x")
val m2 = Map("age"->99,"name"->"j")
val l = List(m1,m2)
val max = l.maxBy(_("age"))
However, instead of the expecting result
val m2 = Map("age"->99,"name"->"j")
I get an error:
<console>:13: error: No implicit Ordering defined for Any.
I know there is something wrong about the implicit parameter,but I don't know how to solve this problem.
update
further,suppose I need a more general solution for this,a function
def max(l:List[Map[String,Any]],key:String)
then
max(l,"age") == Map("age"->99,"name"->"j")
max(l,"name") == Map("age"->60,"name"->"x")
Your maps have type Map[String, Any] so compiler could not find Ordering for Any object. Add explicit conversion to Int:
val max = l.maxBy(_("age").asInstanceOf[Int])
Or try use Map with specific value's type such as Map[String, Int] or similar.
Other way is to use case classes or tuples:
val list = List((60, "j"), (99, "x"))
list.maxBy(_._1)
This works,
l.maxBy(m => m("age").toString.toInt)
and is closer to intended semantics on maximal (numerical) age, even that it does not prove type-safe, the original maps type preserves no information on the intended types, as aforementioned.
Try Using last line as
val max = l.maxBy(_("age").toString.toInt)
Something like this may work (not that I like the idea of Map[String, Any]):
def myOrder(k: String) = {
x: Map[String, Any] =>
x(k) match { case i:Int => (i, "")
case s:String => (0, s) }
}
l.maxBy(myOrder("age")) // Map(age -> 99, name -> j)
l.maxBy(myOrder("name")) // Map(age -> 60, name -> x)
I've run into a bit of a problem. I need to convert the types
Map[String, Iterator[Int]] -> Iterator[Map[String, Int]]
My current approach at solving this problem is by using a recursive function:
def Inverter(input: Map[String, Iterator[Int]], output: Iterator[Map[String, Int]]) = {
val inversion: Map[String, Int] = input.flatMap {case (symbol, iterator) => iterator.hasNext match {
case true => Some((symbol,iterator.next))
case false => None
}}
inversion.size match {
case 0 => output
case _ => Inverter(input, output ++ Iterator(inversion))
}
}
This code solves the problem, but is too slow. I think it has something to do with the ++ call being slow. Is there any way I can cons elements onto the head of an Iterator like I can a List in constant time? If not, can anyone come up with a good workaround?
def invert(input: Map[String, Iterator[Int]]) =
Iterator.continually(input.collect {
case (key, it) if it.hasNext => (key, it.next)
}).takeWhile(_.nonEmpty)
Some explanation:
This part: input.collect { case (key, it) if it.hasNext => (key, it.next) } takes a single element from every iterator in the input map and creates a Map[String,Int]. Now, simply apply this operation on the input map continually, until we exhaust all the iterators.
It's a little tricky, because iterators are inherently mutable and we are relying on side effects of the collect invocation.
Besides using match, is there an Option-like way to getOrElse the actual content of the Right or Left value?
scala> val x: Either[String,Int] = Right(5)
scala> val a: String = x match {
case Right(x) => x.toString
case Left(x) => "left"
}
a: String = 5
Nicolas Rinaudo's answer regarding calling getOrElse on either the left or right projection is probably the closest to Option.getOrElse.
Alternatively, you can fold the either:
scala> val x: Either[String,Int] = Right(5)
x: Either[String,Int] = Right(5)
scala> val a: String = x.fold(l => "left", r => r.toString)
a: String = 5
As l is not used in the above fold, you could also write x.fold(_ => "left", r => r.toString)
Edit:
Actually, you can literally have Option.getOrElse by calling toOption on the left or right projection of the either, eg,
scala> val o: Option[Int] = x.right.toOption
o: Option[Int] = Some(5)
scala> val a: String = o.map(_.toString).getOrElse("left")
a: String = 5
I don't particularly like Either and as a result I'm not terribly familiar with it, but I believe you're looking for projections: either.left.getOrElse or either.right.getOrElse.
Note that projections can be used in for-comprehensions as well. This is an example straight from the documentation:
def interactWithDB(x: Query): Either[Exception, Result] =
try {
Right(getResultFromDatabase(x))
} catch {
case ex => Left(ex)
}
// this will only be executed if interactWithDB returns a Right
val report =
for (r <- interactWithDB(someQuery).right) yield generateReport(r)
if (report.isRight)
send(report)
else
log("report not generated, reason was " + report.left.get)
Given type A on both sides, that is, Either[A, A], we can use Either.merge
...to extract values from Either instances regardless of whether they are
Left or Right.
Note if left and right types differ then result is least upper bound of the two types which may become in worst case Any:
val e: Either[Int, String] = Right("hello")
e.merge // hello: Any
In Scala 2.12 there is a getOrElse method for getting the "right" value but you cannot use it for the "left" value directly. However, you can do it like this: e.swap.getOrElse(42).