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)
Related
How to Convert Seq[Option[Map[String, Any]]] to Option[Map[String, Any]]
So skip any option that is None, and keep the valid Maps and merge them. If every option is None, then the final option should be None as well.
A flatMap along with groupMapReduce, followed by an Option filter should do the job:
val listOfMaps: List[Option[Map[String, Any]]] =
List(Some(Map("a"->"p", "b"->2)), None, Some(Map("a"->"q", "c"->"r")))
val mergedMap = listOfMaps.
flatMap(_.getOrElse(Map.empty[String, Any])).
groupMapReduce(_._1)(t => List[Any](t._2))(_ ::: _)
// mergedMap: Map[String, List[Any]] =
// Map("a" -> List("p", "q"), "b" -> List(2), "c" -> List("r"))
Option(mergedMap).filter(_.nonEmpty)
// res1: Option[Map[String, List[Any]]] =
// Some(Map("a" -> List("p", "q"), "b" -> List(2), "c" -> List("r")))
A few notes:
groupMapReduce is available only on Scala 2.13+.
If you must stick to Seq instead of List, simply replace method ::: with ++ in groupMapReduce.
It is assumed that merging of the Maps means aggregating the Map values of a common key into a List. Replace groupMapReduce with toMap if keeping only one of the Map values of a common key is wanted instead.
This solution treats Some(Map(.empty[String, Any])) the same as None.
This is a one-line solution
val in: Seq[Option[Map[String, Any]]] = ???
val out: Option[Map[String, Any]] =
in.flatten.headOption.map(_ => in.flatten.reduce(_ ++ _))
The in.flatten.headOption is a simple way of getting Some if at least one of the elements is Some or None if they are all None.
The reduce just combines all the Maps into one.
This can clearly be optimised by avoiding the duplicate in.flatten call.
Here is an example with flatten,fold, ++ and a match at the end to provide Some or None.
baz.scala
package baz
object baz {
// fixtures
val x0 = Seq[Option[Map[String, Any]]]()
val x1 = Seq[Option[Map[String, Any]]](None)
val x2 = Seq[Option[Map[String, Any]]](Some(Map("a" -> 1, "b" -> "two")))
val x3 = Seq[Option[Map[String, Any]]](Some(Map("a" -> 1, "b" -> "two")), Some(Map("c" -> 3.0)), None)
def f(x: Seq[Option[Map[String, Any]]]) =
x.flatten.fold(Map[String, Any]())((a,b) => a ++ b) match { case m if m.isEmpty => None case m => Some(m) }
}
Sample run
bash-3.2$ scalac baz.scala && scala -classpath .
Welcome to Scala 2.13.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_66).
Type in expressions for evaluation. Or try :help.
scala> import baz.baz._
import baz.baz._
scala> x0 -> f(x0)
res0: (Seq[Option[Map[String,Any]]], Option[Map[String,Any]]) = (List(),None)
scala> x1 -> f(x1)
res1: (Seq[Option[Map[String,Any]]], Option[Map[String,Any]]) = (List(None),None)
scala> x2 -> f(x2)
res2: (Seq[Option[Map[String,Any]]], Option[Map[String,Any]]) = (List(Some(Map(a -> 1, b -> two))),Some(Map(a -> 1, b -> two)))
scala> x3 -> f(x3)
res3: (Seq[Option[Map[String,Any]]], Option[Map[String,Any]]) = (List(Some(Map(a -> 1, b -> two)), Some(Map(c -> 3.0)), None),Some(Map(a -> 1, b -> two, c -> 3.0)))
scala> :quit
I have a map that contains a few HTTP parameters that will be sent to an API.
val sortedParameters: SortedMap[String, String] = SortedMap(
"oauth_nonce" -> nonce,
"oauth_callback" -> callbackURL,
"oauth_signature_method" -> signatureMethod,
"oauth_consumer_key" -> consumerKey
)
The above parameters have to be URL encoded and concatenated in the form key1=value1&key2=value2 etc.
What would be the best idiomatic way to achieve this in Scala?
Pretty much the same as the other answer but including encoding.
scala> import scala.collection.immutable.SortedMap
import scala.collection.immutable.SortedMap
scala> val enc = (s: String) => java.net.URLEncoder.encode(s, "utf-8")
enc: String => String = $$Lambda$1060/160696258#6c796cc1
scala> val sortedMap = SortedMap("a" -> "b&&c means both b and c are true", "c" -> "d=1")
sortedMap: scala.collection.immutable.SortedMap[String,String] = Map(a -> b&&c means both b and c are true, c -> d=1)
scala> sortedMap.map(kv => s"${enc(kv._1)}=${enc(kv._2)}").mkString("&")
res2: String = a=b%26%26c+means+both+b+and+c+are+true&c=d%3D1
EDIT:
And a more idiomatic destructuring from a comment:
sortedMap.map({ case (k, v) => s"${enc(k)}=${enc(v)}" }).mkString("&")
res2: String = a=b%26%26c+means+both+b+and+c+are+true&c=d%3D1
.map() on each elem to create k=v pattern then concat them using TraversableOnce#foldLeft(z)(op) or TraversableOnce#mkString(separator)
example,
scala> import scala.collection.SortedMap
import scala.collection.SortedMap
scala> val sortedParameters = SortedMap("a" -> 1, "b" -> 2, "c" -> 3)
sortedParameters: scala.collection.SortedMap[String,Int] = Map(a -> 1, b -> 2, c -> 3)
using mkString,
scala> sortedParameters.map(kv => kv._1 + "=" + kv._2).mkString("&")
res1: String = a=1&b=2&c=3
using foldLeft,
scala> sortedParameters.map(kv => kv._1 + "=" + kv._2)
.foldLeft(new String)((a, b) => { if(a.equals("")) b else a + "&" + b})
res2: String = a=1&b=2&c=3
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)
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
Given a Map[Int, Set[Int]], how can I modify a single value of the Map, generating a new one in the process, for example:
val x = Map(1 -> Set(1,2,3))
x(1) + 5 // This creates a new Set, but not a new Map
val y = x(1) change { x => x + 5 }
// The previous functionality is what I'm looking for
// z: Set[Int]] = List(Set(1, 2, 3, 5))
As Robin Green suggests, lenses are made for this job. In fact, you want a partial lens, since a map is a partial function of key -> value.
Scalaz 7 includes the mapVPLens function to make a partial lens (PLens) to the value at a chosen key:
import scalaz.PLens._
val x = Map(1 -> Set(1,2,3))
mapVPLens(1) mod ((_: Set[Int]) + 5, x) // Map(1 -> Set(1, 2, 3, 5))
Modifying the value at a non-existent key will have no effect:
mapVPLens(9) mod ((_: Set[Int]) + 5, x) // Map(1 -> Set(1,2,3))
In scala 2.10:
implicit class ChangeableMap[K,V]( val m: Map[K,V] ) extends AnyVal {
def change( k: K )( transform: V => V ): Map[K,V] = {
m.get( k ).map{ v => m + (k-> transform(v)) }.getOrElse( m )
}
}
Some test:
scala>val x = Map(1 -> Set(1,2,3), 2 -> Set(4,5))
x: scala.collection.immutable.Map[Int,scala.collection.immutable.Set[Int]] = Map(1 -> Set(1, 2, 3), 2 -> Set(4, 5))
scala> x.change(1) { x => x + 5 }
res1: Map[Int,scala.collection.immutable.Set[Int]] = Map(1 -> Set(1, 2, 3, 5), 2 -> Set(4, 5))
If you're in scala 2.9, this will do:
class ChangeableMap[K,V]( m: Map[K,V] ) {
def change( k: K )( transform: V => V ): Map[K,V] = {
m.get( k ).map{ v => m + (k-> transform(v)) }.getOrElse( m )
}
}
implicit def toChangeableMap[K,V]( m: Map[K,V] ) = new ChangeableMap[K,V]( m )
Use lenses!
However, Scalaz 6, which defines lenses, doesn't have a specific pre-made lens for your situation, which means slightly more work for you - though if your Map is in turn contained in another object, it does have (well-hidden) support for that situation. And Scalaz 7 will have a lens for standalone Maps.
Also, lenses are just pairs of functions, requiring no language support, so you could just roll your own.
Here's one from our codebase.
/**
* Alters a value in a map.
*
* modifyMap :: Map k v -> k -> (Maybe v -> Maybe v) -> Map k v
* See Haskell's Data.Map.alter
*
* #param m the map to modify
* #param key the key to modify the value of
* #param mod a function that takes the existing value (if any) and returns an optional new value
*
* #return the modified map
*/
def modifyMap[K,V](m: Map[K,V], key: K)
(mod: (Option[V] ⇒ Option[V])): Map[K,V] = {
mod(m.get(key)) match {
case Some(newVal) ⇒ m + (key → newVal)
case None ⇒ m - key
}
}
And here's how you use it:
modifyMap(myMap, "someKey") {
case Some(someVal) =>
// present
if (condition)
Some(valueDerivedFrom(someVal)) // provide a new mapping for someKey
else
None // someKey will now be unset
case None =>
// wasn't present
if (condition)
Some(newValue) // provide a new value for someKey
else
None // leave someKey unset
}
A very idiomatic way of solving this problem would be the following (thanks Viktor Klang):
val x = Map(1 -> Set(1,2,3), 2 -> Set(1), 3 -> Set(5))
x.map { case (1, v) => (1, v + 5); case x => x }
// res0: Map(1 -> Set(1, 2, 3, 5))
Or nicely packed into a class as well as an implicit:
class ChangeableMap[K,V](map:Map[K,V]) {
def change(index:K)(f:V => V) = map.map {
case (`index`, v:V) => (index, f(v))
case x => x
}
}
object ChangeableMap {
implicit def fromMap[K,V](map:Map[K,V]) = new ChangeableMap(map)
}
With the previous declaration, the following will work:
x.change(1) { x => x + 5 }
x.change(1) { _ + 5 }
// res1: Map(1 -> Set(1, 2, 3, 5))
Note that this is probably not the fastest solution, given that Scala will (probably, haven't confirmed) iterate over the entire map!
A possibly faster implementation would be the following (though I have not verified if it is actually faster):
class ChangeableMap[K,V](map:Map[K,V]) {
def change(index:K)(f:V => V) = map.get(index) match {
case Some(x) => map + ((index, f(x)))
case None => map
}
}
I think the easiest way would be using scala.collection.mutable.Map.
import scala.collection.mutable.Map
val m = Map(1 -> Set(1,2,3))
m.update(1, m(1) + 5)
// now the Map looks like this: Map(1 -> Set(1,2,3,5))
If you get an immutable Map, you can simply convert it to a mutable one by using:
val n: collection.mutale.Map(m.toSeq: _*)
This also works the other way around, if you need to return an immutable Map.
As mentioned before you can use Partial Lens for this sort of problem, scalaz and Monocle implements it. Here is how you would do it with Monocle:
import monocle.syntax.taversal._ // to use |->>
import monocle.syntax.at._ // to use at
val x = Map(1 -> Set(1,2,3))
x |->> at(1) modify(_ + 5) == Map(1 -> Set(1,2,3,5))