Scala how to get the key of Map where value as List - scala

I have Map like this:
val myMap = Map(
testKey1 -> List(testValue1, testValue2, testValue3....),
testKey2 -> List(testValue4, testValue5, testValue6....),
testKey3 -> List(testValue7, testValue8, testValue9....)
)
I want to do some exact matching of the list value and get the corresponding key of map.
Like example: I want to check if 'testValue9' is in this Map then I will get the key 'testKey3'.
I think it could be solvable by this way but I can't iterate through list's value to check the value is there or not.
Or could someone please give me some hints.
myMap foreach {
case (key, List(_)) => println( key )
case _ =>
}

If you are trying to find a single value in a Map you can use find:
myMap.find(_._2.contains(value)).map(_._1)
This will return Some(key) if the value is found, otherwise None.
If you think there may be multiple matching values, you can replace find with filter
myMap.filter(_._2.contains(value)).map(_._1)
or use collect
myMap.collect{ case (k, v) if v.contains(value) => k }
In both cases this will return a list of all matching keys and will depend on how Map is implemented.
Note that the filter option can be expressed using a for expression, which does exactly the same thing:
for { (k,v) <- myMap if v.contains(value) } yield k
In most cases it is just a question of style as to which is better, though the collect is likely to perform best.
Update
Raman Mishra helpfully points out that the filter version can be simplified to
myMap.filter(_._2.contains(value)).keys
This returns Set rather than List which is more appropriate because the order is not significant.

you want to do this i think:
Assuming Key is String and the value is List[String]
val keyContainsValue: immutable.Iterable[String] = myMap map {
case (key, value) => if (value.contains(testValue9)) key else ""
}
you can use empty string as the defalut value so that you can get the return type as iterable[String].
As i don't know the type of your key and value you can use option too. For that purpose like this.
val keyContainsValue: immutable.Iterable[Option[String]] = myMap map {
case (key, value) => if (value.contains(testValue9)) Some(key) else None
}
println(keyContainsValue.flatten) //you will get the list of keys which contains the value specified.

val searchValue = "testValue9"
myMap.collect{ case (key, values) if values.contains(searchValue) => key }

you can do something like
val getKeys = (k: String) => for (m<- myMap.keys;
v<- myMap(m);
if v==k) yield m

Related

chaage value of Map scala based in key

I have the following :
val dynamoItem = rowAsMap.mapValues {
v: Any => new AttributeValue().withS(v.toString)
}.asJava
I want to change the mapValues function and make it apply the appropriate function to value based on the Key
So if the Key is equal to "AER" I want it to do : v: Any => new AttributeValue().withN(v.toString)
and for other values of Key I wanted to make
v: Any => new AttributeValue().withS(v.toString)
I would avoid the x._n notation to access tuple elements wherever possible. This is essentially the same answer provided above but I find this more readable.
val dynamoItem = rowAsMap.map {
case("AER", value) => "AER" -> new AttributeValue().withN(value.toString)
case (key, value) => key -> new AttributeValue().withS(value.toString)
}
Note - I assume you are preserving keys and replacing values with new AttributeValue().withN(value.toString) or new AttributeValue().withS(value.toString)
If I understand, you want to transform rowAsMap values (which are Map() values).
val dynamoItem = rowAsMap.map { item =>
if (item._1 == "AER")
new AttributeValue().withN(item._2.toString)
else
new AttributeValue().withS(item._2.toString)
}

Scala create immutable nested map

I have a situation here
I have two strins
val keyMap = "anrodiApp,key1;iosApp,key2;xyz,key3"
val tentMap = "androidApp,tenant1; iosApp,tenant1; xyz,tenant2"
So what I want to add is to create a nested immutable nested map like this
tenant1 -> (andoidiApp -> key1, iosApp -> key2),
tenant2 -> (xyz -> key3)
So basically want to group by tenant and create a map of keyMap
Here is what I tried but is done using mutable map which I do want, is there a way to create this using immmutable map
case class TenantSetting() {
val requesterKeyMapping = new mutable.HashMap[String, String]()
}
val requesterKeyMapping = keyMap.split(";")
.map { keyValueList => keyValueList.split(',')
.filter(_.size==2)
.map(keyValuePair => (keyValuePair[0],keyValuePair[1]))
.toMap
}.flatten.toMap
val config = new mutable.HashMap[String, TenantSetting]
tentMap.split(";")
.map { keyValueList => keyValueList.split(',')
.filter(_.size==2)
.map { keyValuePair =>
val requester = keyValuePair[0]
val tenant = keyValuePair[1]
if (!config.contains(tenant)) config.put(tenant, new TenantSetting)
config.get(tenant).get.requesterKeyMapping.put(requester, requesterKeyMapping.get(requester).get)
}
}
The logic to break the strings into a map can be the same for both as it's the same syntax.
What you had for the first string was not quite right as the filter you were applying to each string from the split result and not on the array result itself. Which also showed in that you were using [] on keyValuePair which was of type String and not Array[String] as I think you were expecting. Also you needed a trim in there to cope with the spaces in the second string. You might want to also trim the key and value to avoid other whitespace issues.
Additionally in this case the combination of map and filter can be more succinctly done with collect as shown here:
How to convert an Array to a Tuple?
The use of the pattern with 2 elements ensures you filter out anything with length other than 2 as you wanted.
The iterator is to make the combination of map and collect more efficient by only requiring one iteration of the collection returned from the first split (see comments below).
With both strings turned into a map it just needs the right use of groupByto group the first map by the value of the second based on the same key to get what you wanted. Obviously this only works if the same key is always in the second map.
def toMap(str: String): Map[String, String] =
str
.split(";")
.iterator
.map(_.trim.split(','))
.collect { case Array(key, value) => (key.trim, value.trim) }
.toMap
val keyMap = toMap("androidApp,key1;iosApp,key2;xyz,key3")
val tentMap = toMap("androidApp,tenant1; iosApp,tenant1; xyz,tenant2")
val finalMap = keyMap.groupBy { case (k, _) => tentMap(k) }
Printing out finalMap gives:
Map(tenant2 -> Map(xyz -> key3), tenant1 -> Map(androidApp -> key1, iosApp -> key2))
Which is what you wanted.

