Scala: Get largest number from List of tuples - list

If I had a list of (String,Double) tuples in Scala
(i.e. List[(String,Double)])
, how would I get the tuple with the largest Double value?

Just use maxBy:
val list: List[(String, Double)] = List(("a", 2.3), ("b", 3.5), ("c", 8.9))
list.maxBy(_._2)

There are a few ways to do it. The one that comes to mind is by doing a .sortBy(- _._2)
val t1 = ("string1", 1)
val t2 = ("string2", 5)
val t3 = ("string3", 12)
val t4 = ("string4", 3)
val t5 = ("string5", 8)
val myList = List(t1, t2, t3, t4, t5)
val largestTuple = myList.sortBy(- _._2).head
largestTuple: (String, Int) = (string3,12)

Also reduce can convey the semantics like this,
xs.reduce( (a,v) => if (v._2 > a._2) v else a)
Namely, scan the list and preserve the max value. Even so, maxBy is the way to go for simplicity.

Related

Sort a list of tuples by their first index in Elm

Assuming that I have a list of tuples that resemble the following example:
[(5, "a"), (1, "c"), (7, "d")]
in Elm, how do I go about sorting the list in ascending order by their first elements so that we get the following result?
[(1, "c"), (5, "a"), (7, "d")]
Using the Elm List documentation, it appears that the sortBy and sortWith functions will be useful for this case. My attempt at an implementation is as follows:
maxTuples : Tuple(a, b) -> Tuple(a, b) -> Tuple(a, b)
maxTuples t1 t2 =
case compare t1 t2 of
((Tuple.first t1) >= (Tuple.first t2)) -> GT
_ -> LT
sortByFirst : List (Tuple (a, b)) -> List (Tuple (a, b))
sortByFirst lst =
List.sortWith maxTuples lst
However, I'm getting compiler errors of the following nature:
I ran into something unexpected when parsing your code!
99| ((Tuple.first t1) >= (Tuple.first t2)) -> GT
^
I am looking for one of the following things:
an upper case name
My hunch would be that the compiler is looking for GT/LT/EQ per the API of List library, but if this the case I'm not sure how we would be able to use either sortBy or sortWith to sort a list of Tuples in Elm by the first index of each element.
You found the right functions. In your code, there are actually multiple problems:
The type annotations should be just (a, b), not Tuple(a, b).
You compare t1 with t2, which will compare the tuples lexicographically. You actually want compare (Tuple.first t1) (Tuple.first t2)
The case branches require a pattern before the ->. In this case would be something like EQ, since you are matching on the result of compare, which returns Order type.
You could fix the code like this:
maxTuples : (comparable, b) -> (comparable, b) -> (comparable, b)
maxTuples t1 t2 =
case compare (Tuple.first t1) (Tuple.first t2) of
GT -> GT
EQ -> EQ
_ -> LT
But now there is an unnecessary repetition, you are simply returning the result of compare function.
maxTuples t1 t2 =
compare (Tuple.first t1) (Tuple.first t2)
Together with the sort function, it would look like this:
sortByFirst lst =
List.sortWith (\t1 t2 -> compare (Tuple.first t1) (Tuple.first t2)) lst
It turns out this kind of operation is quite common, especially with lists of records. For this reason, Elm offers another function – sortBy. It takes a function and compares the elements after applying the function:
sortBy f lst =
List.sortWith (\a b -> compare (f a) (f b)) lst
You can therefore use sortBy function to greatly simplify the code:
sortByFirst : List (comparable, b) -> List (comparable, b)
sortByFirst =
sortBy Tuple.first
It is worth noting that what you want is the default behavior of List.sort (sort will use the first element of the tuple to order the list and if two elements have the same first element will move to compare the second and so on)
List.sort [(5, "a"), (1, "c"), (1, "a"), (7, "d")] == [(1,"a"),(1,"c"),(5,"a"),(7,"d")]
Using sortBy in this case is redundant.
I'm not sure what compare is in case compare t1 t2 of, but you can just use a straight if instead of a case (and also use destructuring instead of Tuple.first, if you prefer):
maxTuples ( val1, _ ) ( val2, _ ) =
if val1 >= val2 then
GT
else
LT
I have a complete working example at https://ellie-app.com/ZZ9Hzhg7yva1/2 (there were also some type annotation changes that needed to be made to get it compiling)
However, sortBy is the simpler option, because it can just take Tuple.first to sort by whatever is in there:
sortByFirst lst =
List.sortBy Tuple.first lst
That version is at https://ellie-app.com/ZZ9Hzhg7yva1/3

