Edited at

injectとeach_with_objectって何が違うのさ?

More than 5 years have passed since last update.

injecteach_with_objectの違いが分からない。

どちらもEnumerableなオブジェクトの要素を使って何かしらのオブジェクトを得る。

でもやっぱり違いがあったのでメモ。


イメージで説明



  • injectは、各要素が力を合わせて一つのオブジェクトを作る


  • each_with_objectは、ターゲットとなるオブジェクトに対して、各要素を作用させる


挙動で説明

配列の各要素を2乗する処理を書く。表面上は同じ挙動をする。

でもdoの後のresultは、同じresultでも意味合いが違っていて、



  • injectの場合はresultにはブロック内で最後に評価した値が入る


  • each_with_objectの場合は、resultは常にeach_with_objectの引数として渡されたオブジェクトを指す


hoge.rb

# inject

[1, 2, 3].inject [] do |result, i|
result << i*i
end

# each_with_object
[1, 2, 3].each_with_object [] do |i, result|
result << i*i
end



injectを使いたい場合の例

たとえば配列の和を求める場合


hoge.rb

sum = [1, 2, 3].inject 0 do |s, i|

s + i
end

(この場合はsum = [ 1, 2, 3].inject :+でいいけど敢えてこの形で。)

ここでeach_with_objectを使おうとしてしまうと、


hoge.rb

sum = [ 1, 2, 3].each_with_object 0 do |i, s|

s += i
end

とすればよさげだけど、sは破壊的に変更できない0(Fixnum)を指しているので、

sumに入るのは0となってしまう。


each_with_objectを使いたい場合の例

たとえば[["Alice", 50], ["Bob", 40], ["Charlie", 70]]から、{"Alice" => 50, "Bob" => 40, "Charlie" => 70}を得たい場合、


hoge.rb

ret = [["Alice", 50], ["Bob", 40], ["Charlie", 70]].each_with_object({}) do |(key, value), hash|

hash[key] = value
end

こんな感じでかける。

ここでinjectを使おうとすると、できないわけではないけど、

作成中のハッシュをブロックの最後に評価しないといけないので1行余分に必要になる。


hoge.rb

ret = [["Alice", 50], ["Bob", 40], ["Charlie", 70]].inject({}) do |hash, (key, value)|

hash[key] = value
hash # ブロックの最後の評価を作成中のハッシュにしないといけない
end

参考:

プログラミングは素晴らしい - [プログラミング] each、each_with_object、inject、map