tags:

views:

151

answers:

3

Let's say I have a class that looks something like this:

class Foo(Prop1:Int, Prop2:Int, Prop3:Int)
{
 ..
}

And I wanted to create a function that gets the max of some arbitrary property from a list of Foos.

Like this:

def getMax(Foos:List[Foo], Property:??) = Foos.map(_.Property).sort(_ > _).head

If I called getMax(myFooList, Prop1), it would return the value of the highest Prop1 from this list of Foos.

My question is, how can I make this work? I guess I could create some kind of enum (the scala equivalent) for the Property and do a match and then run the map on the appropriate property, but that seems like a lot of work - I'd have to extend my enum and the function each time Foo is refactored.

Also, not as important, but is there a better way to grab the max value of a list then what I did?

+4  A: 

Hi,

You can simply pass another function into getMax to instruct it how to map each Foo:

case class Foo(p1:Int, p2:Int)

def getMax(foos:List[Foo], mapper:Foo=>Int):Int = foos.map(mapper).foldLeft(Math.MIN_INT)((i,m)=>m.max(i))

val fooList = List(Foo(1,2),Foo(2,2),Foo(3,2),Foo(4,2))

getMax(fooList,_.p1)
//-->  4
Jon Hoffman
Note that for an empty list this will return Math.MIN_INT
Jon Hoffman
Ah thank you, gotta get with that functional style.
ryeguy
By the way: An even better and shorter way to get the min is to use reduceLeft.
ryeguy
The method `reduceLeft` won't work on empty lists, so it should be used with care.
Daniel
+1  A: 

The way I would do it is by passing to the getMax() method a function that knows how to extract the required property out of your Foo, i.e. something of type Foo => Int.

The way I would do it therefore is as follows:

scala> case class Foo(p1: Int, p2: Int, p3: Int)
defined class Foo

scala> def getMax(foos: List[Foo], prop: Foo => Int) = foos.map(prop).sort(_ > _).head
getMax: (List[Foo],(Foo) => Int)Int

scala> val lst = List(Foo(1,2,3), Foo(2,3,4), Foo(3,4,5))
lst: List[Foo] = List(Foo(1,2,3), Foo(2,3,4), Foo(3,4,5))

scala> getMax(lst, _.p1)
res0: Int = 3

scala> getMax(lst, _.p2)
res1: Int = 4

scala> getMax(lst, _.p3)
res2: Int = 5

-- Flaviu Cipcigan

Flaviu Cipcigan
+1  A: 

You can use objects inheriting from Product. It will be simpler and more type safe if you know arity in advance:

def getMax(foos: List[Product2[Int,Int]], f: Product2[Int,Int] => Int) = foos.map{f} ....

Then, you are free to feed getMax with anything like Tuple, e.g.

class Foo(val prop1: Int, val prop2: Int) extends Tuple2[Int, Int](prop1, prop2)
// this will duplicate values in an object actually.

getMax((new Foo(1,2)), _._2)

or inherit right from Product:

class Bar(val prop1: Int, val prop2: Int) extends Product2[Int, Int] {
  def _1 = prop1
  def _2 = prop2
}
val b = new Bar(2, 3)
getMax(List(b), _._2)

or simply use Scala's tuples:

getMax( (1,10) :: Nil, _._2)
getMax( List(1 -> 10), _._2)
// these are the same

Everything will become more complicated in case you do not know arity beforehand, because generic Product will let you retrieve elements as Any only (See Product.productElement(n: Int) method) -- thus you are loosing type safety.

Alexander Azarov