Understanding Scala code: (-_._2) - scala

Help me understand this Scala code:
sortBy(-_._2)
I understand that the first underscore (_) is a placeholder. I understand that _2 means the second member of a Tuple.
But what does a minus (-) stand for in this code?

Reverse order (i.e. descending), you sort by minus the second field of the tuple
The underscore is an anonymous parameter, so -_ is basically the same as x => -x
Some examples in plain scala:
scala> List(1,2,3).sortBy(-_)
res0: List[Int] = List(3, 2, 1)
scala> List("a"->1,"b"->2, "c"->3).sortBy(-_._2)
res1: List[(String, Int)] = List((c,3), (b,2), (a,1))
scala> List(1,2,3).sortBy(x => -x)
res2: List[Int] = List(3, 2, 1)

Sort by sorts by ascending order as default. To inverse the order a - (Minus) can be prepended, as already explained by #TrustNoOne .
So sortBy(-_._2) sorts by the second value of a Tuple2 but in reverse order.
A longer example:
scala> Map("a"->1,"b"->2, "c"->3).toList.sortBy(-_._2)
res1: List[(String, Int)] = List((c,3), (b,2), (a,1))
is the same as
scala> Map("a"->1,"b"->2, "c"->3).toList sortBy { case (key,value) => - value }
res1: List[(String, Int)] = List((c,3), (b,2), (a,1))

Related

Scala - how to sort tuples by both attributes in different order?

I want to sort a List[(String, Int)] so that the Ints are first sorted in descending order and then the Strings are sorted alphabetically. With my current implementation I achieved sorting Ints as expected. But the Strings are sorted in reverse order. I suppose this is due to the reverse ordering applied to the whole tuple.
How should I correct this to get the Strings sorted alphabetically?
val list: List[(String, Int)] = List(("x", 1), ("a", 1), ("c", 1), ("a", 2), ("b", 2), ("b", 1), ("a", 5), ("c", 5))
val sortedList = list.sortBy(x => (x._2, x._1))(implicitly[Ordering[(Int, String)]].reverse)
// Prints List((c,5), (a,5), (b,2), (a,2), (x,1), (c,1), (b,1), (a,1))
println(sortedList)
Expected: List((a,5), (c,5), (a,2), (b,2), (a,1), (b,1), (c,1), (x,1))
scala> val sortedList = list.sortBy(x => (-x._2.toLong, x._1))
sortedList: List[(String, Int)] = List((a,5), (c,5), (a,2), (b,2), (a,1), (b,1), (c,1), (x,1))
The trick with toLong here is to work properly for arbitrary Int values, including Int.MinValue for which:
scala> Int.MinValue == -Int.MinValue
res0: Boolean = true
scala> Int.MinValue.toLong == -Int.MinValue.toLong
res1: Boolean = false
For less allocations and better efficiency in runtime, please, consider using sorted with a custom ordering function:
scala> :paste
// Entering paste mode (ctrl-D to finish)
list.sorted((x: (String, Int), y: (String, Int)) => {
if (y._2 > x._2) 1
else if (y._2 < x._2) -1
else x._1.compareTo(y._1)
})
// Exiting paste mode, now interpreting.
res2: List[(String, Int)] = List((a,5), (c,5), (a,2), (b,2), (a,1), (b,1), (c,1), (x,1))

How the get the index of the duplicate pair in the scala list

