ハッカーを目指す白Tのブログ

夏は白のTシャツを着ています。ラジオが好きです。(ラジオネーム: 隠れキリジダン)

Rubyのmap、reduce処理について

Rubyのmap処理が結構ややこしいのでまとめてみた。
最後にreduceメソッドについても触れる。

1.RubyのArray#map, Hash#mapメソッドについて

RubyのArrayクラス、Hashクラスのmapメソッドは、ブロックを引数にとり、要素の数だけループをまわし、その戻り値をarrayに格納する。mapメソッドは、別名collectメソッドともいう。
mapメソッドが使われる例として、以下のようなものがあげられる。

#1.arrayの要素に処理をしてarrayに格納する
array = [ 1, 2, 3, 4, 5, 6 ]
array.map{ |num| num.to_s }  #=> [ "1", "2", "3", "4", "5", "6" ]

#2.hashのvalueをarrayに格納する
hash = { key1: 1, key2: 2, key3: }
hash.map{ |k,v| v }   #=>[ 1, 2, 3 ]

#2については、以下の方法もある
hash.values  #=> [ 1, 2, 3 ]

2.Rubyのmap処理を短く記述する

RubyではActiveSupportというgemをrequireすることで、上記のmap処理をより簡単に記述することができる。

ActiveSupportは、rubyのstring, array, hash, dateオブジェクトにメソッドを追加するライブラリである。Railsの機能のひとつであるが、Rubyの機能ではないことに注意してほしい。)

その記法について、説明する。

#1.arrayの要素に処理をしてarrayに格納する
array = [ 1, 2, 3, 4, 5, 6 ]
array.map(&:to_s)  #=> [ "1", "2", "3", "4", "5", "6" ]

mapメソッドの引数の先頭についている「&」は、その引数がblockであることを表す。
Rubyでは、ほとんどのものがobjectだが、blockはobjectではない。そこで、「&」のあとにはblock内の手続きとコンテキストをオブジェクト化した、Procオブジェクトが期待される。

(Procとは、procedureの略で、手続きを意味する。また、コンテキストとは、簡単に言うとローカル変数の状態のことをいう。)

しかし、上記の例では「&」のあとにProcオブジェクトがきている訳ではない。なぜ、このような記法が許されるかというと、requireしたRubyのgemであるActiveSupportが「&」のあとにあるオブジェクトにto_procメソッドがある場合、to_procメソッドを呼び出してからブロックにすると定義しているからである。上記の例では、シンボルオブジェクトにto_proc メソッドが定義されていたから、そのメソッドが呼び出され、Procオブジェクトに変換されたものが、「&」によりブロックに変換され、map処理が正常に行われた。

3.Enumerable#reduceメソッドについて

Enumerable#reduceメソッドは、ArrayやHashの各値をブロックを使って繰り返し計算するのに使う。ブロックに順に、「Arrayやhashの要素1、Arrayやhashの要素2」、「一つ前のブロック処理の戻り値、Arrayやhashの要素3」、「一つ前のブロック処理の戻り値、Arrayやhashの要素4」、...を渡す。メソッドの戻り値は、ブロックが最後に返した値になる。reduceメソッドは、別名、injectメソッドともいう。
以下、reduceメソッドの使用例を紹介する。

numbers = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
numbers.reduce{ | sum, n | sum + n }  #=> 55

#reduceの引数を指定すると、ブロックに順に、「引数、ArrayやHashの要素1」、「一つ前のブロックの戻り値、ArrayやHashの要素2」、「一つ前のブロックの戻り値、ArrayやHashの要素3」、...を渡す
numbers.reduce(100){ | diff, n | diff - n }     #=> 45

以上