Array#rfind in Ruby 4: Finding Elements from the End, Efficiently

Krishna Singh
4 min read

Ruby 4 added Array#rfind, and honestly, it's about time.
I've been using array.reverse_each.find { ... } for years, and it always felt like a workaround. Now there's a proper method for it.

The Problem

You need the last element in an array that matches some condition. Before Ruby 4, you had a few options:

# What most of us do
[2, 2, 3, 4, 6, 7, 8].reverse_each.find(&:odd?)  # => 7

# The rindex approach
array = [2, 2, 3, 4, 6, 7, 8]
index = array.rindex { |n| n.odd? }
array[index] if index  # => 7
Both work, but they're not great. reverse_each creates an intermediate array, which is wasteful. The rindex approach works but requires nil checking and extra lines. Neither reads particularly well.

Enter rfind

Now you can just do:

[2, 2, 3, 4, 6, 7, 8].rfind(&:odd?)  # => 7
It's find, but from the end. Returns nil if nothing matches, just like find does. Simple.


Real Use Cases

I've hit this pattern a lot in production code:

# Last successful log entry
log_entries.rfind { |entry| entry[:status] == "success" }

# Most recent event of a type
events.rfind { |e| e[:type] == "login" }

# Last non-empty value
values.rfind { |v| !v.to_s.empty? }

# Current (non-deprecated) version
versions.rfind { |v| !v[:deprecated] }

Any time you need "the last thing that matches X," rfind is what you want.


Performance

I ran a quick benchmark on a million-element array, and rfind was about 3x faster than reverse_each.find. For smaller arrays, the difference is negligible, but it's good to know the right tool for the job.

Benchmarking rfind vs reverse_each.find
               1. Small (100) elements (10000 iterations):

reverse_each.find: 0.0066 seconds
rfind: 0.0019 seconds
Speedup: 3.57x faster

                2. Medium (10k) elements (1000 iterations):

reverse_each.find: 0.0024 seconds
rfind: 187.00002692639828 μs
Speedup: 12.91x faster

               3. Large (1M) elements (100 iterations):

reverse_each.find: 46.00000102072954 μs
rfind: 18.000020645558834 μs
Speedup: 2.56x faster

             Summary:

Small (100) : rfind is 3.57x faster
Medium (10k)   : rfind is 12.91x faster
Large (1M) : rfind is 2.56x faster

How It Compares

array = [1, 2, 3, 4, 5, 6, 7, 8]

array.find(&:even?)   # => 2 (first match)
array.rfind(&:even?)  # => 8 (last match)
If you need the index, use rindex. If you need the element, use rfind. That's the distinction.

Also, don't do array.select(&:even?).last when you can use rfindselect processes the entire array, while rfind stops at the first match from the end. It's both faster and more readable.

Edge Cases

Nothing surprising here. It behaves exactly like find, just backwards:

[].rfind(&:odd?)                    # => nil
[2, 4, 6, 8].rfind(&:odd?)          # => nil
[nil, 1, nil, 2].rfind { |x| x }    # => 2


Implementation Details

For those curious: rfind is implemented natively in C (in CRuby), so it's fast. It starts at the last index and walks backwards until it finds a match or hits the beginning. No array allocation, no extra overhead.

Worth noting: Ruby 4 also optimized Array#find itself. Previously it used the generic Enumerable#find, but now it has its own optimized implementation. So find got faster too, not just rfind.


When to Use It

Use rfind when you need the last matching element. Use find when you need the first. That's really it.

One thing I've noticed: if you're working with time-ordered data and need the most recent match, rfind makes the intent crystal clear. Code reviews are easier when the method name tells you what it does.

Array#rfind solves a real problem that comes up more often than you'd think. It's faster, cleaner, and more readable than the alternatives. If you're on Ruby 4, start using it. Your future self (and your code reviewers) will thank you.


Further Reading


Happy coding with Ruby 4! 🚀

ruby ruby-4 array rfind performance ruby-tips enumerable ruby-features array-methods ruby-tutorial software-development coding-best-practices

Comments (0)

No comments yet. Be the first to comment!