假设我有Students
的列表。 Students
包含name
,birth date
,grade
等字段。您如何在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)
,但在列表上运行两次。你能建议一个更好的解决方案吗?
答案 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中对其进行描述。当你通过集合(例如列表)时,它们是积累某些东西的一般方式。如果您不熟悉匹配,则可以使用isEmpty
和head
执行相同的操作。如果您希望学生的顺序与原始列表中的顺序相同,请在最后运行.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
提升为跟踪最佳成绩的对象和包含学生的列表单元格。它还会考虑合并的逻辑:如果新学生有
然后可以使用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)