Enumerableモジュールのメソッドツアー - max_byメソッド
Enumerableモジュールのメソッドを1記事1メソッドで雑に紹介していくコーナー第3弾です。
前回はmax
メソッドを紹介しました。
今回はmax
メソッドにとても似ているmax_by
メソッドについて紹介します。
max_byメソッド
まずはるりま*1の確認から。
module Enumerable (Ruby 3.1 リファレンスマニュアル)
各要素を順番にブロックに渡して実行し、その評価結果を <=> で比較して、最大であった値に対応する元の要素、もしくは最大の n 要素が降順で入った配列を返します。 引数を指定しない形式では要素が存在しなければ nil を返します。引数を指定する形式では、空の配列を返します。該当する要素が複数存在する場合、どの要素を返すかは不定です。 Enumerable#max と Enumerable#max_by の違いは Enumerable#sort と Enumerable#sort_by の違いと同じです。
「各要素をブロックに渡して実行し、〜」とあるようにブロック付きで呼ぶのが基本となるメソッドみたいです。 maxメソッドと同じく、なんらかの観点で比較したときの、最大の要素を引数の数だけ取得するのが基本の使い方になります。
ちなみに、ブロックなしで呼び出した場合はEnumerator
オブジェクトが返ってきます。
では挙動を確認していきます。
irbで挙動の確認
Ruby 3.1.2で実行しています。
ブロックありでの呼び出し
# 一の位が最大である要素を取得 [8, 32,4].max_by{ |n| n % 10} # => 8 # イメージ 8 % 10 <=> 32 % 10で比較 8が大きい 8 % 10 <=> 4 % 10 で比較 8が大きい 8を返す # 最大の要素がない場合はnil [].max_by{ |n| n } => nil
引数を指定すると最大の要素を引数の数だけ取得することができます。 また、返り値は配列になります。
# 文字数で比較 %w(panda monkey dog cat).max_by(2) { |str| str.length } => ["monkey", "panda"] # 引数に負数を渡すとArgumentError %w(panda monkey dog cat).max_by(-1) { |str| str.length } `max_by': negative size (-1) (ArgumentError)
ちょっとだけ実用的な例を見てみましょう。 数学のテストの点数が一番高いユーザーのテスト結果を調べます。
students = [ {name: 'taro', japanese: 30, math: 50, english: 90}, {name: 'jiro', japanese: 80, math: 23, english: 20}, {name: 'saburo', japanese: 88, math: 92, english: 79}, ] students.max_by { |student| student[:math] } => {:name=>"saburo", :japanese=>88, :math=>92, :english=>79}
ブロックなしで呼び出した場合
ブロックなしで呼び出した場合はEnumerator
オブジェクトを返します。
('a'..'c').max_by => <Enumerator: ...> ('a'..'c').max_by(2) => <Enumerator: ...>
maxメソッドとmax_byメソッドの違い
言葉で説明するのは難しいのですが、
maxメソッドは要素を2つずつ取り、明示的に<=>
メソッドで比較を行います。(正確には左辺が大きいときは正の数、右辺が大きいときは負の数、等しいときは0を返せば<=>
メソッドでなくても良いです)
一方、max_by
メソッドは比較基準だけ示しておけば、内部的に<=>
メソッドで比較を行ってくれます。
パフォーマンスの観点からどちらが優れているというのは未検証のため、言及は控えておきます。
# maxメソッドの場合 %w(panda monkey dog cat).max { |animal1, animal2| animal1.length <=> animal2.length } => "monkey" # max_byメソッドの場合 %w(panda monkey dog cat).max_by { |animal| animal.length } => "monkey"
何かしらの観点で最大の要素を取得したい場合はmax_by
を使った方が記述量は少なくすみそうです。
まとめ
- max_byは最大の要素を取得する
- ブロックを評価した後
<=>
で比較処理が行われる - 引数に正整数を渡すとその数だけ最大の要素を取得できる
- ブロックなしで呼び出した場合は
Enumerator
オブジェクトが返ってくる
以上です!