I have a Map and add index in map by using
val indexByColName = sorted.view.zipWithIndex.toMap
How do I find index for a specific key?
Method indexWhere for instance in Array provides this semantics as follows,
implicit class keyIndexing[A,B](val m: Map[A,B]) extends AnyVal {
def keyIndex(key: A, value: B) = {
m.toArray.indexWhere{ case (k,v) => key == k && value == v }
}
}
Then for
val sorted = Map("a" -> 10, "b" -> 11, "c" -> 12)
scala> sorted.keyIndex("a",10)
res15: Int = 0
and for a non matching (or non existing) key and value,
scala> sorted.keyIndex("a",11)
res16: Int = -1
scala> sorted.keyIndex("z",11)
res19: Int = -1
Defining sorted and showing the output you want would help but I think this might do it.
scala> val sorted = Map("zero" -> 0, "one" -> 1, "two" -> 2)
sorted: scala.collection.immutable.Map[String,Int] = Map(zero -> 0, one -> 1, two -> 2)
scala> val indexByColName =sorted.view.zipWithIndex.toMap
indexByColName: scala.collection.immutable.Map[(String, Int),Int] = Map((zero,0) -> 0, (one,1) -> 1, (two,2) -> 2)
scala> indexByColName.get(("zero", 0)).get
res1: Int = 0
scala> indexByColName.get(("two", 2)).get
res3: Int = 2
Related
I'm working on getting the nearest key value in both ways with a given input integer.
Example:
The defined map is as below.
val m = Map(10 -> "W",20 -> "W",30 -> "I",40 -> "A",50 -> "P",60 -> "S",70 -> "A",80 -> "A",90 -> "A",100 -> "I",110 -> "A",120 -> "E")
The keys are integers and they increase in order.
If say, 95 is given as input, then I should get the tuple output as follows
((90->"A"), (100->"I"))
scala> m.map( x=> (x._1,x._2)).toList.sortBy(_._1).filter( _._1<=95 ).last
res74: (Int, String) = (90,A)
scala> m.map( x=> (x._1,x._2)).toList.sortBy(_._1).filter( _._1>=95 ).head
res75: (Int, String) = (100,I)
scala>
The Map size will be big in real scenario(1K) and I want to avoid the sortBy(). Are there any possible foldLeft or map solutions available to this?.
Here's a pretty straight forward solution. No sorting required.
def nearest[V](m :Map[Int,V], k :Int) :Seq[(Int,V)] =
m.get(k).fold {
val (before, after) = m.keys.partition(_ < k)
Seq(before.maxOption, after.minOption).flatten.map(x => (x,m(x)))
}(v => Seq((k,v)))
testing:
val m = Map(10 -> "W",20 -> "W",30 -> "I",40 -> "A",50 -> "P",60 -> "S",70 -> "A",80 -> "A",90 -> "A",100 -> "I",110 -> "A",120 -> "E")
nearest(m, -7) //res0: Seq[(Int, String)] = List((10,W))
nearest(m, 60) //res1: Seq[(Int, String)] = List((60,S))
nearest(m, 93) //res2: Seq[(Int, String)] = List((90,A), (100,I))
nearest(m, 121) //res3: Seq[(Int, String)] = List((120,E))
Here's one approach that leverages TreeMap's sorting and range query features as shown below:
def nearestValues(m: Map[Int, String], key: Int) = {
import scala.collection.immutable.TreeMap
val tm = TreeMap(m.toSeq: _*)
Seq(tm.to(key).lastOption, tm.from(key).headOption).flatten.distinct
}
val m = Map(
10 -> "W", 20 -> "W", 30 -> "I", 40 -> "A", 50 -> "P", 60 -> "S",
70 -> "A", 80 -> "A", 90 -> "A", 100 -> "I", 110 -> "A", 120 -> "E"
)
nearestValues(m, 95)
// res1: Seq[(Int, String)] = List((90,A), (100,I))
nearestValues(m, 20)
// res2: Seq[(Int, String)] = List((20,W))
nearestValues(m, 125)
// res3: Seq[(Int, String)] = List((120,E))
Note that the above method returns a Seq rather than a Tuple to accommodate cases of exact or one-sided matches. To return a Tuple, one could go with something similar to the following:
def nearestValues(m: Map[Int, String], key: Int) = {
import scala.collection.immutable.TreeMap
val tm = TreeMap(m.toSeq: _*)
Seq(tm.to(key).lastOption, tm.from(key).headOption) match {
case Seq(None, None) => (0 -> "", 0 -> "") // Default tuple for empty Map
case Seq(x, None) => (x.get, Int.MaxValue -> "")
case Seq(None, y) => (Int.MinValue -> "", y.get)
case Seq(x, y) => (x.get, y.get)
}
}
nearestValues(m, 95)
// res1: ((Int, String), (Int, String)) = ((90,A),(100,I))
nearestValues(m, 20)
// res2: ((Int, String), (Int, String)) = ((20,W),(20,W))
nearestValues(m, 125)
// res3: ((Int, String), (Int, String)) = ((120,E),(2147483647,""))
UPDATE:
Starting Scala 2.13, methods to and from for TreeMap are replaced with rangeTo and rangeFrom, respectively.
You can use sorted collection as Leo C proposed, however that requires collection construction and then search, so complexity of such algorithm will be O(n*log n).
Approaching this task in algorithmic way, you can calculate the difference for the keys and then find the closest negative and positive values to 0. In this case complexity will be lower O(n).
Below example returns nearest keys from the left and the right excluding exact match (you may change that by changing a condition in the filter):
val data = Map(1-> "a", 5->"b")
val key = 4
val diffs = data.keys.map(_ - key)
val rightKeyOpt = diffs.filter(_ > 0).reduceOption(_ min _).map(_ + key)
val leftKeyOpt = diffs.filter(_ < 0).reduceOption(_ max _).map(_ + key)
val result = (leftKeyOpt.map(k=> k->data(k)), rightKeyOpt.map(k=> k->data(k)))
println (leftKeyOpt, rightKeyOpt)
println result
I bet this could be done in a single line if you need that very much:)
For example, I have a Map[Integer,String] like
val map = Map(1 -> "a", 2 -> "b", 3 -> "c", 5 -> "d", 9 -> "e", 100 -> "z")
If given key is 2, then "b" is expected to return.
If given key is 50, then "e" and "z" are expected to return.
If given key is 0, then "a" is expected to return.
In other words, if the key exists in the Map the corresponding value should be returned. Otherwise the values of the closest smaller and larger keys should be returned (in the case no other key is smaller only the value of the closest larger key should be returned and vice versa).
How can this be accomplished?
Map doesn't preserve order hence I would suggest creating a method that:
converts the Map into a TreeMap
generates the lower/upper Map entries as Options in a list using to(key).lastOption and from(key).headOption, respectively
flattens the list and extracts the Map values:
Sample code as follows:
val map = Map(1->"a", 2->"b", 100->"z", 9->"e", 3->"c", 5->"d")
def closestValues(m: Map[Int, String], key: Int): Seq[String] = {
import scala.collection.immutable.TreeMap
val tm = TreeMap(m.toSeq: _*)
Seq( tm.to(key).lastOption, tm.from(key).headOption ).
flatten.distinct.map{ case (k, v) => v }
}
closestValues(map, 0)
// res1: Seq[String] = List(a)
closestValues(map, 2)
// res2: Seq[String] = List(b)
closestValues(map, 50)
// res3: Seq[String] = List(e, z)
closestValues(map, 101)
// res4: Seq[String] = List(z)
UPDATE:
Starting Scala 2.13, methods to and from for TreeMap are replaced with rangeTo and rangeFrom, respectively.
My 2-cents worth.
def getClose[K](m: Map[Int,K], k: Int): Seq[K] =
if (m.get(k).nonEmpty) Seq(m(k))
else {
val (below,above) = m.keys.partition(_ < k)
Seq( if (below.isEmpty) None else Some(below.max)
, if (above.isEmpty) None else Some(above.min)
).flatten.map(m)
}
I would recommend first converting the Map to a SortedMap since the order of the keys needs to be taken into account.
val map = Map(1->"a",2->"b",3->"c",5->"d",9->"e",100->"z")
val sortedMap = SortedMap[Int, String]() ++ map
After that, use the following method to get the closest values. The result is returned as a List.
def getClosestValue(num: Int) = {
if (sortedMap.contains(num)) {
List(sortedMap(num))
} else {
lazy val larger = sortedMap.filterKeys(_ > num)
lazy val lower = sortedMap.filterKeys(_ < num)
if (larger.isEmpty) {
List(sortedMap.last._2)
} else if (lower.isEmpty) {
List(sortedMap.head._2)
} else {
List(lower.last._2, larger.head._2)
}
}
}
Testing it with the following values:
println(getClosestValue(2))
println(getClosestValue(50))
println(getClosestValue(0))
println(getClosestValue(101))
will give
List(b)
List(z, e)
List(a)
List(z)
This not an efficient solution but you can do something like below
val map =Map(1->"a",2->"b",3->"c",5->"d",9->"e",100->"z")
val keyset = map.keySet
def getNearestValues(key: Int) : Array[String] = {
if(keyset.contains(key)) Array(map(key))
else{
var array = Array.empty[String]
val less = keyset.filter(_ < key)
if(!less.isEmpty) array = array ++ Array(map(less.toList.sortWith(_ < _).last))
val greater = keyset.filter(_ > key)
if(!greater.isEmpty) array = array ++ Array(map(greater.toList.sortWith(_ < _).head))
array
}
}
A small bit of functional way
val map =Map(1->"a",2->"b",3->"c",5->"d",9->"e",100->"z")
val keyset = map.keySet
def getNearestValues(key: Int) : Array[String] = keyset.contains(key) match {
case true => Array(map(key))
case false => {
val (lower, upper) = keyset.toList.sortWith(_ < _).span(x => x < key)
val lowArray = if(lower.isEmpty) Array.empty[String] else Array(map(lower.last))
val upperArray = if(upper.isEmpty) Array.empty[String] else Array(map(upper.head))
lowArray ++ upperArray
}
}
getNearestValues(0) should return Array(a) and getNearestValues(50) should return Array(e, z) and getNearestValues(9) should return Array(e)
You can solve this problem with the complexity smaller that in any proposed solutions above . So, if performance is critical, check this answer.
Another Scala solution
val m = Map(1 -> "a", 2 -> "b", 3 -> "c", 5 -> "d", 9 -> "e", 100 -> "z")
List(0, 2, 50, 101).foreach { i => {
val inp = i
val (mn, mx) = if (m.get(inp).nonEmpty) (Map(inp -> m(inp)), Map(inp -> m(inp))) else m.partition(x => x._1 > inp)
(mn, mx) match {
case (x, y) if y.isEmpty => println(m(mn.keys.min))
case (x, y) if x.isEmpty => println(m(mx.keys.max))
case (x, y) if y == x => println(m(inp))
case (x, y) => println(m(mn.keys.min), m(mx.keys.max))
}
}
}
Results:
a
b
(z,e)
z
I am new to scala. As title, I would like to create a mutable map Map[Int,(Int, Int)] and with default value as tuple (0,0) if key not exist. In python the "defaultdict" make such effort easy. what is the elegant way to do it in Scala?
Use withDefaultValue after creating the map:
import scala.collection.mutable
val map = mutable.Map[Int,(Int, Int)]().withDefaultValue((0, 0))
you are probably lookign for .getOrElseUpdate which takes the key, if not present updates with given value.
scala> val googleMap = Map[Int, (Int, Int)]().empty
googleMap: scala.collection.mutable.Map[Int,(Int, Int)] = Map()
scala> googleMap.getOrElseUpdate(100, (0, 0))
res3: (Int, Int) = (0,0)
scala> googleMap
res4: scala.collection.mutable.Map[Int,(Int, Int)] = Map(100 -> (0,0))
You can also pass the orElse part implicitly,
scala> implicit val defaultValue = (0, 0)
defaultValue: (Int, Int) = (0,0)
scala> googleMap.getOrElseUpdate(100, implicitly)
res8: (Int, Int) = (0,0)
scala> googleMap
res9: scala.collection.mutable.Map[Int,(Int, Int)] = Map(100 -> (0,0))
withDefaultValue is much simpler than getOrElseUpdate.
import scala.collection.mutable
var kv1 = mutable.Map[Int, Int]().withDefaultValue(0)
var kv2 = mutable.Map[Int, Int]()
kv1(1) += 5 // use default value when key is not exists
kv1(2) = 3
kv2(2) = 3 // both can assign value to a new key.
println(f"kv1(1) ${kv1(1)}, kv1(2) ${kv1(2)} " )
println(f"kv1 ${kv1}")
kv2.getOrElseUpdate(1, 18) // set a default if key not exists
println(f"kv2(1) ${kv2(1)}, kv2(2) ${kv2(2)}")
println(f"kv2 ${kv2}")
Output:
kv1(1) 5, kv1(2) 3
kv1 Map(2 -> 3, 1 -> 5)
kv2(1) 18, kv2(2) 3
kv2 Map(2 -> 3, 1 -> 18)
Sometimes I use a Map as a memoization cache. With mutable maps, I use getOrElseUpdate:
mutableMap.getOrElseUpdate(key, {
val value = <compute the value>
value
})
Immutable maps don't have getOrElseUpdate. So I want to do this
immutableMap.getOrElse(key, {
val value = <compute the value>
immutableMap += key -> value
value
})
This seems to work in practice, I have good arguments to believe it works in theory, and it's more or less readable -- is it a terrible idea for some reason I'm missing?
The other alternatives I'm considering are
immutableMap.get(key) match {
case Some(value) => value
case None =>
val value = <compute the value>
immutableMap += key -> value
value
}
which is not much different and is more cumbersome, or
if (immutableMap.contains(key)) {
immutableMap(key)
} else {
val value = <compute the value>
immutableMap += key -> value
value
}
which is the dumbest and probably least idiomatic.
In principle I rather not go for a solution that uses a helper to return the value and the updated map, unless it's the unarguably superior way.
Sure, it seems reasonable except for one small issue... it's not updating your collection! If you're using an immutable Map, then that Map is immutable. You can not change it, ever.
In fact, immutable Map from Scala collection does not even have a += method defined on it, see immutable.Map. All the methods with "append" or "add" new values to the Map actually return a new Map. So for what you've written above to compile, you'd have to not be using something immutable.
To do this with an immutable map, you'll need to work with a var and replace that var with the new Map (which can lead to issues with threading) or you have to adopt a State Monad type pattern in which you return not only the new value but also the new Map.
def getOrCalc(m: Map[Key, Value], k: Key)(f: Key => Value): (Map[Key, Value], Value] ={
if(m.contains(k)) (m, m(k))
else{
val value = f(k)
(m +: (k, value), value)
}
}
My only recommendation (regarding the reasons why you choosed var instead of mutable.Map or Java's ConcurrentMap) is to wrap it into DSL, like:
case class Mutable[K,V](var m: Map[K,V]) {
def orElseUpdate(key: K, compute: => V) = m.getOrElse(key, {
val value = compute
m += key -> value
value
})
}
scala> val a = Mutable(Map(1 -> 2))
a: Mutable[Int,Int] = Mutable(Map(1 -> 2))
scala> a.orElseUpdate(2, 4)
res10: Int = 4
scala> a.orElseUpdate(2, 6)
res11: Int = 4
scala> a.orElseUpdate(3, 6)
res12: Int = 6
Another option (if your computation is lightweight) is just:
m += key -> m.getOrElse(key, compute)
m(key)
Example:
scala> var m = Map(1 -> 2)
m: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)
scala> m += 3 -> m.getOrElse(3, 5)
scala> m
res1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5)
scala> m += 3 -> m.getOrElse(3, 5)
scala> m
res3: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5)
scala> m += 3 -> m.getOrElse(3, 6)
scala> m
res5: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5)
You can wrap it into DSL as well:
implicit class RichMap[K,V](m: Map[K,V]) {
def kvOrElse(k: K, v: V) = k -> m.getOrElse(k, v)
}
scala> m += m.kvOrElse(3, 7)
scala> m
res7: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5)
scala> m += m.kvOrElse(4, 7)
scala> m
res9: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5, 4 -> 7)
Scala immutable Map has a member mapValues, which allows to perform mapping on values only (not keys). When using immutable ListMap instead, this member is inherited, however it is not overridden, therefore it still returns a Map, not ListMap.
Is there some simple way how to implement mapValues for a ListMap?
In the following fragment, I want the return type to be a ListMap, not Map:
import scala.collection.immutable.ListMap
val lm = ListMap(1 -> "1", 0 -> "0", 2 -> "2")
lm.mapValues ( v => v+v )
How about:
def listMapValues[K,V1,V2](lm:ListMap[K,V1], f: V1 => V2) = lm.map { case (k,v1) => (k,f(v1))}
You can then use it like:
scala> listMapValues(lm, v => v + v)
res16: scala.collection.immutable.ListMap[Int,String] = Map(1 -> 11, 0 -> 00, 2 -> 22)
If you want to use it as infix method, just need to declare it an implicit class:
implicit class ListMapOps[K,V1](lm: ListMap[K,V1]) {
def listMapValues[V2](f: V1 => V2)= lm.map { case (k,v1) => (k,f(v1))}
}
scala> lm.listMapValues( v => v + v )
res17: scala.collection.immutable.ListMap[Int,String] = Map(1 -> 11, 0 -> 00, 2 -> 22)
You can do like this.
ListMap[Int, String]().empty ++ lm.mapValues(v => v + v)
Your code :
import scala.collection.immutable.ListMap
val lm: ListMap[Int, String] = ListMap(1 -> "1", 0 -> "0", 2 -> "2")
val result: ListMap[Int, String] = ListMap[Int, String]().empty ++ lm.mapValues(v => v + v)
Output at REPL :
import scala.collection.immutable.ListMap
lm: scala.collection.immutable.ListMap[Int,String] = Map(1 -> 1, 0 -> 0, 2 -> 2)
result: scala.collection.immutable.ListMap[Int,String] = Map(1 -> 11, 0 -> 00, 2 -> 22)