How to convert list to mutable map with pre processing in Scala?

I have a list (size of the list is variable):
val ids = List(7, 8, 9)
and would like to get the following map:
val result= Map("foo:7:bar" -> "val1",
"foo:8:bar" -> "val1",
"foo:9:bar" -> "val1")
everything in the map is hard-coded except ids and value is the same for everyone, but map has to be mutable, I'd like to update one of its values later:
result("foo:8:bar") = "val2"
val result= Map("foo:7:bar" -> "val1",
"foo:8:bar" -> "val2",
"foo:9:bar" -> "val1")
You can do that like this: First map over the list to produce a list of tuples, then call toMap on the result which will make an immutable Map out of the tuples:
val m = ids.map(id => ("foo:" + id + ":bar", "val1")).toMap
Then convert the immutable Map to a mutable Map, for example like explained here:
val mm = collection.mutable.Map(m.toSeq: _*)
edit - the intermediate immutable Map is not necessary as the commenters noticed, you can also do this:
val mm = collection.mutable.Map(ids.map(id => ("foo:" + id + ":bar", "val1")): _*)
Try this:
import scala.collection.mutable
val ids = List(7, 8, 9)
val m = mutable.Map[String, String]()
ids.foreach { id => m.update(s"foo:$id:bar", "val1") }
scala> m
Map(foo:9:bar -> val1, foo:7:bar -> val1, foo:8:bar -> val1)
You, don't need to create any intermediate objects, that map does.
Why not use foldLeft?
ids.foldLeft(mutable.Map.empty[String, String]) { (m, i) =>
m += (s"foo:$i:bar" -> "val1")
}
Try Like this:
scala> import scala.collection.mutable
scala> val result = mutable.Map[String, String]()
result: scala.collection.mutable.Map[String,String] = Map()
scala> val ids = List(7, 8, 9)
ids: List[Int] = List(7, 8, 9)
scala> for(x <- ids){
| result("foo:%d:bar".format(x)) = "val1"
| }
scala> result
res3: scala.collection.mutable.Map[String,String] = Map(foo:9:bar -> val1, foo:7:bar -> val1, foo:8:bar -> val1)

Scala Filter List[Int] Which Exists in other List of Tuples

I've a two lists dest (contains:x) and points (x,y)
dest:List[Int] and Points:List[(Int,Int)]
I want to filter elements in the dest, if it exists in points (x==points._1) i
var newl:List[Int] = List()
for(x<-dest) if(!points.filter(_._1==x).isEmpty) newl=newl:+x
I feel like there must be a better concise way with exists but tuple making it complex. So whats the best way to do the above?
Here is a concise way:
val dest= List(1,2,4,5)
val points = List((1,3), (2,3) , (3,4))
val newl = dest.filter{d => points.exists(_._1 == d)} // returns List(1, 2)
The following is even better order of complexity wise:
val dest= List(1,2,4,5)
val points = List((1,3), (2,3) , (3,4))
val xs = points.map{_._1}.toSet
val newl = dest.filter(xs.contains(_))

Scala: map a Map to list of tuples

