如何在列表中找到成绩最好的学生?

时间:2011-11-11 11:42:51

标签: scala list-comprehension

假设我有Students的列表。 Students包含namebirth dategrade等字段。您如何在Scala中找到Students最好的grade

例如:

List(Student("Mike", "A"), Student("Pete", "B"), Student("Paul", A))"

我想要

List(Student("Mike", "A"), Student("Paul", A))

显然,我可以找到max grade(上面列表中的“A”),然后filter列表

students.filter(_.grade == max_grade)

此解决方案为O(N),但在列表上运行两次。你能建议一个更好的解决方案吗?

5 个答案:

答案 0 :(得分:6)

两次运行列表可能是最好的方法,但是如果你坚持只运行一次的解决方案,你可以使用折叠(这里适用于空列表):

(List[Student]() /: list){ (best,next) => best match {
  case Nil => next :: Nil
  case x :: rest =>
    if (betterGrade(x,next)) best
    else if (betterGrade(next,x)) next :: Nil
    else next :: best
}}

如果您不熟悉折叠,则会在答案here中对其进行描述。当你通过集合(例如列表)时,它们是积累某些东西的一般方式。如果您不熟悉匹配,则可以使用isEmptyhead执行相同的操作。如果您希望学生的顺序与原始列表中的顺序相同,请在最后运行.reverse

答案 1 :(得分:3)

使用foldLeft只能遍历学生列表一次:

scala> students.foldLeft(List.empty[Student]) {
     | case (Nil, student) => student::Nil
     | case (list, student) if (list.head.grade == student.grade) => student::list
     | case (list, student) if (list.head.grade > student.grade) => student::Nil
     | case (list, _) => list
     | }
res17: List[Student] = List(Student(Paul,A), Student(Mike,A))

答案 2 :(得分:3)

已有6个答案,但我仍然觉得有必要加上我的答案:

case class Lifted(grade: String, students: List[Student]) {
  def mergeBest(other: Lifted) = grade compare other.grade match {
    case  0 => copy(students = other.students ::: students)
    case  1 => other
    case -1 => this
  }
}

这个小案例类会将Student提升为跟踪最佳成绩的对象和包含学生的列表单元格。它还会考虑合并的逻辑:如果新学生有

  • 相同等级=>将新学生合并到结果列表中,将前面较短的学生合并到效率中 - 否则结果将不是O(n)
  • 更高等级=>用新学生替换当前最好的
  • 低年级=>保持目前最好的

然后可以使用reduceLeft

轻松构建结果
val result = { 
  list.view.map(s => Lifted(s.grade, List(s)))
    .reduceLeft((l1, l2) => l1.mergeBest(l2))
    .students
}
// result: List[Student] = List(Student(Paul,A), Student(Mike,A))

PS。我正在推荐你的问题 - 基于生成的回复数量

答案 3 :(得分:2)

您可以在学生列表中使用filter

case class Student(grade: Int)
val students = ...
val minGrade = 5
students filter ( _.grade < minGrade)

对于String

类型也可以正常工作

答案 4 :(得分:2)

排序后,您可以使用takeWhile来避免第二次迭代。

case class Student(grade: Int)
val list = List(Student(1), Student(3), Student(2), Student(1), Student(25), Student(0), Student (25))

val sorted = list.sortWith (_.grade > _.grade)
sorted.takeWhile (_.grade == sorted(0).grade)

在服用生奶油之前,它仍然对整个事情进行排序,即使是我们不感兴趣的1级,3级,0级和-1级,但它是短代码。

更新

可以并行执行的第二种方法是拆分列表,然后取每边的最大值,然后只取较高的值,如果有的话 - 其他都是:

def takeMax (l: List[Student]) : List [Student] = l.size match {
    case 0 => Nil
    case 1 => l 
    case 2 => if (l(0).grade > l(1).grade) List (l(0)) else 
      if (l(0).grade < l(1).grade) List (l(1)) else List (l(0), l(1))
    case _ => {
      val left = takeMax (l.take (l.size / 2))
      val right= takeMax (l.drop (l.size / 2))
     if (left (0).grade > right(0).grade) left else 
     if (left (0).grade < right(0).grade) right else left ::: right }
}

当然,我们喜欢分析学生,以及比较其中两个的方法。

def takeMax [A] (l: List[A], cmp: ((A, A) => Int)) : List [A] = l.size match {
    case 0 | 1 => l 
    case 2     => cmp (l(0), l(1)) match {
      case 0 => l 
      case x => if (x > 0) List (l(0)) else List (l(1))
    }
    case _ => {
      val left = takeMax (l.take (l.size / 2), cmp)
      val right= takeMax (l.drop (l.size / 2), cmp)
      cmp (left (0), right (0)) match {
        case 0 => left ::: right
        case x => if (x > 0) left else right }
     }
}

def cmp (s1: Student, s2: Student) = s1.grade - s2.grade 
takeMax (list, cmp)