Как я могу получить максимум произвольного свойства из списка в Scala? - PullRequest
5 голосов
/ 18 сентября 2009

Допустим, у меня есть класс, который выглядит примерно так:

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

И я хотел создать функцию, которая получает максимум некоторого произвольного свойства из списка Foo с.

Как это:

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

Если бы я позвонил getMax(myFooList, Prop1), он вернул бы значение самого высокого Prop1 из этого списка Foo с.

У меня вопрос, как я могу заставить эту работу? Я думаю, я мог бы создать какой-нибудь enum (эквивалент scala) для Property и сделать match, а затем запустить map для соответствующего свойства, но это кажется большой работой - я бы расширять мое перечисление и функцию каждый раз, когда Foo подвергается рефакторингу.

Кроме того, не так важно, но есть ли лучший способ получить максимальное значение списка, чем то, что я сделал?

Ответы [ 5 ]

12 голосов
/ 17 февраля 2012

Вы можете сделать это настолько просто, используя существующую функциональность, что, вероятно, в написании собственного getMax нет необходимости:

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

Если вы хотите указать свойство getter в другом месте, вы можете сделать это следующим образом:

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
9 голосов
/ 19 февраля 2014

Вы должны использовать стандартный maxBy метод:

List(("a", 2), ("b", 3), ("c", 4)).maxBy(_._2)
=> (String, Int) = (c,4)
6 голосов
/ 18 сентября 2009

Вы можете просто передать другую функцию в getMax, чтобы указать ей, как отображать каждый 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
1 голос
/ 18 сентября 2009

Вы можете использовать объекты, наследуемые от Product. Это будет проще и безопаснее, если вы знаете arity заранее:

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

Тогда вы можете кормить getMax чем-нибудь вроде Tuple, например,

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)    

или наследовать права от 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)

или просто используйте кортежи Scala:

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

Все станет сложнее, если вы заранее не знаете arity, потому что универсальный Product позволит вам получать элементы только как Any (см. Product.productElement(n: Int) метод) - таким образом вы теряете безопасность типов. *

1 голос
/ 18 сентября 2009

Я бы сделал это, передав методу getMax() функцию, которая знает, как извлечь необходимое свойство из вашего Foo, то есть что-то типа Foo => Int.

Таким образом, я бы сделал это следующим образом:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...