How can I get the max of an arbitrary property from a list in Scala?

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 maximum of some arbitrary property from Foo s list.

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 Foo s.

My question is: how can I do this? I think I could create some kind of enum (equivalent to scala) for Property and make match , and then run map in the corresponding property, but this seems like a lot of work - d need to extend my enum and function every time Foo reorganizes.

Also, not so important, but is there a better way to capture the maximum value of the list than what I did?

+4
source share
5 answers

You can simply pass another function to getMax to tell it how to match 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 
+6
source

You can do this simply by using existing functions that write your own getMax, probably not needed:

 scala> val fooList = List(Foo(1,2),Foo(2,2),Foo(3,2),Foo(4,2)) fooList: List[Foo] = List(Foo(1,2), Foo(2,2), Foo(3,2), Foo(4,2)) scala> fooList.map(_.p2).max res12: Int = 2 scala> fooList.map(_.p1).max res13: Int = 4 

If you want to specify the "getter" property elsewhere, you can do it like this:

 scala> def p1 = (f: Foo) => f.p1 p1: Foo => Int scala> def p2 = (f: Foo) => f.p2 p2: Foo => Int scala> fooList.map(p1).max res14: Int = 4 scala> fooList.map(p2).max res15: Int = 2 
+11
source

You should use the standard maxBy method:

 List(("a", 2), ("b", 3), ("c", 4)).maxBy(_._2) => (String, Int) = (c,4) 
+5
source

I would do this by passing the getMax() method a function that knows how to extract the property you are looking for from your Foo , that is, something like Foo => Int .

So I would do it like this:

 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

+1
source

You can use objects that inherit from Product . It will be easier and safer if you know arity in advance:

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

Then you can give getMax something like Tuple , for example.

 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 the 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 just use Scala tuples:

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

Everything will become more complicated if you do not know arity in advance, because the general Product will allow you to retrieve elements only as Any (see the Product.productElement(n: Int) method) - this way you lose type safety.

+1
source

All Articles