Scala - Transform a Iterator into a Map - scala

How to get from an Iterator like this
val it = Iterator("one","two","three","four","five")
a map like
Map(four -> 4, three -> 5, two -> 3, five -> 4, one -> 3)
var m = Map[String, Int]()
while (it.hasNext) {
val cell = it.next()
m += (cell -> cell.length())
}
this is a solution using var but I'd like to use just Immutable and val variable.
If I use the for yield statement the returning object would be a Iterator[Map] and I do not want that:
val m = for(i<- it if it.hasNext) yield Map(i->i.length())

You can just use map:
val m = it.map(c => c -> c.length).toMap

Related

How to avoid Duplicates in List.newBuilder Scala?

How do I avoid duplicates for this code:
val lastUpdatesBuilder = List.newBuilder[(String, Int)]
val somelist = List("a","a")
for (v <- somelist) {
lastUpdatesBuilder += v -> 1
}
println(lastUpdatesBuilder.result())
Result is List((a,1), (a,1)) and I want it to be List((a,1)) only.
Here you go:
object Demo extends App {
val lastUpdatesBuilder = Set.newBuilder[(String, Int)]
val somelist = List("a","a")
for (v <- somelist) {
lastUpdatesBuilder += v -> 1
}
println(lastUpdatesBuilder.result())
}
Tho i would suggest not to use mutable set you can do something like this.
val ans = somelist.map{ key =>
key -> 1
}.toMap
println(ans)
Or you can first remove the duplicate using distinct and then create a map out of it.
val somelist = List("a","a").distinct
val ans = somelist.map{ key =>
key -> 1
}.toMap
This is what the distinct method does.

Merge scala map and update common key based on condition

I wrote following code to merge map and update common keys. Is there any better way to write this
case class Test(index: Int, min: Int, max: Int, aggMin: Int, aggMax: Int)
def mergeMaps(oldMap: Map[Int, Test], newMap: Map[Int, Test]): Map[Int, Test] = {
val intersect: Map[Int, Test] = oldMap.keySet.intersect(newMap.keySet)
.map(indexKey => indexKey -> (Test(newMap(indexKey).index, newMap(indexKey).min, newMap(indexKey).max,
oldMap(indexKey).aggMin.min(newMap(indexKey).aggMin), oldMap(indexKey).aggMax.max(newMap(indexKey).aggMax)))).toMap
val merge = (oldMap ++ newMap ++ intersect)
merge
}
Here is my test case
it("test my case"){
val oldMap = Map(10 -> Test(10, 1, 2, 1, 2), 25 -> Test(25, 3, 4, 3, 4), 46 -> Test(46, 3, 4, 3, 4), 26 -> Test(26, 1, 2, 1, 2))
val newMap = Map(32 -> Test(32, 5, 6, 5, 6), 26 -> Test(26, 5, 6, 5, 6))
val result = mergeMaps(oldMap, newMap)
//Total elements count should be map 1 elements + map 2 elements
assert(result.size == 5)
//Common key element aggMin and aggMax should be updated, keep min aggMin and max aggMax from 2 common key elements and keep min and max of second map key
assert(result.get(26).get.aggMin == 1)//min aggMin -> min(1,5)
assert(result.get(26).get.aggMax == 6)//max aggMax -> max(2,6)
assert(result.get(26).get.min == 5)// 5 from second map
assert(result.get(26).get.max == 6)//6 from second map
}
Here's a slightly different take on a solution.
def mergeMaps(oldMap :Map[Int,Test], newMap :Map[Int,Test]) :Map[Int,Test] =
(oldMap.values ++ newMap.values)
.groupBy(_.index)
.map{ case (k,v) =>
k -> v.reduceLeft((a,b) =>
Test(k, b.min, b.max, a.aggMin min b.aggMin, a.aggMax max b.aggMax))
}
I could have followed the groupBy() with mapValues() instead of map() but that doesn't result in a pure Map.
Another version to do the same task.
def mergeMaps(oldMap: Map[Int, Test], newMap: Map[Int, Test]): Map[Int, Test] = {
(newMap ++ oldMap).map(key => {
val _newMapData = newMap.get(key._1)
if (_newMapData.isDefined) {
val _newMapDataValue = _newMapData.get
val oldMapValue = key._2
val result = Test(_newMapDataValue.index, _newMapDataValue.min, _newMapDataValue.max,
oldMapValue.aggMin.min(_newMapDataValue.aggMin), oldMapValue.aggMax.max(_newMapDataValue.aggMax))
(key._1 -> result)
} else (key._1 -> key._2)
})
}

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.

How does Scala's mutable Map update [map(key) = newValue] syntax work?

I'm working through Cay Horstmann's Scala for the Impatient book where I came across this way of updating a mutable map.
scala> val scores = scala.collection.mutable.Map("Alice" -> 10, "Bob" -> 3, "Cindy" -> 8)
scores: scala.collection.mutable.Map[String,Int] = Map(Bob -> 3, Alice -> 10, Cindy -> 8)
scala> scores("Alice") // retrieve the value of type Int
res2: Int = 10
scala> scores("Alice") = 5 // Update the Alice value to 5
scala> scores("Alice")
res4: Int = 5
It looks like scores("Alice") hits apply in MapLike.scala. But this only returns the value, not something that can be updated.
Out of curiosity I tried the same syntax on an immutable map and was presented with the following error,
scala> val immutableScores = Map("Alice" -> 10, "Bob" -> 3, "Cindy" -> 8)
immutableScores: scala.collection.immutable.Map[String,Int] = Map(Alice -> 10, Bob -> 3, Cindy -> 8)
scala> immutableScores("Alice") = 5
<console>:9: error: value update is not a member of scala.collection.immutable.Map[String,Int]
immutableScores("Alice") = 5
^
Based on that, I'm assuming that scores("Alice") = 5 is transformed into scores update ("Alice", 5) but I have no idea how it works, or how it is even possible.
How does it work?
This is an example of the apply, update syntax.
When you call map("Something") this calls map.apply("Something") which in turn calls get.
When you call map("Something") = "SomethingElse" this calls map.update("Something", "SomethingElse") which in turn calls put.
Take a look at this for a fuller explanation.
Can you try this: => to update list of Map
import java.util.concurrent.ConcurrentHashMap
import scala.collection.JavaConverters._
import scala.collection.concurrent
val map: concurrent.Map[String, List[String]] = new ConcurrentHashMap[String, List[String]].asScala
def updateMap(key: String, map: concurrent.Map[String, List[String]], value: String): Unit = {
map.get(key) match {
case Some(list: List[String]) => {
val new_list = value :: list
map.put(key, new_list)
}
case None => map += (key -> List(value))
}
}
The problem is you're trying to update immutable map. I had the same error message when my map was declared as
var m = new java.util.HashMap[String, Int]
But when i replaced the definition by
var m = new scala.collection.mutable.HashMap[String, Int]
the m.update worked.