Convert string with nested delimiters into a map - scala

I want to turn this String:
"1:a|2:b|3:c"
into this:
Map(1 -> "a", 2 -> "b", 3 -> "c")
I have a procedural method that works, but it seems there ought to be a more functional approach.

val a = [YOUR STRING]
a.split('|').map(_.split(':')).map(a => (a(0) -> a(1))).toMap

val s = "1:a|2:b|3:c"
"(\\d+)\\:(\\w+)".r.findAllMatchIn(s).map( m => (m.group(1).toInt -> m.group(2)) ).toMap

Related

Compare nested maps in Scala

val mapa = Map("a" -> Array(Map("b" -> "c", "d" -> Array("e"))))
val mapa2 = Map("a" -> Array(Map("b" -> "c", "d" -> Array("e"))))
Is there way how to get key and value from both same maps and compare them?
or how to get all key from map with such structure?

Scala flatten a map with list as key and string as value

I have a peculiar case where I want to declare simple configuration like so
val config = List((("a", "b", "c"), ("first")),
(("d", "e"), ("second")),
(("f"), ("third")))
which at run time, I would like to have a map, which maps like
"a" -> "first"
"b" -> "first"
"c" -> "first"
"d" -> "second"
"e" -> "second"
"f" -> "third"
Using toMap, I was able to convert the config to a Map
scala> config.toMap
res42: scala.collection.immutable.Map[java.io.Serializable,String] = Map((a,b,c) -> first, (d,e) -> second, f -> third)
But I am not able to figure out how to flatten the list of keys into keys so I get the final desirable form. How do I solve this?
If you structure your config using List the code is very simple:
val config = List(
(List("a", "b", "c"), ("first")),
(List("d", "e"), ("second")),
(List("f"), ("third")))
config.flatMap{ case (k, v) => k.map(_ -> v) }.toMap
You can try the solution below:
val config = List(
(("a", "b", "c"), ("first")),
(("d", "e"), ("second")),
(("f"), ("third")))
val result = config.map {
case (k,v) =>
(
k.toString().replace(")", "")
.replace("(", "")
.split(","), v)
}
val res = result.map {
case (key,value) => key.map{ data =>
(data,value)
}.toList
}.flatten.toMap
In case you change the config structure to something like below, solution is much more simpler:
val config1 = List (
(List("a", "b", "c"), "first"),
(List("d", "e"), "second"),
(List("f"), "third")
)
config1.flatMap{
case (k,v) => k.map{data => (data,v)}
}.toMap
I think the above answers are good practical answers. If you're in a situation where you have no control over the input and you're stuck with Tuples instead of Lists, I'd do it this way:
val result: Map[String, String] = config.flatMap {
case (s: String, v) => List(s -> v)
case (ks: Product, v) => ks.productIterator.collect { case s: String => s -> v }
case _ => Nil //Prevent throwing
}.toMap
This will throw away anything that's not a String in the keys.
by using in built spark sql functions
val config = List((Array("a", "b", "c"), ("first")),
(Array("d", "e"), ("second")),
(Array("f"), ("third"))).toDF(List("col1","col2") : _*)
config.withColumn("exploded",functions.explode_outer($"col1")).drop("col1").show()

toMap on List of List generates error

Given that this example:
val myList = List("age=21", "name=xyz", "profession=Tester", "city=cuba", "age=43", "name=abc", "profession=Programmer", "city=wellington")
val myMap = myList.map(text => text.split("=")).map(a => (a(0) -> a(1))).toMap
works fine, returning:
myList: List[String] = List(age=21, name=xyz, profession=Tester, city=cuba, age=43, name=abc, profession=Programmer, city=wellington)
myMap: scala.collection.immutable.Map[String,String] = Map(age -> 43, name -> abc, profession -> Programmer, city -> wellington)
I am wondering why the following which is just N sets of values:
val myList = List("age=21", "name=xyz", "profession=Tester", "city=cuba", "age=43", "name=abc", "profession=Programmer", "city=Sydney")
val myMap = myList.grouped(4).toList.map(text => text.split("=")).map(a => (a(0) -> a(1))).toMap
generates the error, and how to solve:
notebook:9: error: value split is not a member of List[String]
val myMap = myList.grouped(4).toList.map(text => text.split("=")).map(a => (a(0) -> a(1))).toMap
I must be missing something elementary here.
myList.grouped(4).toList returns a nested list – List[List[String]].
To transform the grouped sublists into Maps:
val myMap = myList.grouped(4).toList.
map(_.map(_.split("=")).map(a => (a(0) -> a(1))).toMap)
// myMap: List[scala.collection.immutable.Map[String,String]] = List(
// Map(age -> 21, name -> xyz, profession -> Tester, city -> cuba),
// Map(age -> 43, name -> abc, profession -> Programmer, city -> Sydney)
// )

