- 公開日
Ruby の inject(reduce) メソッドを使いこなす
目次
Rubyのイテレータメソッドinject(reduce)の使い方を紹介してみようと思います。
文中では便宜上、injectで表記しますがreduceも等価なメソッドなので文中のinjectはreduceに置き換え可能です
injectの基本的な使い方
Rubyのinjectはこんなふうに使えます。
enum.inject { |memo, item| block }
enum.inject(init) { |memo, item| block }
Sum(合計)を出す
これだけではわかりにくいと思うので合計を出す処理を書いてみましょう。
injectを使わないパターン
まずはinjectを使わないパターン。
sum = 0
(1..10).each { |i| sum += i }
puts sum # => 55
injectを使うパターン
これをinjectで書き直すとこう。
(1..10).inject(0) { |sum, i| sum + i }
# => 55
この場合、sumの初期値をinjectの引数の0で初期化しています。ただinjectのデフォルト引数設定は0なので下記のように省略することができます。
(1..10).inject { |sum, i| sum + i }
# => 55
さらにinjectにはシンボルで演算子を渡してうまいようにやってくれます。
(1..10).inject(:+)
# => 55
これが一番シンプルでエレガント。
Ruby 2.4+ の場合
Ruby 2.4 以上をお使いの場合、Array#sumを使って書くことができます。
(1..10).sum
# => 55
応用編1: 配列内の要素数をハッシュに
Hash.new(0)で初期化してinject.
[:great, :good, :bad, :good, :good, :bad, :awesome, :great].inject(Hash.new(0)) { |hash, key| hash[key] += 1; hash}
# => {:great=>2, :good=>3, :bad=>2, :awesome=>1}
each_with_object を使う
上記の例だとhash[key] += 1; hashとやや冗長な書き方となっていますが、each_with_object使うともう少しシンプルに書けます。
[:great, :good, :bad, :good, :good, :bad, :awesome, :great].each_with_object(Hash.new(0)) { |key, hash| hash[key] += 1}
# => {:great=>2, :good=>3, :bad=>2, :awesome=>1}
応用編2: フィボナッチ数列
inject を利用したフィボナッチ数列ロジック。
(0..10).inject([1, 1]) { |fib, i| fib << fib[i] + fib[i+1] }
# => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233]
応用編3: ループを止める
途中でループを止めて処理を終えたい場合、breakを使えば可能。
(1..5).inject([]) do |ary, number|
break ary if number == 4
ary << number
end
# => [1, 2, 3]
番外編: each_with_index
each_with_indexでもinjectは使える。
(1..10).each_with_index.inject(0) { |sum, (value, index)| sum + value }
# => 55
(1..10).each_with_index.inject(0) { |sum, (value, index)| sum + value + index}
# => 100