I have a scala list like this below:
slist = List("a","b","c","a","d","c","a")
I want to get the index of the same element pair in this list.
For example,the result of this slist is
(0,3),(0,6),(3,6),(2,5)
which (0,3) means the slist(0)==slist(3)
(0,6) means the slist(0)==slist(6)
and so on.
So is there any method to do this in scala?
Thanks very much
There's simpler approaches but starting with zipWithIndex leads down this path. zipWithIndex returns a Tuple2 with the index and one of the letters. From there we groupBy the letter to get a map of the letter to it's indices and filter the ones with more than one value. Lastly, we have this MapLike.DefaultValuesIterable(List((a,0), (a,3), (a,6)), List((c,2), (c,5)))
which we take the indices from and make combinations.
scala> slist.zipWithIndex.groupBy(zipped => zipped._1).filter(t => t._2.size > 1).values.flatMap(xs => xs.map(t => t._2).combinations(2))
res40: Iterable[List[Int]] = List(List(0, 3), List(0, 6), List(3, 6), List(2, 5))
Indexing a List is rather inefficient so I recommend a transition to Vector and then back again (if needed).
val svec = slist.toVector
svec.indices
.map(x => (x,svec.indexOf(svec(x),x+1)))
.filter(_._2 > 0)
.toList
//res0: List[(Int, Int)] = List((0,3), (2,5), (3,6))
val v = slist.toVector; val s = v.size
for(i<-0 to s-1;j<-0 to s-1;if(i<j && v(i)==v(j))) yield (i,j)
In Scala REPL:
scala> for(i<-0 to s-1;j<-0 to s-1;if(i<j && v(i)==v(j))) yield (i,j)
res34: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector((0,3), (0,6), (2,5), (3,6))

How to calculate length of string in a tuple in scala

Given a list of tuples, where the 1st element of the tuple is an integer and the second element is a string,
scala> val tuple2 : List[(Int,String)] = List((1,"apple"),(2,"ball"),(3,"cat"),(4,"doll"),(5,"eggs"))
tuple2: List[(Int, String)] = List((1,apple), (2,ball), (3,cat), (4,doll), (5,eggs))
I want to print the numbers where the corresponding string length is 4.
Can this be done in one line ?
you need .collect which is filter+map
given your input,
scala> val input : List[(Int,String)] = List((1,"apple"),(2,"ball"),(3,"cat"),(4,"doll"),(5,"eggs"))
input: List[(Int, String)] = List((1,apple), (2,ball), (3,cat), (4,doll), (5,eggs))
filter those of length 4,
scala> input.collect { case(number, string) if string.length == 4 => number}
res2: List[Int] = List(2, 4, 5)
alternative solution using filter + map,
scala> input.filter { case(number, string) => string.length == 4 }
.map { case (number, string) => number}
res4: List[Int] = List(2, 4, 5)
you filter and print as below
tuple2.filter(_._2.length == 4).foreach(x => println(x._1))
You should have output as
2
4
5
I like #prayagupd answer using collect. But foldLeft is the one of my favourite function in Scala! you can use foldLeft:
scala> val input : List[(Int,String)] = List((1,"apple"),(2,"ball"),(3,"cat"),(4,"doll"),(5,"eggs"))
input: List[(Int, String)] = List((1,apple), (2,ball), (3,cat), (4,doll), (5,eggs))
scala> input.foldLeft(List.empty[Int]){case (acc, (n,str)) => if(str.length ==4) acc :+ n else acc}
res3: List[Int] = List(2, 4, 5)
Using a for comprehension as follows,
for ((i,s) <- tuple2 if s.size == 4) yield i
which for the example above delivers
List(2, 4, 5)
Note we pattern match and extract the elements in each tuple and filter by string size. To print a list consider for instance aList.foreach(println).
This will do:
tuple2.filter(_._2.size==4).map(_._1)
In Scala REPL:
scala> val tuple2 : List[(Int,String)] = List((1,"apple"),(2,"ball"),(3,"cat"),(4,"doll"),(5,"eggs"))
tuple2: List[(Int, String)] = List((1,apple), (2,ball), (3,cat), (4,doll), (5,eggs))
scala> tuple2.filter(_._2.size==4).map(_._1)
res261: List[Int] = List(2, 4, 5)
scala>

combine two lists with same keys

