why does scala not match implicitly on tuples? - scala

you can do the following in ruby:
l = [[1, 2], [3, 4], [5, 6]]
m = l.map {|(a, b)| a+b}
but you can not do the following in scala:
val a = List((1, 2), (3, 4), (5, 6))
a.map((f, s) => f + s)
<console>:9: error: wrong number of parameters; expected = 1
a.map((f, s) => f + s)
instead you have to do this:
a.map { case (f, s) => f + s }
I find this rather wordy, since scala defines a "tuple" type, I was expecting it to also provide the syntactic sugar on top of it to match implicitly like above. Is there some deep reason why this kind of matching is not supported? Is there a more elegant way of doing this?

the reason
The reason is that the syntax you are trying to use already has a meaning. It is used when the higher order function expects a two-argument function. For example, with reduce or fold:
List(1,2,3).reduce((a,b) => a+b)
a solution
The cleaner way can be achieved by defining your own implicit method:
import scala.collection.generic.CanBuildFrom
import scala.collection.GenTraversableLike
implicit class EnrichedWithMapt2[A, B, Repr](val
self: GenTraversableLike[(A, B), Repr]) extends AnyVal {
def mapt[R, That](f: (A, B) => R)(implicit bf: CanBuildFrom[Repr, R, That]) = {
self.map(x => f(x._1, x._2))
}
}
Then you can do:
val a = List((1, 2), (3, 4), (5, 6))
a.mapt((f, s) => f + s) // List(3, 7, 11)
other alternatives
There are some other tricks you can do, like using tupled, but they doesn't really help you with the situation you described:
val g = (f: Int, s: Int) => f + s
a.map(g.tupled)
Or just
a.map(((f: Int, s: Int) => f + s).tupled)

More alternatives
If you have more time, you can even do the following with implicits:
val _1 = { t: { def _1: Int } => t._1 }
val _2 = { t: { def _2: Int } => t._2 }
implicit class HighLevelPlus[A](t: A => Int) {
def +(other: A => Int) = { a: A => t(a) + other(a) }
def *(other: A => Int) = { a: A => t(a) * other(a) }
}
val a = List((1, 2), (3, 4), (5, 6))
a map _1 + _2
Another possibility with monads and the keyword for and yield:
val a = List((1, 2), (3, 4), (5, 6))
for((i, j) <- a) yield i + j
but this one might not be the solution you prefer.

Is there some deep reason why this kind of matching is not supported?
Yes, because in Scala, the syntax
(f, s) =>
means an anonymous function that takes 2 arguments.
Is there a more elegant way of doing this?
(Somewhat tongue-in-cheek answer.) Use Haskell, where \(f, s) -> actually means a function that takes a tuple as an argument.

you can use _1, _2 properties of Tuple for desired effect
scala>val a = List((1, 2), (3, 4), (5, 6))
a: List[(Int, Int)] = List((1,2), (3,4), (5,6))
scala>a.map(x => x._1 + x._2)
res2: List[Int] = List(3, 7, 11)

Related

How to split an iterator based on a condition on prev and curr elements?