scala map get the value by key with key case insensitive

I have a map of Map[String, Info], it contains keys which can be either uppercase or lowercase, like this:
person1: PersonInfo1
person2: PersonInfo2
PERSON1: PersonInfo1
i want to get the value for key 'person1', if nothing found I'll try with key of 'PERSON1', I tried this code:
val info = map.get(key) match {
case Some(personInfo) => personInfo
case None =>
map.get(key.toUpperCase()) match {
case Some(personInfo) => personInfo
case None => None
}
}
but this return info as type of Product with Serializable, how can I have info returned as type of PersonInfo? Is there a way in Scala that allow me to get value from map by key and ignore cases of the key?
There are comparators for sorted maps which allow getting from the map case insensitively. Example: https://scastie.scala-lang.org/PfHTh16CROag7PNrknx1sQ
val map = scala.collection.immutable.SortedMap("key1" -> 45, "Key2" -> 43, "KEY3" -> 42)(scala.math.Ordering.comparatorToOrdering(String.CASE_INSENSITIVE_ORDER))
map.get("key1") // Some(45)
map.get("key2") // Some(43)
map.get("key3") // Some(42)
map.get("key4") // None
Your actual problem can be fixed if you return Options on all cases, for example:
val info = map.get(key) match {
case somePi#Some(personInfo) => somePi
case None => map.get(key.toUpperCase()) match {
case Some(personInfo) => Some(personInfo)
case None => None
}
}
Note the somePi# => somePi parts for referring the expression or the Some(personInfo).
Probably worth explaining why you got this error message. I assume personInfo is a case class which implements Product and Serializable, just like None. The common type for them is Product with Serializable.
You could chain gets with orElse. I would create an extension method for that:
implicit class CaseInsensitiveGetMap[V] (m: Map[String,V]) {
def iget (key: String): Option[V] = m.get(key)
.orElse(m.get(key.toUpperCase())) //you can add more orElse in chain
}
Then you can just use it like:
map.iget("person2")
The reason you're getting Product with Serializable is because your code is trying to return either a String (if key is good) or an Option (i.e. None if key not found). Those two types are not compatible. You should decide if you want String (maybe an empty string if key not found) or Option (i.e. Some[String] or None).
See if this works for you. It returns an Option[String].
map.get(key).fold(pm.get(key.toUpperCase))(Some(_))
The 1st get() returns an Option. The fold()() unwraps the Option and either tries a 2nd get() with upper case key value, or, if the 1st get returned a value, the value is re-wrapped in an Option so that the types match up.
If, on the other hand, you want to return a String instead, you might do this.
map.getOrElse(key, pm.getOrElse(key.toUpperCase, ""))
You can do a find instead of a get but you may want to consider performance when doing this.
map.find(k => k._1.equalsIgnoreCase(key)) match {
case Some =>
case None =>
}

Is there an operation update(key) = Option(Value) in scala.Map?

You use either update(key) = value or remove(key) operations to update a Map. But can you embed desired operation in the value? This is what I currently do:
map.update(key) = {
case Some(value) => map += key -> value
case None => map -= key
}
Can I simply write map(key) = option?
If I understand your question correctly:
if the option has a value (Some(value)), you want to add the value to the map (with key key)
if the option has no value (None), you want to remove the key key from the map
It think this could be done with:
val newMap = option.map(value => map + key -> value).getOrElse(map - key)
If you use a mutable map, this will return a new map and will not update the map value.

How to convert keys in a Map to lower case?

I have a map where the key is a String and I need to change every key to lower case before work with this map.
How can I do it in Scala? I was thinking something like:
var newMap = scala.collection.mutable.Map[String, String]()
data.foreach(d => newMap +=(d._1.toLowerCase -> d._2))
What is the best approach for it? Thanks in advance.
The problem here is that you're trying to add the lower-cased keys to the mutable Map, which is just going to pile additional keys into it. It would be better to just use a strict map here, rather than a side-effecting function.
val data = scala.collection.mutable.Map[String, String]("A" -> "1", "Bb" -> "aaa")
val newData = data.map { case (key, value) => key.toLowerCase -> value }
If you really want to do it in a mutable way, then you have to remove the old keys.
data.foreach { case (key, value) =>
data -= key
data += key.toLowerCase -> value
}
scala> data
res79: scala.collection.mutable.Map[String,String] = Map(bb -> aaa, a -> 1)
Your approach would work, but in general in Scala for this kind of transformation the immutable variants of the collections are preferred.
You can use the map method of the Map class with the following one liner:
val m = Map("a"->"A", "B"->"b1", "b"->"B2", "C"->"c")
m.map(e=>(e._1.toLowerCase,e._2))