chaage value of Map scala based in key - scala

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)
}

Related

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 how to get the key of Map where value as List

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

How to use map / flatMap on a scala Map?

I have two sequences, i.e. prices: Seq[Price] and overrides: Seq[Override]. I need to do some magic on them yet only for a subset based on a shared id.
So I grouped them both into a Map each via groupBy:
I do the group by via:
val pricesById = prices.groupBy(_.someId) // Int => Seq[Cruise]
val overridesById = overrides.groupBy(_.someId) // // Int => Seq[Override]
I expected to be able to create my wanted sequence via flatMap:
val applyOverrides = (someId: Int, prices: Seq[Price]): Seq[Price] => {
val applicableOverrides = overridesById.getOrElse(someId, Seq())
magicMethod(prices, applicableOverrides) // returns Seq[Price]
}
val myPrices: Seq[Price] = pricesById.flatMap(applyOverrides)
I expected myPrices to contain just one big Seq[Price].
Yet I get a weird type mismatch within the flatMap method with NonInferedB I am unable to resolve.
In scala, maps are tuples, not a key-value pair.
The function for flatMap hence expects only one parameter, namely the tuple (key, value), and not two parameters key, value.
Since you can access first element of a tuple via _1, the second via _2 and so on, you can generate your desired function like so:
val pricesWithMagicApplied = pricesById.flatMap(tuple =>
applyOverrides(tuple._1, tuple._2)
Another approach is to use case matching:
val pricesWithMagicApplied: Seq[CruisePrice] = pricesById.flatMap {
case (someId, prices) => applyOverrides(someId, prices)
}.toSeq

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.

What data type do I get when I iterate over a map?

I have an application which has to gather some external data first, then turn them into objects. Afterwards, it will do some analysis on the data.
I managed to gather the data and put it into a map. The map contains a unique key for each of the future objects, and a ListBuffer of the data needed to build the object.
Now I want to create a list of objects from this map, and don't know how to get my data out of the map. I haven't worked with maps before (yes, I am that new to the language), but found a question which says that, when I want to access an element of the map with head, I get a tuple of the key and the value. I hoped that I get the same when I iterate over the map with map (the method), but this doesn't appear to work. And I looked in Programming with Scala, but couldn't find a place saying what I get when I iterate over a map.
Here is an MWE for what I want to do:
//This code will gather number names from different languages and then create objects of type Number containing each name.
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
class Number (val theNumber: Int, val names: List[String]) {
override def toString = theNumber + " is known as " + names.mkString(", ") + "."
}
// Construct a map holding example data
val numbersAsMap = mutable.Map.empty[Int, ListBuffer[String]]
numbersAsMap += (1 -> new ListBuffer[String])
numbersAsMap += (2 -> new ListBuffer[String])
numbersAsMap += (3 -> new ListBuffer[String])
numbersAsMap(1) += "one"
numbersAsMap(1) += "eins"
numbersAsMap(1) += "uno"
numbersAsMap(2) += "two"
numbersAsMap(2) += "zwei"
numbersAsMap(2) += "due"
numbersAsMap(3) += "three"
numbersAsMap(3) += "drei"
numbersAsMap(3) += "tre"
// Create a list of numbers
numbersAsMap map ((key, value) => new Number(key, value.toList)).toList
// error: missing parameter type
// obviously I'm not getting tuples, let's try it another way
numbersAsMap.keys map (key => new Number(key, numbersAsMap(key).toList)).toList
// it throws the same error as above :(
The map method of Map complies with the map method of other collections, so it's body only gets one parameter. In case of a Map, this is a tuple consisting of the key and the value.
So you can write:
numbersAsMap.map(kv => new Number(kv._1, kv._2.toList)).toList
If you want to name the tuple values:
numbersAsMap.map {
kv =>
val (key, value) = kv
new Number(key, value.toList)
}.toList
But there is another option to write it nicely in a single line: Use a partial function:
numbersAsMap.map { case (key, value) => new Number(key, value.toList) }.toList
A { case ... } defines a partial function; and this way you can extract the values of the tuple.
Here are two possible ways to do the map operation on your Map:
val result = numbersAsMap.map{
case (key, value) =>
new Number(key, value.toList)
}.toList
val result2 = numbersAsMap.map(kv => new Number(kv._1, kv._2.toList)).toList