How to find duplicate values in Map - scala

I have the following Map[String, Int]:
val m = Map[String, Int](
"[LOGIN-011]" -> 0,
"[LOGIN-103]" -> 3,
"[LOGIN-222]" -> 10,
"[ERROR-110]" -> 1,
"[ERROR-012]" -> 3,
...
)
How to find duplicated values in the Map and print the values with List[String] as follows:
3 -> List("[LOGIN-103]", "[ERROR-012]")

Try
m
.toSeq
.groupBy { case (key, value) => value }
.collect { case (key, values: List[(String, Int)]) if values.size > 1 => (key, values.map(_._1)) }
which outputs
HashMap(3 -> List([ERROR-012], [LOGIN-103]))
Here is Luis' one-liner:
m.groupBy(_._2).collect { case (key, group: Map[String, Int]) if group.size > 1 => (key, group.keySet) }

Following works in scala 2.13+ only
val map = Map (
"[LOGIN-011]" -> 0,
"[LOGIN-103]" -> 3,
"[LOGIN-222]" -> 10,
"[ERROR-110]" -> 1,
"[ERROR-012]" -> 3
)
val duplicateValues = map.groupMap(_._2)(_._1).filterNot(_._2.sizeIs == 1)
//Map(3 -> List([ERROR-012], [LOGIN-103]))

Related

How do I get a new list with mapped keys if exist else external key?

input:
type DeviceType = Any
val rh: Map[(String, String), String] = Map(("ss", "ex-ss") -> "MobileType", ("ud", "ex-ud") -> "DesktopType")
val lh: Map[String, DeviceType] = Map("ex-ss" -> 1, "ex-ud" -> "A#F4CC", "ex-zip" -> 30052)
Expected output:
val res:Map[String, DeviceType] = Map("ss" -> 1, "ud" -> "A#F4CC", "ex-zip" -> 30052)
So, basically you would need to create one more map to store connection between the tuples, from rh map.
Please, see code below for more details:
// Create device synonyms index, for instance: "ex-ss" -> "ss"
val deviceSynonyms: Map[String, String] = rh.keys.map(_.swap).toMap
val res: Map[String, DeviceType] = lh.map {
case (deviceName, deviceType) => deviceSynonyms.getOrElse(deviceName, deviceName) -> deviceType
}
Which will produce desired result:
Map(ss -> 1, ud -> A#F4CC, ex-zip -> 30052)
The solution is :
type DeviceType = Any
val rh: Map[(String, String), String] = Map(("ss", "ex-ss") -> "MobileType", ("ud", "ex-ud") -> "DesktopType")
val lh: Map[String, DeviceType] = Map("ex-ss" -> 1, "ex-ud" -> "A#F4CC", "ex-zip" -> 30052)
val rhKeySet = rh.keySet
val res: Map[String, DeviceType] = lh.map {
case (k,v) =>
if (rh.exists(_._1._2 == k)) {
val res = rhKeySet.filter(_._2 == k).head
res._1 -> v
}else{
k -> v
}
}
The output:
Map(ss -> 1, ud -> A#F4CC, ex-zip -> 30052)
Let me know if it helps!!

update keys in scala

I have to update keys in a map based on certain condition. values will not be modified.
I have ended up with this code, but doesn't look neat to me, is there any better alternative of doing this:
val newOpt = inOpt("type") match {
case "mydb" =>
inOpt map { case (key, value) => (
if (key.contains(XXXX)) {
key.replace("A", "B")
}
else if(...){..}
else {
key
}
, value)
}
}
All the updated keys along with old keys and values will be in newOpt.
Regards
try this
val m = Map(1->"hi",2->"Bye")
scala.collection.immutable.Map[Int,String] = Map(1 -> hi, 2 -> Bye)
Update the key 2 with 5
m.map{x=> if(x._1 == 2) (5 -> x._2) else x}
scala.collection.immutable.Map[Int,String] = Map(1 -> hi, 5 -> Bye)
At least you can separate the updateKey logic to a different function, also you might use pattern matching for if else.
val newMap = originalMap map { case (key, value) => (updatedKey(key), value)}
At least it might look cleaner but not necessarily better solution than yours.
class UpdateKeysSpecs extends FunSuite {
test("updates the keys") {
val originalMap : Map[String, String]= Map("key1" -> "value1",
"key2_old" -> "value2",
"key3_something" -> "value3")
val newMap = originalMap map { case (key, value) => (updatedKey(key), value)}
assert(newMap.keys == Set("key1", "key2_new", "key3_nothing"))
}
def updatedKey(key: String): String = {
key.contains("old") match {
case true => key.replace("old", "new")
case false => {
key.contains("something") match {
case true => key.replace("something", "nothing")
case false => key
}
}
}
}
}
For
val m = Seq((1,2),(11,22)).toMap
m: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 11 -> 22)
create a new map based in m with updated,
val m2 = m.updated(11,23)
m2: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 11 -> 23)

