Simple Scala coding question - scala

Suppose I have list countries of type List[String] and map capitals of type Map[String, String]. Now I would like to write a functionpairs(countries:List[String], capitals:Map[String, String]):Seq[(String, String)]to return a sequence of pairs (country, capital) and print an error if the capital for some country is not found. What is the best way to do that?

To start with, your Map[String,String] is already a Seq[(String,String)], you can formalise this a bit by calling toSeq if you wish:
val xs = Map("UK" -> "London", "France" -> "Paris")
xs.toSeq
// yields a Seq[(String, String)]
So the problem then boils down to finding countries that aren't in the map. You have two ways of getting a collection of those countries that are represented.
The keys method will return an Iterator[String], whilst keySet will return a Set[String]. Let's favour the latter:
val countriesWithCapitals = xs.keySet
val allCountries = List("France", "UK", "Italy")
val countriesWithoutCapitals = allCountries.toSet -- countriesWithCapitals
//yields Set("Italy")
Convert that into an error in whatever way you see fit.

countries.map(x=>(x, capitals(x)))

Related

How to use result of scala's List::groupBy?

I have this code. PartnerReader.fetchParters() returns a List[Partner]. Partners have a country attribute that is a String, and a dates attribute that is a list of Calendar objects. I group all Partners by their country. I expect partnersByCountry is a Map[String, List[Partner]].
I then want to associate countries with all the dates from all their Partners. getAllDatesForPartners() returns a List[Calendar] resulting (hopefully) in a Map[String, List[Calendar]]. I attempt to do this with the assignment to allDatesForCountry, but this fails on the call to map with the error Cannot resolve overloaded method 'map'.
Why does this code not work and what's the correct way to do the transformation?
val partnersByCountry = PartnerReader.fetchPartners()
.groupBy(_.country)
val allDatesForCountry = partnersByCountry
.map((country: String, partners: List[Partner]) => {
country -> getAllDatesForPartners(partners)
})
def getAllDatesForPartners(partners: List[Partner]): List[Calendar] = ???
When you .map() over a Map[?,?] you get a sequence of tuples. To break each tuple into its constituent parts you need pattern matching.
.map{case (country: String, partners: List[Partner]) =>
country -> getAllDatesForPartners(partners)
}
Which is the long (documented) way to write...
.map(tup => (tup._1, getAllDatesForPartners(tup._2)))

Scala- read values from a map based on keys

is there any better way to read values from a map based on keys if I have more keys in a map?
currently I have a Map[String, List[String]] which can have more than 20 keys:
I am using below for retrieving values for each keys
val names= map.getOrElse("Name", List.empty)
.
.
.
val cities = map.getOrElse("City", List.Empty)
Please help If I can write this in better way.
I very much doubt you're doing yourself any favors by replicating the Map data into local variables.
One thing you could do is employ pattern matching to save some (not much) typing.
val knownKeys = List("Name", "City", "Country") // etc. etc.
val List(names
,cities
,countries
// etc. etc.
) = knownKeys.map(data.getOrElse(_, List()))
A major drawback to this idea is that the list of keys has to be in the exact same order as the order of variables in the extraction.
A better idea is to give your Map its own default.
val data = Map("City" -> List("NY","Rome")
,"Name" -> List("Ed","Al")
// etc. etc.
).withDefaultValue(List.empty[String])
Then you don't need .getOrElse().
data("City") // res0: List[String] = List(NY, Rome)
data("Airport") // res1: List[String] = List()

Correct Approach to Recursively Summing Map in Scala

