ということに気付いたのでメモ*1。
これを頭に入れておくと「ああ、これ select
に似てるのに、返値にレシーバから以外の要素を入れたいから select
じゃ書けない…>_<」って時に「そうか each_with_object
で書けば良いんだ」とササっと書ける(のではないかなと思った)。
ちなみに、個人的に「inject
の代わりに each_with_object
を使ってみよう」運動を実施中です。
each_with_objectの復習
each_with_object(obj) {|(*args), memo_obj| ... }
-> object与えられた任意のオブジェクトと要素をブロックに渡し繰り返し、最初に与えられたオブジェクトを返します。
instance method Enumerable#each_with_object (Ruby 2.3.0)
参考:
each_with_object はブロック引数のオブジェクトを破壊的に変更し続ける場合に便利なメソッド
each、each_with_object、inject、map - プログラミングは素晴らしい
逆に言えば、ブロックパラメータの memo_obj
に破壊出来ないオブジェクト(Numericとか)を使うことは出来ません。注意しましょう。
次に整数の和を求める例を示す。
# each_with_object # 破壊的に変更可能なオブジェクトでないと適用不可 sum = [ 1, 2, 3].each_with_object(0) do |i, j| j += i end # p sum #=> 0
各メソッド
to_a/entries
Enumerable#to_a Enumerable#each_with_object([]) { |elem, ary| ary << elem }
map/collect
Enumerable#map { |elem| formula } Enumerable#each_with_object([]) { |elem, ary| ary << formula }
find_all/select
Enumerable#select { |elem| formula } Enumerable#each_with_object([]) { |elem, ary| ary << elem if formula }
reject
Enumerable#reject { |elem| formula } Enumerable#each_with_object([]) { |elem, ary| ary << elem unless formula }
grep(grep_v は判断を if じゃなく unless に)
- ブロックなし
Enumerable#grep(pattern) Enumerable#each_with_object([]) { |elem, ary| ary << elem if pattern === elem }
- ブロックあり
Enumerable#grep(pattern) { |elem| formula } Enumerable#each_with_object([]) { |elem, ary| ary << formula if pattern === elem }
partition
Enumerable#partition { |elem| formula } Enumerable#each_with_object([[], []]) do |elem, (ary_true, ary_false)| if formula ary_true << elem else ary_false << elem end end
first/take
Enumerable#take(n) Enumerable#each_with_object([]).with_index { |(elem, ary), i| ary << elem if i < n }
take_while
…書けず。
drop
Enumerable#drop(n) Enumerable#each_with_object([]).with_index { |(elem, ary), i| ary << elem unless i < n }
drop_while
…これも書けず。
残念
Enumerator#with_index
の手助けが必要な場合もありました。
while 系はダメでしたね((flat_map
はムリっぽいのでやってないですorz))。
*1:速度に関しては無視する。