茶漬けと玉子焼き

swiftとrubyの教室

Ruby の each_with_object と inject について

each_with_object とは

each_with_object を使用すると、配列やハッシュの要素を使って、
新たに配列やハッシュを作成したりする際に、簡潔に書くことができます。
簡単な例を以下に示します

array = [[:hoge, 1], [:fuga, 2], [:piyo, 3]]

array.each_with_object({}) do | (key, value), hash |
  hash[key] = value
end
# => {:hoge=>1, :fuga=>2, :piyo=>3}


inject とは

inject は each_with_object と一見似ていますが、
返り値等の振る舞いが微妙に異なります。
先ほどと同じ振る舞いをinjectを使って実現すると。

array = [[:hoge, 1], [:fuga, 2], [:piyo, 3]]

hash = array.inject({}) do | hash, (key, value) |
  hash[key] = value
  hash
end

p array  # => [[:hoge, 1], [:fuga, 2], [:piyo, 3]]
p hash  # => {:hoge=>1, :fuga=>2, :piyo=>3}

まず、each_with_object が破壊的メソッドであるのに対し
injectは破壊的ではないことがわかります。

さらに、

  • each_with_object では 引数に渡されたオブジェクトが常にhash変数に入っていますが
  • inject ではブロック内で最後に評価された値が入ります

そのため、injectで同じ挙動を再現したかったら最後に hash の一行を余計に書く必要が出てきます。


inject の使い所は?

配列やハッシュの要素から一つのオブジェクトを作る場合は injectがとても便利です。

sum = 0
(0..10).each { |i| sum += i }
p sum   # => 55

こんなコードを

(0..10).inject(0) { |sum, i| sum + i }  # => 55

こんな感じに簡潔にすることができます。

さらに

(0..10).inject(:+)  # => 55

こんな風にもっとスマートに書くこともできます。