I have just started a project in work where we are migrating some C# tooling across to a new Scala project. This is my first exposure to the language (and functional programming in general) so in the interest of not just writing Java style code in Scala, I am wondering what the correct approach to handling the following scenario is.
We have two map objects which represent tabular data with the following structure:
map1 key|date|mapping val
map2 key|number
The mapping value in the first object is not always populated. Currently these are represented by Map[String, Array[String]] and Map[String, Double] types.
In the C# tool we have the following approach:
Loop through key set in first map
For every key, check to see if the mapping val is blank
If no mapping then fetch the number from map 2 and return
If mapping exists then recursively call method to get full range of mapping values and their numbers, summing as you go. E.g. key 1 might have a mapping to key 4, key 4 might have a mapping to key 5 etc and we want to sum all of the values for these keys in map2.
Is there a clever way to do this in Scala which would avoid updating a list from within a for loop and recursively walking the map?
Is this what you are after?
#annotation.tailrec
def recurse(key: String, count: Double, map1: Map[String, String], map2: Map[String, Double]): Double = {
map1.get(key) match {
case Some(mappingVal) if mappingVal == "" =>
count + map2.getOrElse(mappingVal, 0.0)
case Some(mappingVal) =>
recurse(mappingVal, count + map2.getOrElse(mappingVal, 0.0), map1, map2)
case None => count
}
}
example use:
val m1: Map[String, String] = Map("1" -> "4", "4" -> "5", "5" -> "6", "8" -> "")
val m2: Map[String, Double] = Map("1" -> 1.0, "4" -> 4.0, "6" -> 10.0)
m1.map {
case (k, _) => k -> recurse(k, 0.0, m1, m2)
}.foreach(println)
Output:
(1,14.0)
(4,10.0)
(5,10.0)
(8,0.0)
Note that there is no cycle detection - this will never terminate if map1 has a cycle.

scala get first key from seq of map

In scala, I know the mySeq is an array of Map object and the array only has one element. then I want to get first key of this element. Why it doesn't work ? it gave me error: value keySet is not a member of (Int, String)
code:
val mySeq: Seq[(Int, String)] = ...
val aMap = mySeq(0)
val firstKey = aMap.keySet.head
That's actually a Seq of tuples:
val aTuple = mySeq(0)
val firstKey = aTuple._1
To declare a Seq or maps, you'd use:
val mySeq: Seq[Map[Int, String]] = ...
But note that it doesn't make much sense to get the first key of a map, since maps are usually unordered by design.

Typesafe keys for a map

Given the following code:
val m: Map[String, Int] = .. // fetch from somewhere
val keys: List[String] = m.keys.toList
val keysSubset: List[String] = ... // choose random keys
We can define the following method:
def sumValues(m: Map[String, Int], ks: List[String]): Int =
ks.map(m).sum
And call this as:
sumValues(m, keysSubset)
However, the problem with sumValues is that if ks happens to have a key not present on the map, the code will still compile but throw an exception at runtime. Ex:
// assume m = Map("two" -> 2, "three" -> 3)
sumValues(m, 1 :: Nil)
What I want instead is a definition for sumValues such that the ks argument should, at compile time, be guaranteed to only contain keys that are present on the map. As such, my guess is that the existing sumValues type signature needs to accept some form of implicit evidence that the ks argument is somehow derived from the list of keys of the map.
I'm not limited to a scala Map however, as any record-like structure would do. The map structure however won't have a hardcoded value, but something derived/passed on as an argument.
Note: I'm not really after summing the values, but more of figuring out a type signature for sumValues whose calls to it can only compile if the ks argument is provably from the list of keys the map (or record-like structure).
Another solution could be to map only the intersection (i.e. : between m keys and ks).
For example :
scala> def sumValues(m: Map[String, Int], ks: List[String]): Int = {
| m.keys.filter(ks.contains).map(m).sum
| }
sumValues: (m: Map[String,Int], ks: List[String])Int
scala> val map = Map("hello" -> 5)
map: scala.collection.immutable.Map[String,Int] = Map(hello -> 5)
scala> sumValues(map, List("hello", "world"))
res1: Int = 5
I think this solution is better than providing a default value because more generic (i.e. : you can use it not only with sums). However, I guess that this solution is less effective in term of performance because the intersection.
EDIT : As #jwvh pointed out in it message below, ks.intersect(m.keys.toSeq).map(m).sum is, to my opinion, more readable than m.keys.filter(ks.contains).map(m).sum.