Array#max_by
arr.max_by { |item| ... } Array#max_by returns the element whose block value is the largest under <=> comparison. The method itself is defined on Enumerable, and Array inherits it unchanged. Reach for it when you need the “best” element according to a derived value such as length, a hash key, or a computed score.
Signature
The method has four call shapes:
arr.max_by # => Enumerator
arr.max_by { |item| ... } # => element | nil
arr.max_by(n) # => Array
arr.max_by(n) { |item| ... } # => Array
The n-argument form was added in Ruby 2.2.0. With n, the method returns an Array of the top elements ordered by decreasing block value.
Return Value
With a block and no n, the method returns the array element whose block value is greatest, or nil if the array is empty. Without a block and no n, you get an Enumerator, which is chainable with with_index and friends. With n as the argument, you get an Array of the top n elements ordered by block value in descending order; an empty array returns [].
Examples
The longest word in a list
%w[cat elephant dog].max_by { |w| w.length }
# => "elephant"
The block returns a numeric length, and max_by compares those lengths via <=>. The original string still wins because max_by returns the element, not the block value. This pattern generalises to any block whose return value is comparable with <=>, which is most numeric, string, or symbol keys.
The record with the highest score
records = [
{ name: "Ada", score: 92 },
{ name: "Bjorn", score: 87 },
{ name: "Cleo", score: 95 }
]
records.max_by { |r| r[:score] }
# => {:name=>"Cleo", :score=>95}
Closest value to a target
max_by finds the maximum, so to find the value closest to a target, use a negated distance as the key. The negation flips the order, so smaller distances become larger (less negative) scores, and the highest of those wins. This trick is the standard way to repurpose a “maximum” search as a “closest match” search without sorting.
target = 50
[12, 47, 53, 60, 81].max_by { |n| -((n - target).abs) }
# => 47
When the distance is tied (47 and 53 are both 3 away), the first element in iteration order wins. If you need a true nearest neighbor, sort by (n - target).abs and pick the first.
The top N elements (Ruby 2.2+)
records.max_by(3) { |r| r[:score] }
# => [{:name=>"Cleo", :score=>95}, {:name=>"Ada", :score=>92}, {:name=>"Bjorn", :score=>87}]
Pass 0 to get an empty Array; pass a value larger than the array’s size and you get the whole array sorted by block value.
Enumerator form for chaining
Calling max_by without a block returns an Enumerator, which is the standard Enumerable behavior:
%w[ant bee cat].max_by.with_index { |w, i| w.length * 10 - i }
# => "ant"
with_index lowers the score of later elements slightly, breaking length ties in favor of earlier positions.
max_by vs max
Array#max compares the elements themselves using their own <=>. Array#max_by lets you supply a comparison key via the block. Use max_by whenever the rule for “largest” depends on something you have to compute, such as a string’s length, a hash value, a database column, or a score you derive on the fly. Use max when the elements already compare the way you want (numbers, strings, etc.).
[3, 1, 2].max # => 3
[3, 1, 2].max_by(&:itself) # => 3
Gotchas
- It returns the element, not the block value. If you want the block value too, recompute it on the result:
best = arr.max_by { |x| score(x) }; [best, score(best)]. - Ties go to the first element. The implementation only replaces the current best when the new key is strictly greater. If tie order matters, sort and pick, or pre-filter the array.
- No block, no
nreturns anEnumerator. Forgetting the block in tight code surfaces as aNoMethodErrorlater when the enumerator is consumed. Pass a block or call.first/.to_aexplicitly. - The block value must be comparable via
<=>. If the block can return different types (sometimesInteger, sometimesnil), Ruby raisesArgumentError: comparison of NilClass with Integer failed. Filter or coerce first. - The
n-arg form may return more thannitems on ties. This matches the documentedEnumerable#max_bycontract, so don’t rely on a strict length assertion without sorting. - The block runs once per element. O(n) calls, but if the block does heavy work (network calls, parsing), cache the key first with
group_byor build a hash.
See Also
Array#max- returns the largest element using its own<=>.Array#min_by- the symmetric counterpart for the smallest element.Enumerable#max_by- the source method thatArrayinherits.