oblivion
oblivion

Reputation: 6548

How to find the last occurrence of an element in a Scala List?

I have a List of Students from which I want to find the last matching student whose age is 23.

I know that find() method gives us the first matching occurrence as shown below:

case class Student(id: Int, age: Int)

val students = List(Student(1, 23), Student(2, 24), Student(3, 23))

val firstStudentWithAge23 = students.find(student => student.age == 23)
// Some(Student(1, 23))

This code gives me the first matching student. But I need the last matching student.

For now, I am using reverse method followed by find:

val lastStudentWithAge23 = students.reverse.find(student => student.age == 23)
// Some(Student(3,23))

This gives me the last matching student.

But this doesn't not seem to be a good approach since the whole list has to be reversed first. How can I achieve this in a better yet functional way ?

Upvotes: 7

Views: 3943

Answers (5)

NthPortal
NthPortal

Reputation: 382

As of Scala 2.13, you can use findLast to find the last element of a Seq satisfying a predicate, if one exists:

val students = List(Student(1, 23), Student(2, 24), Student(3, 23))
students.findLast(_.age == 23) // Option[Student] = Some(Student(3, 23))

Upvotes: 12

elm
elm

Reputation: 20415

Yet another approach, using partition, for instance as follows,

val (l, _) = list.partition(_.age == 23)
l.lastOption

which delivers the list bisected into a tuple of those aged 23 in order of occurrence in the original list, and the rest. From the first tuple take the last item.

Upvotes: 2

Luka Jacobowitz
Luka Jacobowitz

Reputation: 23502

chengpohi's and jwvh's answers work, but will traverse the list twice or more.

Here's a generic way to find the last occurence that will only traverse the list once:

def findLast[A](la: List[A])(f: A => Boolean): Option[A] =
  la.foldLeft(Option.empty[A]) { (acc, cur) => 
    if (f(cur)) Some(cur)
    else acc
  }

We walk through the collection exactly once and always take the last element that matches our predicate f.

Upvotes: 6

jwvh
jwvh

Reputation: 51271

list.lift(list.lastIndexWhere(_.age == 23))

Indexing on a List isn't optimum, but it's an option.

Upvotes: 2

chengpohi
chengpohi

Reputation: 14217

I think maybe you can achieve this by filter with lastOption, like:

list.filter(student => student.age == 23).lastOption

Upvotes: 5

Related Questions