Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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 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?

like image 492
ryeguy Avatar asked Sep 18 '09 18:09

ryeguy


People also ask

How to find largest element in list Scala?

The max() method is utilized to find the largest element of all the elements in the stated list. Return Type: It returns the largest of all the elements in the stated list.

How do you find the maximum value in Scala?

Scala Int max() method with example The max() method is utilized to return the maximum value of the two specified int numbers. Return Type: It returns true the maximum value of the two specified int numbers.

How do I find the max of a list in R?

How to get the max value in a list in R? First, use the unlist() function to convert the list into a vector, and then use the max() function to get the maximum value. The following are the arguments that you can give to the max() function in R. x – The vector for which you want to compute the max value.


5 Answers

You should use standard maxBy method:

List(("a", 2), ("b", 3), ("c", 4)).maxBy(_._2)
=> (String, Int) = (c,4)
like image 106
Vasiliy Kevroletin Avatar answered Oct 05 '22 22:10

Vasiliy Kevroletin


You can do this so simply using existing functionality that writing your own getMax is probably unnecessary:

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 wanted to specify the property 'getter' elsewhere you could 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
like image 40
Alan Burlison Avatar answered Oct 06 '22 00:10

Alan Burlison


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
like image 41
Jon Hoffman Avatar answered Oct 06 '22 00:10

Jon Hoffman


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

like image 24
Flaviu Cipcigan Avatar answered Oct 05 '22 23:10

Flaviu Cipcigan


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.

like image 21
Alexander Azarov Avatar answered Oct 05 '22 22:10

Alexander Azarov