箱のプログラミング日記。

渋谷の自社開発企業でRails書いてます。

配列に対する繰り返し処理【map】【collect】

f:id:y_hakoiri:20191102121915j:plain

Rubyで配列に対して繰り返しの処理を行う時は、まずeachを使っていたのですが

mapやcollectについてもきちんと勉強し、使いこなせるようにしようと思い

色々試してみました。

 

mapメソッドとは

array.map { |item| block }

mapメソッドは、要素の数だけ繰り返しブロックを実行し、ブロックの戻り値を集めた配列を作成して返します。

numbers = ["68", "65", "6C", "6C", "6F"]
p numbers.map {|item| item.to_i(16) }
[104, 101, 108, 108, 111]

 

ref.xaio.jp

 

うーん、、説明だけ読むといまいちよく分からないんだよなぁ。

そして、このリファレンスに載っているサンプルコードも

いまいち分かりづらいw(16進数?)

 

最終的に整理がついたのですが、

  • 配列に対して使う
  • 配列の中身一つずつに同じ処理をし、新しい値でまた配列をつくる

みたいなイメージだと分かりやすいかな。

 

自前のサンプルコードと解説

array = [1, 2, 3, 4, 5].map {|num| num + 2}
puts array

この場合、結果は[3, 4, 5, 6, 7]になります。

 

mapメソッドは配列に対して使用するもので、

配列の中身の要素一つ一つに、指定の処理を実行していき、

その結果で新たな配列をつくる、というものです

 

ここでは、配列[1, 2, 3, 4, 5]の中身の要素を引数|num|に一つずつ代入し、

numに対して同じ処理(num + 2) を行っています

それを要素の数の分繰り返し、

出来上がった新しい配列をarrayに代入しています

 

2を足した数字で新しく配列が作られるので、

[3, 4, 5, 6, 7]という結果になる、ということです

 

ちなみにここでいうnum + 2はリファレンスの説明内で言う

「ブロック」に当たります。

 

ブロックの別の書き方

mapやcollectメソッドに限らず、

Rubyでは別のブロックの記述方法があります。

以下の2種類の書き方は同じ結果を返します。

 

array = [1, 2, 3, 4, 5].map {|num| num + 2}
 
array = [1, 2, 3, 4, 5].map do |num|
 num + 2
end
 
一行で書いた {|num| num + 2}の部分ですが、
ここの大カッコはdo~endの形で書くこともできます。
 
と言うか、本来であればdo~endで書かれるものを
一行で短く書けるように{}で省略しているようです。
 
each文とかも実際は{}で省略して書ける、と言うことですね
 
mapメソッドってなんかとっつきにくいなーと思って
書けない、読めない状態だったんですが
これを知ってから完全に腑落ちしました。
 
短く書けるものは簡潔に書いた方がもちろん良いので、
mapメソッドでは{}で書かれることの方が多いようですね。
(というか、ほぼそれしか見たことがない)
 

eachで書いてみる

array = []
[1, 2, 3, 4, 5].each do |num|
 array << num + 2
end
puts array
 
each文の場合、配列の各要素に対し繰り返し処理をしてくれる
というところまでは同じですが、
処理結果を配列で返すまではしてくれないので、
 
わざわざ空の配列arrayを用意し
繰り返し処理の中で追加していく、という記述が必要になります

 

配列の結果を期待する時は

each文で冗長に書かずに、mapを使用すべきですね。

 

collectで書いてみる

array = [1, 2, 3, 4, 5].map {|num| num + 2}
array = [1, 2, 3, 4, 5].collect {|num| num + 2}
 

collectの場合、mapメソッドと同じ書き方で同じ結果を得ることができます。

リファレンスにはこのように定義されています

collectメソッドは、要素の数だけ繰り返しブロックを実行し、ブロックの戻り値を集めた配列を作成して返します。ブロック引数には各要素が入ります。

mapメソッドはcollectメソッドの別名です。

 

 

mapとcollectの違い

書き方も同じで結果も同じメソッドがなぜ2つ存在するんだ・・・!

と思って違いを調べたところ、すごく分かりやすい図を見つけました

tech-camp.in

 

概念的な部分で違いがある様子。

  • mapメソッドは配列の形を維持したまま中の要素に対し処理を行う
  • collectメソッドは配列を一旦バラし、一つずつの要素に対し処理を行ったあと 再度集めて配列にする

 

この解釈でいくと、最初に私が説明したイメージは

どちらかというとcollectメソッドに近いのかもしれないです。。

 

 

2019/9/7追記

Railsチュートリアルをやっていたところ

こういう省略形で表現されることもある、というのを知りました

>> %w[A B C].map { |char| char.downcase }
=> ["a", "b", "c"]
>> %w[A B C].map(&:downcase)
=> ["a", "b", "c"]

 

ブロック内の引数に対してある処理を行う場合

わざわざ二回、同じ引数を書くのが面倒なので

&:で表現することが多いらしい。

 

その場合、ブロックが{}ではなくなり

単純にmapの引数として(&:downcase)が登場する感じなんだな。

確かにこっちの方が簡潔で分かりやすいかも〜

 

ただ、downcaseメソッドをシンボルで表現している形なので

メソッド以外の処理を行う場合は使えなさそう。(べき乗とか)

 

Ruby on Rails チュートリアル:実例を使って Rails を学ぼう