はじめに
Ruby には、畳み込み演算を行う Enumerable モジュールのメソッドとして inject
と reduce
というのがあります。
詳しい挙動はこちらを見ていただけると良いと思います。
これらのメソッドはとても便利で、これらを使えば綺麗に書ける場合が多々あります。
しかし少し挙動が分かりにくいため、これらが使えると気付かずに冗長に書いてしまうことが良くあるなと最近思ったので、inject
と reduce
の活用例をまとめておこうと思います。
活用例
商品の注文 order
のうち飲み物の注文(order.type == 'drink'
)の金額 order.fee
の合計額を出したい。
【書いてしまいがちなコード】
def sum_of_drink_order sum = 0 orders.each do |order| sum += order.fee if order.type == 'drink' end sum end
【inject
・reduce
を使うと...】
def sum_of_drink_order orders.inject(0) do |sum, order| sum + order.fee if order.type == 'drink' end end
ある日にアクセスされた記事の ID がアクセスされた回数分ダブって入っている配列 accessed_article_ids
を用いて、各記事のアクセス数のデータ({ '記事ID' => 'アクセス数' }
)を作りたい。
【書いてしまいがちなコード】
def article_access_count hash = Hash.new(0) accessed_article_ids.each do |accessed_article_id| hash[accessed_article_id] += 1 end hash end
【inject
・reduce
を使うと...】
def article_access_count accessed_article_ids.reduce(Hash.new(0)) do |hash, accessed_article_id| hash[accessed_article_id] += 1 hash end end
アカウント account
の中から累計ログイン回数 account.login_count
が 10 回以上のアカウントのメールアドレス account.email
を配列で取得したい。
【書いてしまいがちなコード】
accounts.select{ |account| account.login_count >= 10 }.map(&:email)
【inject
・reduce
を使うと...】
accounts.reduce([]) do |array, account| array << account.email if account.login_count >= 10 array end
終わりに
これらの活用例のように、inject
・reduce
を使うことで分かりやすいコード・効率的なコードを書くことができます。
特に3つ目の活用例は、一見複雑になっているように見えるかもしれませんが、複数の配列を作る必要のない効率的なコードだと思います。
each
・map
・select
などの分かりやすい Enumerable モジュールのメソッドを良く考えずに使ってしまいがちですが、inject
・reduce
も結構使う場面あるよ、という話でした。