I tried to use Map.map to convert a map into a List of Tuples.
However this fails. I did the following experiments:
val m = Map(("a" -> 1), ("b" -> 2))
//> m : scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2)
val r1 = m.map{ case (k,v) => v} //> r1 : scala.collection.immutable.Iterable[Int] = List(1, 2)
def toTuple[A,B](a:A,b:B) = (a,b) //> toTuple: [A, B](a: A, b: B)(A, B)
//val r2: List[Tuple2[_,_]] = m.map(e => (e._1,e._2))
val r3 = m.map(e => toTuple(e._1,e._2)) //> r3 : scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2)
val r4 = m.toSeq //> r4 : Seq[(String, Int)] = ArrayBuffer((a,1), (b,2))
Notice how a List is generated for single elements (r1) but a Map is produced for tuples (r3). Not even forcing the type worked (r2). Only an explicit call to Seq did it (r4) So my question is, why/how does Map.map "automagically" create a new Map and not a list for example? In fact how is the return type determined (Seq, List, etc.)
A Map is a collection of tuples already.
scala> "b" -> 2
res0: (String, Int) = (b,2) // Implicitly converted to a Tuple
When you're mapping a Map, you're mapping the (key, value) pairs that it contains. This can't work, because you're stripping away the keys, and retaining only the values. So what you have is no longer a Map, but a step or two up the collection hierarchy, an Iterable:
val r1 = m.map{ case (k,v) => v}
Forcing the type cannot work, because a Map[A, B] is not a List[(A, B)]. This is the equivalent of m.map(identity). Notice how you're even accessing e with tuple accessors:
val r2: List[Tuple2[_,_]] = m.map(e => (e._1,e._2))
val r3 = m.map(e => toTuple(e._1,e._2))
Here, Seq is more generalized than List:
val r4 = m.toSeq
The simple solution as stated by #EndeNeu is to just use toList. When you map a collection, it should return the original collection type if it can. So mapping a Map should return another Map, unless the underlying structure has made it no longer a Map (like removing keys entirely) in r1.
Why even bother with a map? Wouldn't it be more appropriate for your use case to just have your data structure be a List[(String,Int)]? It's basically the same.. you can even write it in map notation:
val myMapAsList: List[(String, Int)] = List[(String,Int)](
"a" -> 1,
"b" -> 2
) // yields val myMapAsList: List[(String, Int)] = List((a,1), (b,2))

Lists and map orderings

In Scala, if we have List[(Char, Int)] ordered by the first element of each pair, does converting to a map via toMap preserve the ordering?
If so, does converting the resulting map back to list via toList preserve ordering?
No, Map (and Set) are not ordered. You can check this using Scala's interactive interpreter (REPL):
scala> val elems = List('a'->1, 'b'->2, 'c'->3, 'd'->4, 'e'->5)
elems: List[(Char, Int)] = List((a,1), (b,2), (c,3), (d,4), (e,5))
scala> elems.toMap.toList
res8: List[(Char, Int)] = List((e,5), (a,1), (b,2), (c,3), (d,4))
However, scala.collection does provide a SortedMap, which might be what you are looking for, though I haven't used this implementation yet.
Edit: Actually, there's an even more fundamental problem with this conversion: Because you cannot have duplicate keys in a map, you are not just losing guarantees on the ordering, but also, your list may have less elements after the conversion. Consider this:
scala> val elems = List('a'->1, 'a'->2)
elems: List[(Char, Int)] = List((a,1), (a,2))
scala> elems.toMap.toList
res9: List[(Char, Int)] = List((a,2))
As aforementioned, Map and Set have no ordering. Yet consider TreeMap which preserves ordering over its keys; for instance
val a = List(('z', 1), ('a', 4), ('b', 4))
a: List[(Char, Int)] = List((z,1), (a,4), (b,4))
val b = collection.immutable.TreeMap(a:_*)
b: scala.collection.immutable.TreeMap[Char,Int] = Map(a -> 4, b -> 4, z -> 1)
Update
Note that
for ( (k,v) <- b ) yield k
res: scala.collection.immutable.Iterable[Char] = List(a, b, z)