Using maps in Scala - scala

val nums = Map('M' -> 1000, 'D' -> 500, 'C' -> 100, 'L' -> 50, 'X' -> 10, 'V' -> 5, 'I' -> 1)
def convert(data : String):Int = {
var count = 0;
var index = 0;
var list: List[Char] = List();
for((value,index) <- data.reverse.zipWithIndex){
list = value :: list;
}
def calcRM(rm : List[Char]): List[Char] = rm match{
case x :: Nil => x
case x :: tail => ???
case Nil => Nil
}
return count
}
}
I am wondering how I can convert the value I get from x(head) and tail.head by using the Map provided so I can convert the string to the value in Roman Numerals.

To access a map, you simply use its get method:
nums.get(x)
which returns an Option that you would have to unwrap, so you can assume all good values and use the apply:
nums(x) //Throws if key not found
or you could use a default:
nums.getOrElse(x, 0)

You are looking for the apply method on Map. Recall that you can call apply simply by adding parentheses:
nums('M') //returns 1000
nums.apply('M') //equivalent in Scala
You can also use get to return an Option[Int] if you aren't sure the key is available in the Map, and you want to avoid the exception that would otherwise be thrown by apply:
nums.get('M') //returns Some(1000)
nums.get('K') //returns None
nums('K') //throws an exception
I would also recommend looking into some of the more idiomatic paradigms in Scala (e.g. using val). For your example, you might consider something like this, which uses inner methods and values, slightly more complex pattern matching, the ever important map method, the implicit apply used in the map, and recursion:
def convert(rm: String) = {
val nums = Map('M' -> 1000, 'D' -> 500, 'C' -> 100, 'L' -> 50, 'X' -> 10, 'V' -> 5, 'I' -> 1)
def calcRM(rm: Seq[Int]): Int = rm match {
case x :: y :: tail if x < y => y - x + calcRM(tail)
case x :: tail => x + calcRM(tail)
case Nil => 0
}
calcRM(rm.map(nums).toList)
}

Related

How to update value of a key in a immutable tree map in Scala

I'm using Scala 2.11 and i'm trying to update the value of a key in the tree map. I tried using updated:
private val xyz = List(0, 100000, 500000, 1000000)
private val abc = List (0, 5, 25, 50)
private var a = TreeMap.empty[Int, TreeMap[Int, Int]] ++ xyz.map {
aa => aa -> (TreeMap.empty[Int, Int] ++ abc.map(bb => bb -> 0))
}
a(xyz(0)).foreach {
case (key, value) =>
if (key < 50) {
a(xyz(0)) = a(xyz(0)).updated(key, 5)
}
}
And got the error:
value update is not a member of scala.collection.immutable.TreeMap[Int,scala.collection.immutable.TreeMap[Int,Int]]
Is it possible to update it? Or could someone please help me replicate the logic using a Java Tree Map since that will also allow me to use floorEntry and ceilingEntry functions. I tried converting to java tree map and it generated a regular map, not a tree map:
private var a = TreeMap.empty[Int, TreeMap[Int, Int]] ++ xyz.map {
aa => aa -> (TreeMap.empty[Int, Int] ++ abc.map(bb => bb -> 0)).asJava
}
private var b = a.asJava
You are getting confused between var/val and mutable/immutable.
I think you correctly understood the difference between val and var, that the former is an immutable variable and later is mutable. ie, if you try to reassign the object assigned as val you will get an error.
import scala.collection.immutable.TreeMap
val tm = TreeMap(1 -> 1, 2 -> 2, 3 -> 3)
tm = TreeMap(1->2)
^
error: reassignment to val
But a var can be mutated:
import scala.collection.immutable.TreeMap
var tm = TreeMap(1 -> 1, 2 -> 2, 3 -> 3)
tm = TreeMap(1->2)
// mutated tm
Notice that in the latter case, even though we are mutating the variable, we are not mutating the collection itself, we are assigning a new TreeMap. As we were using scala.collection.immutable.TreeMap it cant be mutated.
Instead, if we had used scala.collection.mutable.TreeMap, it has an update function
import scala.collection.mutable.TreeMap
val tm = TreeMap(1 -> 1, 2 -> 2, 3 -> 3)
tm.update(1, 5)
tm //TreeMap(1 -> 5, 2 -> 2, 3 -> 3)
Once you change scala.collection.immutable.TreeMap to scala.collection.mutable.TreeMap, this will work
a(xyz(0)).foreach{ case (key, value) =>
if(key < 50){
a(xyz(0)) = a(xyz(0)).updated(key, 5) //addOne(key, 5) if 2.13+
}
}
EDIT using java.util.TreeMap
private val xyz = List(0, 100000, 500000, 1000000)
private val abc = List(0, 5, 25, 50)
import java.util.{TreeMap => JTreeMap}
val jTreeMap = xyz.foldLeft(new JTreeMap[Int, JTreeMap[Int, Int]]()) { (acc, elem) =>
acc.put(
elem,
abc.foldLeft(new JTreeMap[Int, Int]()) { (acc2, elem2) =>
acc2.put(elem2, 0)
acc2
}
)
acc
}
//Map created
jTreeMap.get(xyz.head).replaceAll{
//hack for scala 2.11.x
new java.util.function.BiFunction[Int, Int, Int]{
def apply(key: Int, value: Int) = if (value < 5) 5 else value
}
}
//value edited
It is not possible to update an immutable object, you can only create a new immutable object from the old one. So the code needs to create a new TreeMap from the original one with different values as necessary.
The code looks like this:
val newMap = a.map{
case (k, v) if k == xyz(0) =>
k -> v.map {
case (k2, v2) if k2 < 50 =>
k2 -> 5
case (k2, v2) =>
k2 -> v2
}
case (k, v) =>
k -> v
}
This breaks down to an outer map that looks for matching keys in the outer TreeMap, and an inner map that looks for matching keys in the inner TreeMap. Pattern matching (case) is used to implement the match tests, and also to extract the keys and values.
Each map has one case that selects the values to be modified, and a second case that leaves other values unchanged. The first case returns the original key with a modified value while the second case just returns the original values (k -> v).
Also note that var applies to a variable, not the contents of a variable. It indicates whether the variable can be updated to refer to a different object, but says nothing about whether the object that the variable refers to can be updated. var is rarely used in Scala because it goes against a clean functional design.

