みなさんこんにちは!
今回は、

・collect()メソッドやeach()メソッドとどう使い分けたらいいの?
というお悩みを解決する記事になっています。
Rubyでは、配列をスマートに取り扱うためのメソッドが数多く用意されています。
中でもmap()メソッドは、配列処理において最も利用されるであろうメソッドの1つで、map()メソッドの使い方を覚えれば、あらゆる配列処理をスムーズに実装できるようになります。
今回は、map()メソッドの使い方について、サンプルプログラムを適宜交えながら詳しく確認していきます。また、混同してしまいがちなその他のメソッドとの違いについても解説していますので、ぜひ最後まで読んでみてください。
-272x300.jpg)
みなさんこんにちは! 今回の記事は、 悩みを抱えた人 ・Rubyってどんなプログラミング言語なの?・Rubyを勉強すると将来役に立つかな?・Rubyのおすすめの学習方法が知りたい! というお悩みを解決する記事にな[…]
フリーランス案件を探すならこのエージェント!
転職を考えているならこのエージェント!
map()メソッドの利用方法
map()メソッドは、RubyのEnumerableモジュールの中で定義されているインスタンスメソッドです。
Enumerableモジュールは、簡単に言えば列挙されたオブジェクトを意味し、配列・ハッシュ・範囲オブジェクトなどがそれに当たります。
map()メソッドを配列で使う
それでは早速、実際のコードを見ながら確認してみましょう。
数値が格納された配列に対し、各要素を累乗したものを新しく配列として作成したいとします。この問題をmap()メソッドを使って実装したものが、以下のプログラムです。
# 配列を用意
sample_data = [3, 4, 12, 45, 51, 67, 87]
# map()メソッドを呼び出す
exp_data = sample_data.map { |data|
# 累乗を計算
# 計算結果がそのまま新しい配列に要素として格納される
data * data * data
}
p exp_data # [27, 64, 1728, 91125, 132651, 300763, 658503]
map()メソッドの基本形は以下の通りです。
[配列の名前].map { |要素を格納する変数の名前| 実施する処理 }
map()メソッドは配列やハッシュなどをレシーバとして呼び出します。メソッドの中では、要素を格納する変数を利用して処理を実装します。戻り値は、最終的な計算結果が格納された配列です。
map()メソッドの実行結果について詳しく見てみましょう。
map()メソッドは、一度呼び出されると、ブロックの中で定義した処理をレシーバの要素数と同じだけ繰り返します。
今回の例を見てみると、初回実行時は1番目の要素に格納されている「3」をメソッド内で利用し、処理を行います。今回は累乗を計算(3乗)する処理を実装したため、したがって、新しく生成する配列の1番目の要素に「27」が格納されます。
これを各要素ごとに実行することで、最終的な戻り値「[27, 64, 1728, 91125, 132651, 300763, 658503]」が生成されます。これにてmap()メソッドの処理は完了です。
map()メソッドをハッシュで使う
ハッシュに対してもmap()メソッドを利用できます。
例として、ある生徒のテストの点数が格納されたハッシュから、各教科ごとの評価を行うプログラムを考えてみましょう。この問題をmap()メソッドで実装したものが、以下のプログラムです。
# 教科と点数が紐付けされたハッシュを用意
test_score = {
japanese: 87,
math: 91,
science: 71,
social_study: 49,
english: 68
}
# ハッシュをレシーバとしてmap()メソッドを呼び出す
evalution = test_score.map { |subject, score|
# テストの点数毎に評価値を設定
case score
when (81..100)
ev_str = 'A'
when (71..80)
ev_str = 'B'
when (61..70)
ev_str = 'C'
else
ev_str = 'D'
end
# 教科と評価を紐付ける
[subject, ev_str]
}
# map()メソッドの戻り値はレシーバの種類によらず配列であることに注意
p evalution # [[:japanese, "A"], [:math, "A"], [:science, "B"], [:social_study, "D"], [:english, "C"]]
# to_h()メソッドでハッシュ化可能
p evalution.to_h # {:japanese=>"A", :math=>"A", :science=>"B", :social_study=>"D", :english=>"C"}
呼び出し方は配列の場合と変わりません。メソッド内の処理についてですが、ハッシュの場合はキーと値をそれぞれ変数として持つことができます。
配列で呼び出した場合と異なる点として、ハッシュをレシーバとしてmap()メソッドを呼び出しても、戻り値は配列であることに注意してください。
例えば今回の例であれば、教科名と点数毎に導出した評価値を配列として格納し、さらにそれを要素とした二次元配列を作成しています。もし、ハッシュとして取り扱いたい場合は、to_h()メソッドを呼び出してハッシュ化してあげましょう。
map()メソッドを省略して書いてみる
ある条件を満たしていれば、map()メソッドの中身をより少なくすることができます。その条件は、以下の通りです。
- 引数が1つであること
- 中身の処理が引数のないメソッドを呼び出すだけであること
例えば、数値型のデータが格納された配列が存在するとして、全ての文字列型に変換する処理を考えてみましょう。
map()メソッドの中身で、引数をレシーバとしてto_s()メソッドを呼び出せば目的の処理が実装できそうです。
ただし、引数が1つだけであり、引数が存在しないメソッドであるto_s()メソッドを呼び出すだけでよいので、以下のサンプルコードのように実装できます。
# 数値型のデータが格納された配列
sample_data = [36, 54.4, 1000, 3, -35, 900, -3.65, 1264, 44]
# 数値型の要素を全て文字列型に置き換える処理
sample_data_s_1 = sample_data.map(&:to_s)
for value in sample_data_s_1 do
# class()メソッドで変数の型を取得
# 文字列であれば「string」を返す
p value + ":" + value.class.to_s
end
上記サンプルコードの実行結果は、以下の通りです。
"36:String"
"54.4:String"
"1000:String"
"3:String"
"-35:String"
"900:String"
"-3.65:String"
"1264:String"
"44:String"
サンプルコードの5行目に注目してください。5行目で実装した処理は、以下の処理と同様の動作をします。
# 省略しない書き方
sample_data_s_2 = sample_data.map { |value|
value.to_s
}
1行で記載する方法は、最初は慣れないかもしれませんが、使っているうちにその便利さにどんどん気づいていくと思いますので、積極的に使ってみてくださいね。
map()メソッドと混同しやすいメソッドたち
Rubyでは、map()メソッド以外にも配列処理にうってつけのメソッドが複数用意されています。
中には、map()メソッドと動作が似ていることためにそれぞれを混同してしまうこともありますので、しっかりと違いを把握しておきましょう。
ここでは、動作が似ているメソッドを3つ取り上げ、map()メソッドとどのように違うのかを説明していきます。
collect()メソッドとの違い
1つ目は、collect()メソッドです。collect()メソッドは名前が異なるだけで動作自体はmap()メソッドと全くもって同じです。
以下のサンプルコードで、両者の呼び出した結果が同じであることを確認しましょう。
VERTICAL_LENGTH = 0
HORIZONTIAL_LENGTH = 1
# 縦と横の長さを持つ配列
vertical_horizontal_length_list = [
[123, 456],
[35, 35],
[900, 231],
[48, 90],
[100, 100]
]
# 面積を計算し結果を配列化
# map()メソッドで実装した場合
area_list_1 = vertical_horizontal_length_list.map { |data|
data[VERTICAL_LENGTH] * data[HORIZONTIAL_LENGTH]
}
# collect()メソッドで実装した場合
area_list_2 = vertical_horizontal_length_list.collect { |data|
data[VERTICAL_LENGTH] * data[HORIZONTIAL_LENGTH]
}
p area_list_1 # [56088, 1225, 207900, 4320, 10000]
p area_list_2 # [56088, 1225, 207900, 4320, 10000]
map!()メソッドとの違い
2つ目は、map!()メソッドです。
map!()メソッドは、引数の扱い方や実装の仕方、戻り値が配列である点でmap()メソッドと変わりありません。大きく違う点は、レシーバ自体も戻り値と同様の配列に変更する仕様があることです。
Rubyでは、レシーバの情報をインスタンスメソッドを呼び出して変更することを「破壊的変更」と呼び、破壊的変更を行うメソッドを「破壊的メソッド」と呼びます。
以下のサンプルコードを実行して、map()メソッドとmap!()メソッド呼び出し後のレシーバの状態を確認しましょう。
LAST = 0
FIRST = 1
# 苗字と名前を持つ配列
name_list = [
['Suga', 'Yoshihide'],
['Abe', 'Shinzo'],
['Noda', 'Yoshihiko'],
['Kan', 'Naoto'],
['Hatoyama', 'Yukio'],
['Aso', 'Taro']
]
name_list.map {|data|
data[LAST] + " " + data[FIRST]
}
# map()メソッド呼び出し後は変化なし
p name_list # [["Suga", "Yoshihide"], ["Abe", "Shinzo"], ["Noda", "Yoshihiko"], ["Kan", "Naoto"], ["Hatoyama", "Yukio"], ["Aso", "Taro"]]
name_list.map! {|data|
data[LAST] + " " + data[FIRST]
}
# map!()メソッド呼び出し後は変更される
p name_list # ["Suga Yoshihide", "Abe Shinzo", "Noda Yoshihiko", "Kan Naoto", "Hatoyama Yukio", "Aso Taro"]
レシーバの情報をこれ以降使うことがなければ、新しく変数を用意せずに破壊的メソッドで書き換えてしまうのも一つの手です。
each()メソッドとの違い
3つ目は、each()メソッドです。
map()メソッドとの違いは、戻り値はレシーバ自身であるということです。
以下のサンプルコードでは、sample_dataという配列に対してeach()メソッドを呼びだし、その中で配列の各要素を2分の1にする処理を実装しています。
処理結果をresultという変数に代入していますが、結果は変わらず、sample_dataと同じ中身が表示されていることがお分かりいただけるかと思います。
sample_data = [48, 280, 458, 272, 4860, 478, 12, 96]
# 各要素に対して2分の1を算出
result = sample_data.each { |data|
data / 2
}
p result # [48, 280, 458, 272, 4860, 478, 12, 96]
上記の処理のeach()をmap()に変更してみると、以下のようになります。
sample_data = [48, 280, 458, 272, 4860, 478, 12, 96]
# 各要素に対して2分の1を算出
result = sample_data.map { |data|
data / 2
}
p result # [24, 140, 229, 136, 2430, 239, 6, 48]
これらを踏まえ、each()メソッドとmap()メソッドは以下の通り使い分けましょう。
- 処理結果を後続の処理で使用する場合→map()メソッド
- 処理結果を後続の処理で使用しない場合→each()メソッド
-272x300.jpg)
今回は、Ruby初心者の方々に向けて、配列(Array)を扱う際に最低限知っておくべきメソッドをご紹介させていただきます。 Webアプリケーションを開発する中で、配列は必ずと言ってよいほど使用することとなるオブジェクトになります。 […]
最後に
今回は、Rubyのmap()メソッドの使い方について解説させていただきました。
Rubyを使ったアプリケーションでは必ずと言って良いほど登場するメソッドですので、他の類似メソッドとの違いも含めてしっかりと理解しておきましょう。
最後にもう一度、本記事の内容を確認しておきましょう。
- map()メソッドの特徴
→配列やハッシュをレシーバにとる →処理結果を新たな配列に格納する - 他の類似メソッドとの違い →map()メソッドとcollect()メソッドは全く同じ →map()メソッドとmap!()メソッドは破壊的/非破壊的の違い →処理結果を後続の処理で使用するか否かでeach()メソッドと使い分ける
このブログを通じて少しでも「傍(はた)を楽(らく)にする」ことができていれば嬉しく思います。
最後まで読んで頂きありがとうございました。