Here's a quite simple request to combine two lists as following:
scala> list1
res17: List[(Int, Double)] = List((1,0.1), (2,0.2), (3,0.3), (4,0.4))
scala> list2
res18: List[(Int, String)] = List((1,aaa), (2,bbb), (3,ccc), (4,ddd))
The desired output is as:
((aaa,0.1),(bbb,0.2),(ccc,0.3),(ddd,0.4))
I tried:
scala> (list1 ++ list2)
res23: List[(Int, Any)] = List((1,0.1), (2,0.2), (3,0.3), (4,0.4),
(1,aaa), (2,bbb), (3,ccc), (4,ddd))
But:
scala> (list1 ++ list2).groupByKey
<console>:10: error: value groupByKey is not a member of List[(Int,
Any)](list1 ++ list2).groupByKey
Any hints? Thanks!
The method you're looking for is groupBy:
(list1 ++ list2).groupBy(_._1)
If you know that for each key you have exactly two values, you can join them:
scala> val pairs = List((1, "a1"), (2, "b1"), (1, "a2"), (2, "b2"))
pairs: List[(Int, String)] = List((1,a1), (2,b1), (1,a2), (2,b2))
scala> pairs.groupBy(_._1).values.map {
| case List((_, v1), (_, v2)) => (v1, v2)
| }
res0: Iterable[(String, String)] = List((b1,b2), (a1,a2))
Another approach using zip is possible if the two lists contain the same keys in the same order:
scala> val l1 = List((1, "a1"), (2, "b1"))
l1: List[(Int, String)] = List((1,a1), (2,b1))
scala> val l2 = List((1, "a2"), (2, "b2"))
l2: List[(Int, String)] = List((1,a2), (2,b2))
scala> l1.zip(l2).map { case ((_, v1), (_, v2)) => (v1, v2) }
res1: List[(String, String)] = List((a1,a2), (b1,b2))
Here's a quick one-liner:
scala> list2.map(_._2) zip list1.map(_._2)
res0: List[(String, Double)] = List((aaa,0.1), (bbb,0.2), (ccc,0.3), (ddd,0.4))
If you are unsure why this works then read on! I'll expand it step by step:
list2.map(<function>)
The map method iterates over each value in list2 and applies your function to it. In this case each of the values in list2 is a Tuple2 (a tuple with two values). What you are wanting to do is access the second tuple value. To access the first tuple value use the ._1 method and to access the second tuple value use the ._2 method. Here is an example:
val myTuple = (1.0, "hello") // A Tuple2
println(myTuple._1) // prints "1.0"
println(myTuple._2) // prints "hello"
So what we want is a function literal that takes one parameter (the current value in the list) and returns the second tuple value (._2). We could have written the function literal like this:
list2.map(item => item._2)
We don't need to specify a type for item because the compiler is smart enough to infer it thanks to target typing. A really helpful shortcut is that we can just leave out item altogether and replace it with a single underscore _. So it gets simplified (or cryptified, depending on how you view it) to:
list2.map(_._2)
The other interesting part about this one liner is the zip method. All zip does is take two lists and combines them into one list just like the zipper on your favorite hoodie does!
val a = List("a", "b", "c")
val b = List(1, 2, 3)
a zip b // returns ((a,1), (b,2), (c,3))
Cheers!

What is the structure that is only enclosed by parentheses in scala?

Here's the problem:
I intend to retrieve a (Int, Int) object from a function, but I don't know how to get the second element. I've tried the following commands so as to retrieve the second value, or convert it to a Seq or List, but with no luck.
scala> val s = (1,2)
s: (Int, Int) = (1,2)
scala> s(1)
<console>:9: error: (Int, Int) does not take parameters
s(1)
^
scala> val ss = List(s)
ss: List[(Int, Int)] = List((1,2))
scala> ss(0)
res10: (Int, Int) = (1,2)
Could anyone give me some idea? Thanks a lot!
val s = (1, 2)
is syntatic sugar and creates a Tuple2, or in other words is equivalent to new Tuple2(1, 2). You can access elements in tuples with
s._1 // => 1
s._2 // => 2
Likewise, (1, 2, 3) would create a Tuple3, which also has a method _3 to access the third element.