Partially filter on a map to get key : Scala

I have a map:
val mapTest = Map("Haley" -> Map("Deran" -> 0.4, "Mike" -> 0.3), "Jack" -> Map("Deran" -> 0.3, "Mike" -> 0.3))
I want to retrieve the key based on a value. Given the value "Deran"-> 0.4 I should get "Haley".
I have tried using this:
mapTest.filter(_._2 == Map("Deran" -> 0.4))
but it doesn't work as filter selects all the values at a time. That's the first question. My second question is what If two keys verify that predicates such as the case for "Jack" and "Haley" for "Mike"
Maybe you want something like this:
val toSearch = List("Deran - > 0.4," Mike" -> 0.3)
mapTest.collectFirst {
case (key, values) if (toSearch.forall { case (k, v) => values.get(k).contains(v) }) => key
}
This could probably solve it:
def filter[K, NK, NV](m: Map[K, Map[NK, NV]])(p: ((NK, NV)) => Boolean): Vector[K] =
m.view.collect { case (k, v) if v.exists(p) => k }.toVector
Where NK is a generic type for a nested key and NV a generic type for a nested value.
This works as follows with the following inputs and outputs
val in1: (String, Double) = "Deran" -> 0.4
val out1: Vector[String] = Vector("Haley")
val in2: (String, Double) = "Mike" -> 0.3
val out2: Vector[String] = Vector("Haley", "Jack")
assert(filter(mapTest)(_ == in1) == out1)
assert(filter(mapTest)(_ == in2) == out2)
You can play around with this code here on Scastie.
Using a predicate you can be very generic but note that the complexity grows proportionally to the size of both the map and the nested maps contained therein.
If you can be less generic and simply check for equality, you can drop the predicate and use this to your advantage to make the nested check run in constant time:
def filter[K, NK, NV](m: Map[K, Map[NK, NV]])(p: (NK, NV)): Vector[K] =
m.view.collect { case (k, v) if v.get(p._1).contains(p._2) => k }.toVector
assert(filter(mapTest)(in1) == out1)
assert(filter(mapTest)(in2) == out2)
This variant is also available here on Scastie.

scala iterating through the set type with condition

I have a type of set and union function as follow
type Set = Int => Boolean
def union(s: Set, t: Set): Set = (e: Int) => s(e) || t(e)
val xs = Set(12001,12002, 12003, 12004)
val ys = Set(13001,13002, 13003, 13004)
When i use the union operation,
union(xs,ys)
It should return me another set which contains all the elements of both sets xs and ys
Edited Section:
I am sorry i was not clear on my question, i have my own implementation of the iterator for both Set xs and ys
var i = xs.iterator;
while(i.hasNext)
println(i.next())
But i was not satisfied with this implementation and found that you can implement the condition with the function (after some googling) but i was unable to get it to work in my eclipse worksheet.
val rs = union(xs,ys) //> rs : Learn2.Set = <function1>
I am guessing it returns a function.
so my questions,
1. is it possible to implement as described above in the edited section? if so, then what am i missing to get it working?
2. I don't understand how the element e in (e: Int) => s(e) || t(e) is iterating over the elements in both the sets
Look at your Set type: Int => Boolean. So it takes an Int and returns a Boolean. What that means is that it is not a collection that you can iterate over to retrieve all its values, because it actually contains no values.
If you want to know what Int values return true then you have to iterate over the entire range of possible inputs (or some subset thereof) and filter for the condition you're looking for.
scala> val res = union(xs,ys)
res: Set = $$Lambda$1091/332405156#2c30c81d
scala> (0 to 20000).filter(res).foreach(println)
12001
12002
12003
12004
13001
13002
13003
13004
scala>
update
Your confusion stems from the fact that you've named your function after an existing collection in the standard library. xs.itorator works because xs is not an example of your Set, it is a Set from the standard library with all the associated methods. Rename your type alias to something like Xet and you'll see what I mean.
type Xet = Int => Boolean
def union(s: Xet, t: Xet): Xet = (e: Int) => s(e) || t(e)
val xx: Xet = _ == 12001
val yx: Xet = _ == 13002
val zx: Xet = union(xx, yx)
xx.itrerator // Error, won't compile
(1 to 20000).filter(zx).foreach(println) // output: 12001 & 13002

Can I call a method inside of sortWith when sorting a sequence (or a map)?

I have a map Map[String,Option[Seq[String]]] and I have values for each of the string in a different map: Map[String,Option[Int]]. I am trying to map over the values and use a sortWith on the sequence but as I read online, I don't see any examples of having custom methods inside the sortWith.
How can I sort my sequence using sortWith? If I wanted to implement a custom method that returns a boolean to tell me what object is considered greater, is this possible?
val fieldMap = Map("user1" -> Seq("field1_name", "field2_name"), "user2" -> Seq("field3_name"))
val fieldValues = Map("field1_name" -> 2, "field2_name" -> 1, "field3_name" -> 3)
val sortedMap = fieldMap.mapValues(fieldList => fieldList.sortWith(fieldValues(_) < fieldValues(_)) // Scala doesn't like this
I tried:
fieldList.sortWith{(x,y) =>
val x = fieldValues(x)
val y = fieldValues(y)
x < y
}
This gives me a Type mismatch of expected type:
(String,String) => Boolean
and actual:
(String,String) => Any
EDIT Solution:
fieldList.sortWith{(x,y) =>
val x = fieldValues(x)
val y = fieldValues(x)
x.getOrElse[Double](0.0) < y.getOrElse[Double](0.0) // have to unwrap the Option.
}
You're using wrong syntax. For using sortWith you have to do something like:
fieldMap.mapValues(
fieldList => fieldList.sortWith(
(a,b) => fieldValues(a) > fieldValues(b)
)
)

Filtering out keys of a map but keeping all values in scala

I'm trying to write a method with the following signature:
def buildSumMap(minInterval:Int, mappes:SortedMap[Int, Long]):SortedMap[Int, Long] = {...}
Within the method I want to return a new map by applying the following pseudo-code to each
(key:Int,value:Long)-pair of "mappes":
If(key + minInterval > nextKey) {
value += nextValue
}
else {
//Forget previous key(s) and return current key with sum of all previous values
return (key, value)
}
Example: If I had the source Map ((10 -> 5000), (20 -> 5000), (25 -> 7000), (40 -> 13000)) and defined a minInterval of 10, I'd expect the resulting Map:
((10 -> 5000), (25 -> 12000), (40 -> 13000))
I found a lot of examples for transforming keys and values of filtering keys and values seperately but none so far for dropping keys, while preserving the values.
This solution uses List as intermediate structure. It traverses map from left to right and appends key-value pairs to list if interval is big enough, otherwise it replaces head of the list with new key-value pair. TreeMap factory metod reverses list at the end.
import collection.immutable._
def buildSumMap(minInterval:Int, mappes:SortedMap[Int, Long]):SortedMap[Int, Long] =
TreeMap(
mappes.foldLeft[List[(Int, Long)]] (Nil) {
case (Nil, nextKV) => nextKV :: Nil
case (acc # (key, value) :: accTail, nextKV # (nextKey, nextValue)) =>
if (nextKey - key < minInterval)
(nextKey -> (value + nextValue)) :: accTail
else
nextKV :: acc
} : _*
)
To answer the question, basically there is no totally simple way of doing this, because the requirement isn't simple. You need to somehow iterate through the SortedMap while comparing adjacent elements and build a new Map. There are several ways to do it:
Use a fold / reduce / scan / groupBy higher order functions: generally the preferred way, and most concise
Recursion (see http://aperiodic.net/phil/scala/s-99/ for plenty of examples): what you resort to if using higher order functions gets too complicated, or the exact function you need doesn't exist. May be faster than using functions.
Builders - a nice term for a brief foray into mutable-land. Best performance; often equivalent to the recursive version without the ceremony
Here's my attempt using scanLeft:
def buildSumMap(minInterval: Int, mappes: SortedMap[Int, Long]) =
SortedMap.empty[Int, Long] ++ mappes.toSeq.tail.scanLeft(mappes.head){
case ((k1, v1), (k2, v2)) => if (k2 - k1 > minInterval) (k2,v2) else (k1,v2)
}.groupBy(_._1).mapValues(_.map(_._2).sum)
It looks complicated but it isn't really, once you understand what scanLeft and groupBy do, which you can look up elsewhere. It basically scans the sequence from the left and compares the keys, using the key to the left if the gap is too small, then groups the tuples together according to the keys.
TLDR: The key is to learn the built-in functions in the collections library, which takes some practice, but it's good fun.
import scala.collection.SortedMap
def buildSumMap(minInterval:Int, mappes:SortedMap[Int, Long]):SortedMap[Int, Long] = {
def _buildSumMap(map: List[(Int, Long)], buffer: List[(Int, Long)], result:SortedMap[Int, Long]): SortedMap[Int, Long] = {
def mergeBufferWithResult = {
val res = buffer.headOption.map { case (k, v) =>
(k, buffer.map(_._2).sum)
}
res.map(result + _).getOrElse(result)
}
map match {
case entry :: other =>
if(buffer.headOption.exists(entry._1 - _._1 < minInterval)) {
_buildSumMap(other, entry :: buffer, result)
} else {
_buildSumMap(other, entry :: Nil, mergeBufferWithResult)
}
case Nil =>
mergeBufferWithResult
}
}
_buildSumMap(mappes.toList, List.empty, SortedMap.empty)
}
val result = buildSumMap(10 , SortedMap(10 -> 5000L, 20 -> 5000L, 25 -> 7000L, 40 -> 13000L))
println(result)
//Map(10 -> 5000, 25 -> 12000, 40 -> 13000)
I tried to split the parts of the algorithm :
import scala.collection._
val myMap = SortedMap((10 -> 5000), (20 -> 5000), (25 -> 7000), (40 -> 13000)).mapValues(_.toLong)
def filterInterval(minInterval: Int, it: Iterable[Int]):List[Int] = {
val list = it.toList
val jumpMap = list.map(x => (x, list.filter( _ > x + minInterval))).toMap.
filter(_._2.nonEmpty).mapValues(_.min)
def jump(n:Int): Stream[Int] = jumpMap.get(n).map(j => Stream.cons(j, jump(j))).getOrElse(Stream.empty)
list.min :: jump(list.min).toList
}
def buildSumMap(minInterval:Int, mappes:Map[Int, Long]):Map[Int,Long] = {
val filteredKeys: List[Int] = filterInterval(minInterval, mappes.keys)
val agg:List[(Int, Long)] = filteredKeys.map(finalkey =>
(finalkey,mappes.filterKeys(_ <= finalkey).values.sum)
).sort(_._1 < _._1)
agg.zip((filteredKeys.min, 0L) :: agg ).map(st => (st._1._1, st._1._2 - st._2._2)).toMap
}
buildSumMap(10, myMap)
Here's another take:
def buildSumMap(map: SortedMap[Int, Int], diff: Int) =
map.drop(1).foldLeft(map.take(1)) { case (m, (k, v)) =>
val (_k, _v) = m.last
if (k - _k < diff) (m - _k) + (k -> (v + _v))
else m + (k -> v)
}
A much cleaner (than my first attempt) solution using Scalaz 7's State, and a List to store the state of the computation. Using a List makes it efficient to inspect, and modify if necessary, the head of the list at each step.
def f2(minInterval: Int): ((Int, Int)) => State[List[(Int, Int)], Unit] = {
case (k, v) => State {
case (floor, acc) :: tail if (floor + minInterval) > k =>
((k, acc + v) :: tail) -> ()
case state => ((k, v) :: state) -> ()
}
}
scala> mappes.toList traverseS f2(10) execZero
res1: scalaz.Id.Id[List[(Int, Int)]] = List((40,13000), (25,12000), (10,5000))