I want to split a list of elements into a list of lists such that neighboring elements in the inner list satisfy a given condition.
A simple condition would be neighboring elements are equal. Then if the input is List(1,1,1,2,2,3,3,3,3) output is List(List(1,1,1),List(2,2),List(3,3,3)).
Another condition could be current element should be greater than prev element. Then if the input is List(1,2,3,1,4,6,5,7,8), the output is List(List(1,2,3), List(1,4,6), List(5,7,8)). It would also be wonderful if the method can act on Iterator. The typedef of the method is
def method[A](lst:List[A], cond:(A,A)=>Boolean):List[List[A]]
def method[A](lst:Iterator[A], cond:(A,A)=>Boolean):Iterator[Iterator[A]]
You can use sliding together with span in a recursive function for the desired effect. This quick and dirty version is less efficient, but terser than some of the alternative:
def method[A](lst: TraversableOnce[A], cond: (A, A) => Boolean): List[List[A]] = {
val iterable = lst.toIterable
iterable.headOption.toList.flatMap { head =>
val (next, rest) = iterable.sliding(2).filter(_.size == 2).span(x => cond(x.head, x.last))
(head :: next.toList.map(_.last)) :: method(rest.map(_.last), cond)
}
}
If you want to lazily execute the code, you can return an Iterator[List[A]] instead of List[List[A]]:
def method[A](lst: TraversableOnce[A], cond: (A, A) => Boolean): Iterator[List[A]] = {
val iterable = lst.toIterable
iterable.headOption.toIterator.flatMap { head =>
val (next, rest) = iterable.sliding(2).filter(_.size == 2).span(x => cond(x.head, x.last))
Iterator(head :: next.toList.map(_.last)) ++ method(rest.map(_.last), cond)
}
}
And you can verify that this is lazy:
val x = (Iterator.range(0, 10) ++ Iterator.range(3, 5) ++ Iterator.range(1, 3)).map(x => { println(x); x })
val iter = method(x, (x: Int, y: Int) => x < y) //Only prints 0-9, and then 3!
iter.take(2).toList //Prints more
iter.toList //Prints the rest
You can make it even lazier by returning an Iterator[Iterator[A]]:
def method[A](lst: TraversableOnce[A], cond: (A, A) => Boolean): Iterator[Iterator[A]] = {
val iterable = lst.toIterable
iterable.headOption.toIterator.flatMap { head =>
val (next, rest) = iterable.sliding(2).filter(_.size == 2).span(x => cond(x.head, x.last))
Iterator(Iterator(head) ++ next.toIterator.map(_.last)) ++ method(rest.map(_.last), cond)
}
}
As a relatively unrelated side note, when you have generic parameters of this form, you're better off using 2 parameter lists:
def method[A](lst: TraversableOnce[A])(cond: (A, A) => Boolean)
When you have 2 parameter lists like this, the type inference can be a little bit smarter:
//No need to specify parameter types on the anonymous function now!
method(List(1, 3, 2, 3, 4, 1, 8, 1))((x, y) => x < y).toList
//You can now even use underscore anonymous function notation!
method(List(1, 4, 2, 3, 4, 1, 8))(_ < _)
Here is something close (I believe) to what you are asking for. The only issue with this is that it always produces a List of Lists for the result as opposed to being based on the input type:
val iter = Iterator(1,1,2,2,2,3,3,3)
val list = List(4,5,5,5,5,6,6)
def same(a:Int,b:Int) = a == b
def gt(a:Int, b:Int) = b > a
println(groupByPred(iter, same))
println(groupByPred(list, gt))
def groupByPred[L <: TraversableOnce[T], T](trav:L, cond:(T,T) => Boolean):List[List[T]] = {
val (ret, inner) =
trav.foldLeft((List.empty[List[T]], List.empty[T])){
case ((acc, list), el) if list.isEmpty || cond(list.head, el) => (acc,el :: list)
case ((acc, list), el) => (list.reverse :: acc,el :: List.empty)
}
(inner.reverse :: ret).reverse
}
If you run that code, the output should be the following:
List(List(1, 1), List(2, 2, 2), List(3, 3, 3))
List(List(4, 5), List(5), List(5), List(5, 6), List(6))
Try this.
Puts the head of the list as the first element of the first element of the List of Lists. Then adds things to that first List if the condition holds. If it doesn't, starts a new List with the current entry as the first element.
Both the inner list and the outer are constructed in the wrong order. So reverse each element of the outer List (with map) and then reverse the outer list.
val xs = List(1, 1, 1, 2, 2, 3, 3, 3, 3)
val ys = List(1, 2, 3, 1, 4, 6, 5, 7, 8)
def method[A](lst: List[A], cond: (A, A) => Boolean): List[List[A]] = {
lst.tail.foldLeft(List(List(lst.head))) { (acc, e) =>
if (cond(acc.head.head, e))
(e :: acc.head) :: acc.tail
else List(e) :: acc
}.map(_.reverse).reverse
}
method(xs, { (a: Int, b: Int) => a == b })
//> res0: List[List[Int]] = List(List(1, 1, 1), List(2, 2), List(3, 3, 3, 3))
method(ys, { (a: Int, b: Int) => a < b })
//> res1: List[List[Int]] = List(List(1, 2, 3), List(1, 4, 6), List(5, 7, 8))
Iterator overload
def method[A](iter:Iterator[A], cond: (A, A) => Boolean): List[List[A]] = {
val h = iter.next
iter.foldLeft(List(List(h))) { (acc, e) =>
if (cond(acc.head.head, e))
(e :: acc.head) :: acc.tail
else List(e) :: acc
}.map(_.reverse).reverse
}
method(xs.toIterator, { (a: Int, b: Int) => a == b })
//> res0: List[List[Int]] = List(List(1, 1, 1), List(2, 2), List(3, 3, 3, 3))
method(ys.toIterator, { (a: Int, b: Int) => a < b })
//> res1: List[List[Int]] = List(List(1, 2, 3), List(1, 4, 6), List(5, 7, 8))
More generic version (hat-tip to #cmbaxter for some ideas here) that works with Lists, Iterators and anything that can be traversed once:
def method[A, T <: TraversableOnce[A]](trav: T, cond: (A, A) => Boolean)
: List[List[A]] = {
trav.foldLeft(List(List.empty[A])) { (acc, e) =>
if (acc.head.isEmpty || !cond(acc.head.head, e)) List(e) :: acc
else (e :: acc.head) :: acc.tail
}.map(_.reverse).reverse
}

Update multiple values in a sequence

To get a sequence with one value updated, one can use
seq.updated(index, value)
I want to set a new value for a range of elements. Is there a library function for that? I currently use the following function:
def updatedSlice[A](seq: List[A], ind: Iterable[Int], value: A): List[A] =
if (ind.isEmpty) seq
else updatedSlice(seq.updated(ind.head, value), ind.tail, value)
Besides the need of writing function, this seems to be inefficient, and also works only for lists, rather than arbitrary subclasses of Seq and Strings. So,
is there a method that performs it?
how can I parametrize the function to take (and return) some subclass of Seq[A]?
To my knowledge there's no combinator that directly provides this functionality.
For the Seq part, well, it works only for List because you're taking a List as a parameter. Take a Seq, return a Seq and you already have one less problem.
Moreover, your implementation throws an IndexOutOfBounds exception if ind contains an index greater or equal to the seq length.
Here's an alternative implementation (which uses Set for a O(1) contains)
def updatedAtIndexes[A](seq: Seq[A], ind: Set[Int], value: A): Seq[A] = seq.zipWithIndex.map {
case (el, i) if ind.contains(i) => value
case (el, _) => el
}
Example
updatedAtIndexes(List(1, 2, 3, 4, 5), Set(0, 2), 42) // List(42, 2, 42, 4)
You can even make it prettier with a simple implicit class:
implicit class MyPimpedSeq[A](seq: Seq[A]) {
def updatedAtIndexes(ind: Set[Int], value: A): Seq[A] = seq.zipWithIndex.map {
case (el, i) if ind.contains(i) => value
case (el, _) => el
}
}
Examples
List(1, 2, 3, 4).updatedAtIndexes(Set(0, 2), 42) // List(42, 2, 42, 4)
Vector(1, 2, 3).updatedAtIndexes(Set(1, 2, 3), 42) // Vector(1, 42, 42)
No one at a computer has said:
scala> (1 to 10).toSeq patch (3, (1 to 5), 3)
res0: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 1, 2, 3, 4, 5, 7, 8, 9, 10)
Save your green checks for #Marth.
Note they're still working on it.
https://issues.scala-lang.org/browse/SI-8474
Which says something about less-frequently-used API.
Update: I glanced at the question a second time and saw that I misread it, oh well:
scala> implicit class x[A](as: Seq[A]) {
| def updatedAt(is: collection.Traversable[Int], a: A) = {
| (as /: is) { case (xx, i) => xx updated (i, a) } } }
defined class x
scala> (1 to 10) updatedAt (Seq(3,6,9), 0)
res9: Seq[Int] = Vector(1, 2, 3, 0, 5, 6, 0, 8, 9, 0)
Just a relaxing round of golf.
Update: s/relaxing/annoying
Looks like it needs more type params, but I don't have a time slice for it.
scala> implicit class slicer[A, B[_] <: Seq[_]](as: B[A]) {
| def updatedAt[That<:B[_]](is: Traversable[Int], a: A)(implicit cbf: CanBuildFrom[B[A], A, That]) =
| (as /: is) { case (x,i) => x updated[A,That] (i,a) }}
<console>:15: error: type arguments [A,That] conform to the bounds of none of the overloaded alternatives of
value updated: [B >: _$1, That](index: Int, elem: B)(implicit bf: scala.collection.generic.CanBuildFrom[Seq[_$1],B,That])That <and> [B >: A, That](index: Int, elem: B)(implicit bf: scala.collection.generic.CanBuildFrom[Repr,B,That])That
(as /: is) { case (x,i) => x updated[A,That] (i,a) }}
^
Who even knew updated was overloaded?
My new favorite Odersky quote:
I played with it until it got too tedious.

