map で配列を変換しよう
配列の各要素に処理を適用して、新しい配列を作る方法を学びます。
.map メソッドとは
.map メソッドは、配列の各要素に処理を適用して、新しい配列を作るためのメソッドです。
numbers = [1, 2, 3] doubled = numbers.map do |num| num * 2 end p doubled # => [2, 4, 6]
この例では、numbers 配列の各要素を2倍にした新しい配列 [2, 4, 6] が作られています。
メソッドの戻り値のレッスンで学んだように、Ruby では最後に評価された式が自動的に戻り値になります。.map のブロックでも同様で、num * 2 が各要素に対するブロックの戻り値となり、それらが集められて新しい配列になります。
ブロックの復習
.map メソッドは、each メソッドと同じようにブロックを使います。do...end や {} で囲んだ部分がブロックです。
配列.map do |変数| 処理 end
1行で書ける短い処理には、ブレース記法も使えます。
numbers = [1, 2, 3] doubled = numbers.map { |num| num * 2 } p doubled # => [2, 4, 6]
.map と .each の違い
.each と .map はどちらも配列の各要素を順番に処理しますが、戻り値が異なります。
.eachは元の配列をそのまま返す(処理の結果は返さない).mapはブロックの戻り値を集めた新しい配列を返す
次の例で違いを確認してみましょう。
numbers = [1, 2, 3] # each の場合 result_each = numbers.each { |num| num * 2 } p result_each # => [1, 2, 3](元の配列がそのまま返る) # map の場合 result_map = numbers.map { |num| num * 2 } p result_map # => [2, 4, 6](処理結果の新しい配列)
.each は繰り返し処理を行いたいとき、.map は配列を変換して新しい配列を作りたいときに使います。
.map の活用例
.map は配列の変換に幅広く使えます。
文字列の配列を変換する
words = ["hello", "world"] upper_words = words.map { |word| word.upcase } p upper_words # => ["HELLO", "WORLD"]
数値を文字列に変換する
numbers = [1, 2, 3] strings = numbers.map { |num| num.to_s } p strings # => ["1", "2", "3"]
破壊的メソッド .map!
.map は元の配列を変更せず、新しい配列を返します。一方、.map! は元の配列自体を変更します。このように元のデータを直接変更するメソッドを破壊的メソッドと呼びます。
numbers = [1, 2, 3] # map は元の配列を変更しない doubled = numbers.map { |num| num * 2 } p doubled # => [2, 4, 6] p numbers # => [1, 2, 3](元のまま) # map! は元の配列を変更する numbers.map! { |num| num * 2 } p numbers # => [2, 4, 6](変更された)
Ruby では、破壊的メソッドの末尾に ! をつける慣習があります。ただし、すべての破壊的メソッドに ! がついているわけではないため、使う前にドキュメントで確認するとよいでしょう。
シンボルを使った簡潔な書き方
ブロック内で各要素に対して引数なしのメソッドを1つだけ呼び出す場合、&:メソッド名 という省略記法が使えます。
words = ["hello", "world"] # 通常の書き方 upper_words = words.map { |word| word.upcase } p upper_words # => ["HELLO", "WORLD"] # 省略記法 upper_words = words.map(&:upcase) p upper_words # => ["HELLO", "WORLD"]
どちらも同じ結果になりますが、&:upcase の方が簡潔に書けます。
さまざまなメソッドで使う
この省略記法はさまざまなメソッドで使えます。
# 数値を文字列に変換 numbers = [1, 2, 3] strings = numbers.map(&:to_s) p strings # => ["1", "2", "3"] # 前後の空白を除去 words = [" hello ", " world "] trimmed = words.map(&:strip) p trimmed # => ["hello", "world"]
&:メソッド名 は .map 以外のメソッド(.each、.select など)でも使えます。
まとめ
.mapメソッドは各要素に処理を適用して新しい配列を作る.eachは元の配列を返すが、.mapは変換結果の新しい配列を返す.map!は元の配列自体を変更する破壊的メソッド&:メソッド名で引数なしメソッドの呼び出しを簡潔に書ける
演習に挑戦