Inverting a Map[Long, Set[Long]) to a Map[Long, Long]

Trying to convert a Map[Long, Set[Long]] to a Map[Long, Long].
I tried this but having compile issues:
m.map(_.swap).map(k => k._1.map((_, k._2)))
Example:
Map(10 -> Set(1,2,3), 11 -> Set(4,5))
Should become:
Map(1 -> 10,
2 -> 10,
3 -> 10,
4 -> 11,
5 -> 11)
flatMap on Map[A,B] will "just work" with collections of tuples:
m.flatMap {case (k,v) => v.map(_ -> k)} // Map[Long,Long]
going from a Map[Long,Set[Long]] to a series of Set[(Long,Long)] that gets flattened to a Map[Long,Long].
Assuming in is your Map[Long, Set[Long]]:
in./:(Map.empty[Long, Long]) { case (acc, (key, values)) => acc ++ values.map(_ -> key) }
To clarify, seem like you have this:
Map(10 -> Set(1,2,3), 11 -> Set(4,5))
And you want to convert this map in another map, but with something like this:
Map(1 -> 10,
2 -> 10,
3 -> 10,
4 -> 11,
5 -> 11)
As you can see if the sets are not disjoint, some keys in the resulted map with be missing:
Having this in consideration, the code will look like this:
val m: Map[Long, Set[Long]] = Map(10l -> Set(1l,2l,3l), 11l -> Set(4l,5l))
m.map(_.swap).map(k => k._1.map((_, k._2)))
val foo: Iterable[(Long, Long)] = m.flatMap { t =>
val (key, value) = t
value.map(_ -> key)
}
val result: Map[Long, Long] = foo.toMap
This will invert your Map m from Map[Long, Set[Long]] to Map[Long, List[Long]].
m flatten {case(k, vs) => vs.map((_, k))} groupBy (_._1) mapValues {_.map(_._2)}
You haven't specified what should happen when different Set values contains some of the same Longs (i.e. Map(8 -> Set(1,2), 9 -> Set(2,3))). If you're sure that won't happen you can use the following adjustment.
m flatten {case(k, vs) => vs.map((_, k))} groupBy (_._1) mapValues {_.head._2}
Or even more simply:
m.flatten {case(k, vs) => vs.map((_, k))}.toMap

Convert List[Map[String,Map[String,Int]]] to Map[Int,Int] in Scala

Given the following:
val t: List[Map[String, Map[String, Int]]] = List(
Map("a" -> Map("m" -> 1, "l" -> 21)),
Map("a" -> Map("m" -> 2, "l" -> 22)),
Map("a" -> Map("m" -> 3, "l" -> 23)),
Map("a" -> Map("m" -> 4, "l" -> 24))
)
I want the result:
Map(1->21,2->22,3->23,4->24)
What I have so far is:
val tt = (for {
(k,v) <- t
newKey = v("m")
newVal = v("l")
} yield Map(newKey -> newVal)).flatten.toMap
But this does not type check so Im missing some basic understanding since I cant understand why not?
My questions are:
Why is my code faulty?
What would be the most idiomatic way to do the transformation I want?
You've got List[Map[...]], not Map[...] so you want to unpack that first.
val tt = (for {
map <- t
(k, v) <- map
} ...)
t
.iterator
.flatMap(_.values)
.map { v => v("m") -> v("l") }
.toMap

Scala Map Transformation