Groupby like Python's itertools.groupby

In Python I'm able to group consecutive elements with the same key by using itertools.groupby:
>>> items = [(1, 2), (1, 5), (1, 3), (2, 9), (3, 7), (1, 5), (1, 4)]
>>> import itertools
>>> list(key for key,it in itertools.groupby(items, lambda tup: tup[0]))
[1, 2, 3, 1]
Scala has groupBy as well, but it produces different result - a map pointing from key to all the values found in the iterable with the specified key (not the consecutive runs with the same key):
scala> val items = List((1, 2), (1, 5), (1, 3), (2, 9), (3, 7), (1, 5), (1, 4))
items: List[(Int, Int)] = List((1,2), (1,5), (1,3), (2,9), (3,7), (1,5), (1,4))
scala> items.groupBy {case (key, value) => key}
res0: scala.collection.immutable.Map[Int,List[(Int, Int)]] = Map(2 -> List((2,9)), 1 -> List((1,2), (1,5), (1,3), (1,5), (1,4)), 3 -> List((3,7)))
What is the most eloquent way of achieving the same as with Python itertools.groupby?
If you just want to throw out sequential duplicates, you can do something like this:
def unchain[A](items: Seq[A]) = if (items.isEmpty) items else {
items.head +: (items zip items.drop(1)).collect{ case (l,r) if r != l => r }
}
That is, just compare the list to a version of itself shifted by one place, and only keep the items which are different. It's easy to add a (same: (a1: A, a2: A) => Boolean) parameter to the method and use !same(l,r) if you want custom behavior for what counts as the same (e.g. do it just by key).
If you want to keep the duplicates, you can use Scala's groupBy to get a very compact (but inefficient) solution:
def groupSequential(items: Seq[A])(same: (a1: A, a2: A) => Boolean) = {
val ns = (items zip items.drop(1)).
scanLeft(0){ (n,cc) => if (same(cc._1, cc._2)) n+1 else n }
(ns zip items).groupBy(_._1).toSeq.sortBy(_._1).map(_._2)
}
Using List.span, like this
def keyMultiSpan(l: List[(Int,Int)]): List[List[(Int,Int)]] = l match {
case Nil => List()
case h :: t =>
val ms = l.span(_._1 == h._1)
ms._1 :: keyMultiSpan(ms._2)
}
Hence let
val items = List((1, 2), (1, 5), (1, 3), (2, 9), (3, 7), (1, 5), (1, 4))
and so
keyMultiSpan(items).map { _.head._1 }
res: List(1, 2, 3, 1)
Update
A more readable syntax, as suggested by #Paul, an implicit class for possibly neater usage, and type parameterisation for generality,
implicit class RichSpan[A,B](val l: List[(A,B)]) extends AnyVal {
def keyMultiSpan(): List[List[(A,B)]] = l match {
case Nil => List()
case h :: t =>
val (f, r) = l.span(_._1 == h._1)
f :: r.keyMultiSpan()
}
}
Thus, use it as follows,
items.keyMultiSpan.map { _.head._1 }
res: List(1, 2, 3, 1)
Here is a succinct but inefficient solution:
def pythonGroupBy[T, U](items: Seq[T])(f: T => U): List[List[T]] = {
items.foldLeft(List[List[T]]()) {
case (Nil, x) => List(List(x))
case (g :: gs, x) if f(g.head) == f(x) => (x :: g) :: gs
case (gs, x) => List(x) :: gs
}.map(_.reverse).reverse
}
And here is a better one, that only invokes f on each element once:
def pythonGroupBy2[T, U](items: Seq[T])(f: T => U): List[List[T]] = {
if (items.isEmpty)
List(List())
else {
val state = (List(List(items.head)), f(items.head))
items.tail.foldLeft(state) { (state, x) =>
val groupByX = f(x)
state match {
case (g :: gs, groupBy) if groupBy == groupByX => ((x :: g) :: gs, groupBy)
case (gs, _) => (List(x) :: gs, groupByX)
}
}._1.map(_.reverse).reverse
}
}
Both solutions fold over items, building up a list of groups as they go. pythonGroupBy2 also keeps track of the value of f for the current group. At the end, we have to reverse each group and the list of groups in order to get the correct order.
Try:
val items = List((1, 2), (1, 5), (1, 3), (2, 9), (3, 7), (1, 5), (1, 4))
val res = compress(items.map(_._1))
/** Eliminate consecutive duplicates of list elements **/
def compress[T](l : List[T]) : List[T] = l match {
case head :: next :: tail if (head == next) => compress(next :: tail)
case head :: tail => head :: compress(tail)
case Nil => List()
}
/** Tail recursive version **/
def compress[T](input: List[T]): List[T] = {
def comp(remaining: List[T], l: List[T], last: Any): List[T] = {
remaining match {
case Nil => l
case head :: tail if head == last => comp(tail, l, head)
case head :: tail => comp(tail, head :: l, head)
}
}
comp(input, Nil, Nil).reverse
}
Where compress is the solution of one of the 99 Problems in Scala.
hmm couldn't find something out of the box but this will do it
def groupz[T](list:List[T]):List[T] = {
list match {
case Nil => Nil
case x::Nil => List(x)
case x::xs if (x == xs.head) => groupz(xs)
case x::xs => x::groupz(xs)
}}
//now let's add this functionality to List class
implicit def addPythonicGroupToList[T](list:List[T]) = new {def pythonGroup = groupz(list)}
and now you can do:
val items = List((1, 2), (1, 5), (1, 3), (2, 9), (3, 7), (1, 5), (1, 4))
items.map(_._1).pythonGroup
res1: List[Int] = List(1, 2, 3, 1)
Here is a simple solution that I used for a problem I stumbled on at work. In this case I didn't care too much about space, so did not worry about efficient iterators. Used an ArrayBuffer to accumulate the results.
(Don't use this with enormous amounts of data.)
Sequential GroupBy
import scala.collection.mutable.ArrayBuffer
object Main {
/** Returns consecutive keys and groups from the iterable. */
def sequentialGroupBy[A, K](items: Seq[A], f: A => K): ArrayBuffer[(K, ArrayBuffer[A])] = {
val result = ArrayBuffer[(K, ArrayBuffer[A])]()
if (items.nonEmpty) {
// Iterate, keeping track of when the key changes value.
var bufKey: K = f(items.head)
var buf: ArrayBuffer[A] = ArrayBuffer()
for (elem <- items) {
val key = f(elem)
if (key == bufKey) {
buf += elem
} else {
val group: (K, ArrayBuffer[A]) = (bufKey, buf)
result += group
bufKey = key
buf = ArrayBuffer(elem)
}
}
// Append last group.
val group: (K, ArrayBuffer[A]) = (bufKey, buf)
result += group
}
result
}
def main(args: Array[String]): Unit = {
println("\nExample 1:")
sequentialGroupBy[Int, Int](
Seq(1, 4, 5, 7, 9, 8, 16),
x => x % 2
).foreach(println)
println("\nExample 2:")
sequentialGroupBy[String, Boolean](
Seq("pi", "nu", "rho", "alpha", "xi"),
x => x.length > 2
).foreach(println)
}
}
Running the above code results in the following:
Example 1:
(1,ArrayBuffer(1))
(0,ArrayBuffer(4))
(1,ArrayBuffer(5, 7, 9))
(0,ArrayBuffer(8, 16))
Example 2:
(false,ArrayBuffer(pi, nu))
(true,ArrayBuffer(rho, alpha))
(false,ArrayBuffer(xi))

How do I create a function that takes any Seq[T,Int] and returns the original type of the Seq.

I'm having a hard time defining this question. I want to do something like this:
val list = List(("a",1), ("b",2))
private def shiftIndex[C <: Seq[_]](seq: C, amount: Int): C = {
seq.map({ case (value, integer) => (value, integer + amount) })
}
shiftIndex(list,3)
Should return
List(("a",4),("b",5)): List[String,Integer]
But it should work for the most general case:
seq implements map (I know its not :Seq)
seq is storing a tuple of anything and an Int.
Any links to resources the explains these concepts would be appreciated.
import scala.collection.generic.CanBuildFrom
import scala.collection.GenTraversable
import scala.collection.GenTraversableLike
def shiftIndex[T, Repr](seq: GenTraversableLike[(T, Int), Repr], amount: Int)(
implicit bf: CanBuildFrom[Repr, (T, Int), Repr]) = {
seq.map { case (value, integer) => (value, integer + amount) }
}
shiftIndex(List(("a", 1), ("b", 2)), 3) // List((a,4), (b,5))
shiftIndex(Set(("a", 1), ("b", 2)), 3) // Set((a,4), (b,5))
shiftIndex(Map(("a", 1), ("b", 2)), 3) // Map(a -> 4, b -> 5)
Though I might recommend a solution that's nicer looking and more generic. Here I enrich GenTraversable so that all traversable objects of pairs have a method mapVals that takes a function to be applied to the second part of the pair, and has a return type the same as the object it was run on:
class EnrichedWithMapVals[T, U, Repr](seq: GenTraversableLike[(T, U), Repr]) {
def mapVals[R, That](f: U => R)(implicit bf: CanBuildFrom[Repr, (T, R), That]) = {
seq.map { case (x, y) => (x, f(y)) }
}
}
implicit def enrichWithMapVals[T, U, Repr](self: GenTraversableLike[(T, U), Repr]) = new EnrichedWithMapVals(self)
List(("a", 1), ("b", 2)).mapVals(_ + 3) // List((a,4), (b,5))
Set(("a", 1), ("b", 2)).mapVals(_ + 3) // Set((a,4), (b,5))
Map(("a", 1), ("b", 2)).mapVals(_ + 3) // Map(a -> 4, b -> 5)

In Scala, how to use Ordering[T] with List.min or List.max and keep code readable

In Scala 2.8, I had a need to call List.min and provide my own compare function to get the value based on the second element of a Tuple2. I had to write this kind of code:
val list = ("a", 5) :: ("b", 3) :: ("c", 2) :: Nil
list.min( new Ordering[Tuple2[String,Int]] {
def compare(x:Tuple2[String,Int],y:Tuple2[String,Int]): Int = x._2 compare y._2
} )
Is there a way to make this more readable or to create an Ordering out of an anonymous function like you can do with list.sortBy(_._2)?
In Scala 2.9, you can do list minBy { _._2 }.
C'mon guys, you made the poor questioner find "on" himself. Pretty shabby performance. You could shave a little further writing it like this:
list min Ordering[Int].on[(_,Int)](_._2)
Which is still far too noisy but that's where we are at the moment.
One thing you can do is use the more concise standard tuple type syntax instead of using Tuple2:
val min = list.min(new Ordering[(String, Int)] {
def compare(x: (String, Int), y: (String, Int)): Int = x._2 compare y._2
})
Or use reduceLeft to have a more concise solution altogether:
val min = list.reduceLeft((a, b) => (if (a._2 < b._2) a else b))
Or you could sort the list by your criterion and get the first element (or last for the max):
val min = list.sort( (a, b) => a._2 < b._2 ).first
Which can be further shortened using the placeholder syntax:
val min = list.sort( _._2 < _._2 ).first
Which, as you wrote yourself, can be shortened to:
val min = list.sortBy( _._2 ).first
But as you suggested sortBy yourself, I'm not sure if you are looking for something different here.
The function Ordering#on witnesses the fact that Ordering is a contra-variant functor. Others include Comparator, Function1, Comparable and scalaz.Equal.
Scalaz provides a unified view on these types, so for any of them you can adapt the input with value contramap f, or with symbolic denotation, value ∙ f
scala> import scalaz._
import scalaz._
scala> import Scalaz._
import Scalaz._
scala> val ordering = implicitly[scala.Ordering[Int]] ∙ {x: (_, Int) => x._2}
ordering: scala.math.Ordering[Tuple2[_, Int]] = scala.math.Ordering$$anon$2#34df289d
scala> List(("1", 1), ("2", 2)) min ordering
res2: (java.lang.String, Int) = (1,1)
Here's the conversion from the Ordering[Int] to Ordering[(_, Int)] in more detail:
scala> scalaz.Scalaz.maContravariantImplicit[Ordering, Int](Ordering.Int).contramap { x: (_, Int) => x._2 }
res8: scala.math.Ordering[Tuple2[_, Int]] = scala.math.Ordering$$anon$2#4fa666bf
list.min(Ordering.fromLessThan[(String, Int)](_._2 < _._2))
Which is still too verbose, of course. I'd probably declare it as a val or object.
You could always define your own implicit conversion:
implicit def funToOrdering[T,R <% Ordered[R]](f: T => R) = new Ordering[T] {
def compare(x: T, y: T) = f(x) compare f(y)
}
val list = ("a", 5) :: ("b", 3) :: ("c", 2) :: Nil
list.min { t: (String,Int) => t._2 } // (c, 2)
EDIT: Per #Dario's comments.
Might be more readable if the conversion wasn't implicit, but using an "on" function:
def on[T,R <% Ordered[R]](f: T => R) = new Ordering[T] {
def compare(x: T, y: T) = f(x) compare f(y)
}
val list = ("a", 5) :: ("b", 3) :: ("c", 2) :: Nil
list.min( on { t: (String,Int) => t._2 } ) // (c, 2)