Scala For Comprehension with Filter

I am using Scala's for comprehension to produce a modified facetFilter. If a value in facetFilter doesn't exist in allFacets, it should be filtered out. Currently, the newFacetFilter doesn't filter at all.
val allFacets = Map(
"band_material" -> Map("Rubber" -> 11),
"dial_color" -> Map("Ivory" -> 68, "Salmon"-> 3))
val facetFilter =
Map("band_material" -> List("Yellow Gold Plated", "Rubber"),
"dial_color" -> List("Ivory"))
val newFacetFilter =
for {
(k,v) <- allFacets
(facetName, facetArr) <- facetFilter
aFacet <- facetArr
if k != facetName || !v.contains(aFacet)
} yield (facetName -> facetArr)
Current Output of newFacetFilter:
Map("band_material" -> List("Yellow Gold Plated", "Rubber"), "dial_color" -> List("Ivory"))
Expected Output of newFacetFilter:
Map("band_material" -> List("Rubber"), "dial_color" -> List("Ivory"))
See this fiddle
Try this:
val newFacetFilter =
for ((k,vs) <- facetFilter)
yield (k, vs filter allFacets(k).contains)
Output:
Map(band_material -> List(Rubber), dial_color -> List(Ivory))
OK, if we are done with edits, I think this is what you want...
val allFacets = Map(
"band_material" -> Map(
"Rubber" -> 11
),
"dial_color" -> Map(
"Ivory" -> 68,
"Salmon"-> 3
)
)
val facetFilter = Map(
"band_material" -> List("Yellow Gold Plated", "Rubber"),
"dial_color" -> List("Ivory"),
"case_material" -> List(),
"movement" -> List(),
"price_range" -> List(),
"gender" -> List()
)
val newFacetFilter = for {
(facetName, facetArr) <- facetFilter
(k,v) <- allFacets
if k == facetName
} yield (facetName, facetArr intersect v.keys.toList)
We simply iterate both maps and when we have the same keys, we intersect the two lists.
Edit: There is a more efficient way, using the Map's get function instead of just iterating everything and ignoring non-matches.
val newFacetFilter = facetFilter.flatMap {
case (n, fs) =>
allFacets.get(n).map(n -> _.keys.toList.intersect(fs))
}
So we take each facetFilter entry ((n, fs)), check allFacets for n, then intersect the optional result with our list fs. If n did not exist, we propagate None and it is flattened out by flatMap.

Returning an immutable map containing a single modified entry from another one

I have an immutable map from which I wish to change a single element and then return another immutable map. Am I stuck with copying all elements twice?
val inmap = Map('a'->1,'b'->2)
import collection.mutable
val mmap = mutable.Map(inmap.toList:_*)
mmap('b')= 3
val mmap2 = Map(mmap.toList:_*)
mmap2: scala.collection.immutable.Map[Char,Int] = Map(b -> 3, a -> 1)
Just wondering if that were the 'required' price for immutability on the 'frontiers' of our methods.
You could use the updated method:
scala> val inmap = Map('a' -> 1, 'b' -> 2)
inmap: scala.collection.immutable.Map[Char,Int] = Map(a -> 1, b -> 2)
scala> val updated = inmap.updated('b', 3)
updated: scala.collection.immutable.Map[Char,Int] = Map(a -> 1, b -> 3)
API doc for Map: look to the updated method.