Can someone recommend a functional way to transform the map specified below from
Map("host.config.autoStart.powerInfo[1].startOrder" -> -1,
"host.config.autoStart.powerInfo[1].startAction" -> "None",
"host.config.autoStart.powerInfo[1].key" -> "vm-XXX",
"host.config.autoStart.powerInfo[0].key" -> "vm-YYY",
"host.config.autoStart.powerInfo[0].startOrder" -> -1,
"host.config.autoStart.powerInfo[0].startAction" -> "None")
to
Map("host.config.autoStart.powerInfo" -> Map(
1 -> Map("startOrder" -> -1,
"startAction" -> "None",
"key" -> "vm-639"),
0 -> Map("startOrder" -> -1,
"startAction" -> "None",
"key" -> "vm-641")))
Extract what is before the subscript and make that the key
Extract the number between the subscript [x] and make that the key of value
A one line (long) solution:
val R = """([^\[]+)\[(\d+)\]\.(.+)""".r
m.map{ case(R(h,i,k),v) => (h,i,k,v) }.groupBy(_._1).mapValues(_.groupBy(_._2).mapValues{ _.map{case(h,i,k,v) => (k,v)}.toMap} )
res1: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,Any]]] =
Map(host.config.autoStart.powerInfo ->
Map(1 -> Map(startAction -> None,
startOrder -> -1,
key -> vm-XXX),
0 -> Map(key -> vm-YYY,
startAction -> None,
startOrder -> -1)
))
Or write it more or less readable:
m.map{ case(R(h,i,k),v) => (h,i,k,v) }
.groupBy(_._1).mapValues{ value =>
value.groupBy(_._2).mapValues{ _.map{case(h,i,k,v) => (k,v)}.toMap}
}
Edit: added some comments to the code to make it easier to see what's going on
Copied from my REPL:
scala> val re = """(.+)\[(\d+)\]\.(.+)""".r // Creates a regex to grab the key values
re: scala.util.matching.Regex = (.+)\[(\d+)\]\.(.+)
scala> val m = Map("host.config.autoStart.powerInfo[1].startOrder" -> -1,"host.config.autoStart.powerInfo[1].startAction" -> "None","host.config.autoStart.powerInfo[1].key" -> "vm-XXX","host.config.autoStart.powerInfo[0].key" -> "vm-YYY","host.config.autoStart.powerInfo[0].startOrder" -> -1,"host.config.autoStart.powerInfo[0].startAction" -> "None")
m: scala.collection.immutable.Map[String,Any] = Map(host.config.autoStart.powerInfo[0].key -> vm-YYY, host.config.autoStart.powerInfo[0].startAction -> None, host.config.autoStart.powerInfo[0].startOrder -> -1, host.config.autoStart.powerInfo[1].startAction -> None, host.config.autoStart.powerInfo[1].startOrder -> -1, host.config.autoStart.powerInfo[1].key -> vm-XXX)
scala> val tempList = m map { // Construct a temporary list of Tuples with all relevant values
| case (key, value) => key match {
| case re(p, i, k) => (p, i, k, value)
| }}
tempList: scala.collection.immutable.Iterable[(String, String, String, Any)] = List((host.config.autoStart.powerInfo,0,key,vm-YYY), (host.config.autoStart.powerInfo,0,startAction,None), (host.config.autoStart.powerInfo,0,startOrder,-1), (host.config.autoStart.powerInfo,1,startAction,None), (host.config.autoStart.powerInfo,1,startOrder,-1), (host.config.autoStart.powerInfo,1,key,vm-XXX))
scala> val accumulator = Map[String, Map[String, Map[String, Any]]]()
accumulator: scala.collection.immutable.Map[String,Map[String,Map[String,Any]]] = Map()
scala> val result = tempList.foldLeft(accumulator) {
| case (acc, e) => {
| val middleMap = acc.getOrElse(e._1, Map[String, Map[String, Any]]())
| val innerMap = middleMap.getOrElse(e._2, Map[String, Any]())
| acc + (e._1 -> (middleMap + (e._2 -> (innerMap + (e._3 -> e._4)))))
| }}
result: scala.collection.immutable.Map[String,Map[String,Map[String,Any]]] = Map(host.config.autoStart.powerInfo -> Map(0 -> Map(key -> vm-YYY, startAction -> None, startOrder -> -1), 1 -> Map(startAction -> None, startOrder -> -1, key -> vm-XXX)))