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
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
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)
rindex. If you need the element, use rfind. That's the distinction.Also, don't do array.select(&:even?).last when you can use rfind. select 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
- Ruby Feature #21678 - The original feature request
- Ruby PR #15189 - The implementation pull request
- Ruby Array Documentation - Official Array documentation
Happy coding with Ruby 4! 🚀
Comments (0)