Ruby 構文チートシート:ブロック・イテレータ・Enumerable 早見表
Ruby のコードを書いていると、配列やハッシュの操作で「あれ、これってどう書くんだっけ?」と手が止まることはありませんか。ブロック、イテレータ、Enumerable モジュールは Ruby の魅力的な機能ですが、種類が豊富で使い分けに迷うこともあるでしょう。
本記事では、Ruby のブロック構文の基礎から Enumerable モジュールの実践的なメソッドまで、すぐに使えるチートシート形式でご紹介します。初心者の方にもわかりやすく、実務でよく使うパターンを中心に解説していきますね。
クイックリファレンス早見表
ブロック構文早見表
| # | 記法 | 使用例 | 用途 | 特徴 |
|---|---|---|---|---|
| 1 | {...} | array.map { |n| n * 2 } | 単一行の処理 | 優先順位が高い、メソッドチェーン向き |
| 2 | do...end | array.each do |n|<br/> puts n<br/>end | 複数行の処理 | 優先順位が低い、可読性重視 |
イテレータメソッド早見表
| # | カテゴリ | メソッド | 戻り値 | 用途 | コード例 |
|---|---|---|---|---|---|
| 1 | 基本反復 | each | 元の配列 | 副作用目的の繰り返し | [1,2,3].each { |n| puts n } |
| 2 | 基本反復 | each_with_index | 元の配列 | インデックス付き繰り返し | ["a","b"].each_with_index { |v,i| puts i } |
| 3 | 変換 | map / collect | 新しい配列 | 要素の変換 | [1,2,3].map { |n| n * 2 } |
| 4 | 変換 | flat_map | 平坦化した配列 | 変換+平坦化 | [[1,2],[3]].flat_map { |a| a } |
| 5 | 選択 | select / filter | 新しい配列 | 条件に合う要素を抽出 | [1,2,3,4].select { |n| n.even? } |
| 6 | 選択 | reject | 新しい配列 | 条件に合わない要素を抽出 | [1,2,3,4].reject { |n| n.even? } |
| 7 | 選択 | find / detect | 最初の要素 | 条件に合う最初の 1 件 | [1,2,3,4].find { |n| n > 2 } |
| 8 | 検査 | all? | 真偽値 | 全要素が条件を満たすか | [2,4,6].all? { |n| n.even? } |
| 9 | 検査 | any? | 真偽値 | 1 つでも条件を満たすか | [1,3,4].any? { |n| n.even? } |
| 10 | 検査 | none? | 真偽値 | 全要素が条件を満たさないか | [1,3,5].none? { |n| n.even? } |
| 11 | 集約 | reduce / inject | 単一の値 | 累積処理で値を集約 | [1,2,3].reduce(0) { |sum,n| sum+n } |
| 12 | 集約 | sum | 数値 | 合計値を計算 | [1,2,3].sum |
| 13 | 集約 | count | 整数 | 条件に合う要素数 | [1,2,3,4].count { |n| n.even? } |
Enumerable メソッド早見表
| # | カテゴリ | メソッド | 戻り値 | 用途 | コード例 |
|---|---|---|---|---|---|
| 1 | グループ化 | group_by | ハッシュ | 条件でグループ化 | [1,2,3].group_by { |n| n.even? } |
| 2 | グループ化 | partition | 2 つの配列 | 条件で 2 分割 | [1,2,3,4].partition { |n| n.even? } |
| 3 | ソート | sort | ソート済配列 | 昇順ソート | [3,1,2].sort |
| 4 | ソート | sort_by | ソート済配列 | 条件でソート | users.sort_by { |u| u[:age] } |
| 5 | 取得 | first(n) | 配列 | 最初の n 件 | [1,2,3,4,5].first(3) |
| 6 | 取得 | last(n) | 配列 | 最後の n 件 | [1,2,3,4,5].last(2) |
| 7 | 取得 | take(n) | 配列 | 最初の n 件 | [1,2,3,4,5].take(3) |
| 8 | 取得 | drop(n) | 配列 | 最初の n 件を除外 | [1,2,3,4,5].drop(2) |
| 9 | 取得 | take_while | 配列 | 条件が真の間取得 | [1,2,3,4].take_while { |n| n<3 } |
| 10 | 取得 | drop_while | 配列 | 条件が真の間除外 | [1,2,3,4].drop_while { |n| n<3 } |
| 11 | ユニーク | uniq | 配列 | 重複を除去 | [1,2,2,3].uniq |
| 12 | 組み合わせ | zip | 配列 | 複数配列を結合 | [1,2].zip(["a","b"]) |
| 13 | 組み合わせ | flatten | 配列 | ネストを平坦化 | [[1,2],[3]].flatten |
| 14 | クリーニング | compact | 配列 | nil を除去 | [1,nil,2,nil].compact |
| 15 | 最大最小 | max / min | 単一の値 | 最大値・最小値 | [1,5,3].max |
| 16 | 最大最小 | max_by / min_by | 単一の値 | 条件で最大・最小 | users.max_by { |u| u[:age] } |
破壊的メソッド vs 非破壊的メソッド
| # | 非破壊的メソッド | 破壊的メソッド | 違い | 使い分け |
|---|---|---|---|---|
| 1 | map | map! | 元の配列を変更しない / する | 元データ保持が必要 / メモリ効率優先 |
| 2 | select | select! | 元の配列を変更しない / する | 元データ保持が必要 / メモリ効率優先 |
| 3 | reject | reject! | 元の配列を変更しない / する | 元データ保持が必要 / メモリ効率優先 |
| 4 | sort | sort! | 元の配列を変更しない / する | 元データ保持が必要 / メモリ効率優先 |
| 5 | reverse | reverse! | 元の配列を変更しない / する | 元データ保持が必要 / メモリ効率優先 |
| 6 | uniq | uniq! | 元の配列を変更しない / する | 元データ保持が必要 / メモリ効率優先 |
よく使うパターン一覧
| # | パターン | コード例 | 説明 |
|---|---|---|---|
| 1 | 配列の各要素を 2 倍 | [1,2,3].map { |n| n * 2 } | 変換処理の基本 |
| 2 | 偶数のみ抽出 | [1,2,3,4].select { |n| n.even? } | フィルタリングの基本 |
| 3 | 合計値を計算 | [1,2,3,4,5].sum | 集計処理の基本 |
| 4 | 条件に合う最初の要素 | [1,2,3,4].find { |n| n > 2 } | 検索処理の基本 |
| 5 | 全要素が条件を満たすか | [2,4,6].all? { |n| n.even? } | 検証処理の基本 |
| 6 | メソッドチェーン | array.select{}.map{}.sum | 複数処理の連結 |
| 7 | ハッシュの各要素に処理 | hash.each { |k,v| puts "#{k}: #{v}" } | ハッシュの反復 |
| 8 | グループ化 | array.group_by { |n| n.even? } | データの分類 |
| 9 | ソート | users.sort_by { |u| u[:age] } | オブジェクトの並べ替え |
| 10 | 重複除去 | [1,2,2,3,3].uniq | ユニーク化 |
背景
Ruby におけるブロックの重要性
Ruby は「プログラマの幸せ」を追求した言語として設計されており、ブロックはその哲学を体現する重要な機能です。ブロックを使うことで、繰り返し処理やコレクション操作を直感的かつ簡潔に記述できるようになります。
他の言語では複数行必要な処理も、Ruby ではブロックを使えば 1 行で表現できることが多いのです。
ブロック・イテレータ・Enumerable の関係性
これら 3 つの概念は密接に関連しています。以下の図で、その関係性を確認してみましょう。
mermaidflowchart TB
block["ブロック<br/>{...} または do...end"]
iterator["イテレータメソッド<br/>each, map, select など"]
enumerable["Enumerable モジュール<br/>豊富なメソッド群"]
block -->|渡される| iterator
iterator -->|活用する| enumerable
enumerable -->|提供する| iterator
図の要点:
- ブロックはイテレータメソッドに渡されて実行されます
- Enumerable モジュールが豊富なイテレータメソッドを提供します
- イテレータメソッドがブロックを活用して処理を実行します
Ruby のコレクション処理の強み
Ruby では Array、Hash、Range などのコレクションクラスが Enumerable モジュールをインクルードしています。これにより、どのコレクションでも共通のメソッド群を使えるのが大きな利点です。
| # | コレクション | 説明 | Enumerable の利用 |
|---|---|---|---|
| 1 | Array | 配列(順序付きコレクション) | ★★★ |
| 2 | Hash | ハッシュ(キーと値のペア) | ★★★ |
| 3 | Range | 範囲オブジェクト | ★★☆ |
| 4 | Set | 集合(重複なし) | ★★☆ |
課題
よくある困りごと
Ruby 初心者が直面する典型的な課題をまとめてみました。
1. ブロック構文の使い分けが難しい
{...} と do...end の 2 つの書き方があり、いつどちらを使うべきか迷うことがあります。優先順位の違いを理解していないと、思わぬバグの原因になることもあるでしょう。
2. メソッドの選択肢が多すぎる
Enumerable モジュールには 50 種類以上のメソッドがあります。map と collect、select と filter など、同じ動作をする別名メソッドも存在するため、どれを使えばよいか判断に困ることもあるのではないでしょうか。
3. 破壊的メソッドと非破壊的メソッドの混乱
map と map!、select と select! のように、末尾に ! が付くメソッドは元のオブジェクトを変更します。この違いを理解していないと、データが意図せず書き換わってしまうリスクがあります。
課題の発生パターン
以下の図は、Ruby のコレクション処理でよく発生する課題のパターンを示しています。
mermaidflowchart TD
start["配列・ハッシュの処理"] --> q1{ブロック構文<br/>選択}
q1 -->|わからない| error1["構文エラー<br/>優先順位の問題"]
q1 -->|OK| q2{適切なメソッド<br/>選択}
q2 -->|わからない| error2["非効率な実装<br/>冗長なコード"]
q2 -->|OK| q3{破壊的/非破壊的<br/>判断}
q3 -->|間違い| error3["意図しない<br/>データ変更"]
q3 -->|OK| success["正しい実装"]
図で理解できる要点:
- ブロック構文の選択ミスは構文エラーにつながります
- メソッド選択の迷いは非効率なコード実装の原因です
- 破壊的メソッドの誤用はデータ破損のリスクを生みます
解決策
ブロック構文の使い分けルール
Ruby のブロックには 2 つの記法がありますが、以下の明確なルールに従えば迷うことはありません。
| # | 記法 | 使用場面 | 優先順位 |
|---|---|---|---|
| 1 | {...} | 1 行で書ける短い処理 | 高い |
| 2 | do...end | 複数行にわたる処理 | 低い |
単一行ブロックの記法
1 行で完結する処理には波括弧 {} を使用します。
ruby# 配列の各要素を 2 倍にする
numbers = [1, 2, 3, 4, 5]
doubled = numbers.map { |n| n * 2 }
# => [2, 4, 6, 8, 10]
このコードは配列の各要素に対してブロック内の処理を実行し、新しい配列を返します。|n| はブロックパラメータで、配列の各要素が順番に渡されます。
ruby# 偶数のみを抽出
evens = numbers.select { |n| n.even? }
# => [2, 4]
select メソッドはブロックが真を返す要素だけを抽出します。even? は Ruby の整数メソッドで、偶数なら true を返します。
複数行ブロックの記法
複数行にわたる処理には do...end を使用します。
ruby# 配列の各要素に対して複雑な処理を実行
users = ["alice", "bob", "charlie"]
formatted_users = users.map do |user|
capitalized = user.capitalize # 先頭を大文字化
"User: #{capitalized}" # フォーマット
end
# => ["User: Alice", "User: Bob", "User: Charlie"]
このコードでは、各ユーザー名に対して 2 つの処理(大文字化とフォーマット)を実行しています。複数行の処理が見やすく整理されているのがわかりますね。
メソッドチェーンでの注意点
メソッドチェーンを使う場合は、優先順位に注意が必要です。
ruby# 正しい例:波括弧は優先順位が高い
result = [1, 2, 3].map { |n| n * 2 }.sum
# => 12
このコードは期待通りに動作します。map の結果に対して sum が実行されます。
ruby# 注意が必要な例:do...end は優先順位が低い
# この書き方は意図しない結果になることがある
result = [1, 2, 3].map do |n| n * 2 end.sum
# do...end のブロックが map に渡される前に
# 構文の解釈が複雑になる可能性があります
メソッドチェーンでは波括弧 {} の使用をおすすめします。
ブロック引数のパターン
ブロックには複数の引数を渡すこともできます。特にハッシュの処理では頻繁に使用されます。
ruby# ハッシュの各キーと値に対する処理
scores = { alice: 85, bob: 92, charlie: 78 }
scores.each { |name, score| puts "#{name}: #{score}点" }
# 出力:
# alice: 85点
# bob: 92点
# charlie: 78点
ハッシュに対して each を使うと、ブロックにはキーと値の 2 つの引数が渡されます。このパターンは非常によく使われるので覚えておくと便利です。
主要なイテレータメソッド一覧
Ruby でよく使用されるイテレータメソッドを機能別に整理しました。
基本的な反復処理
ruby# each - 各要素に対して処理を実行(戻り値は元の配列)
[1, 2, 3].each { |n| puts n }
# 出力: 1, 2, 3
# 戻り値: [1, 2, 3]
each は最も基本的なイテレータです。副作用(画面出力など)が目的の場合に使用します。
ruby# each_with_index - インデックス付きで反復
["a", "b", "c"].each_with_index { |item, i| puts "#{i}: #{item}" }
# 出力:
# 0: a
# 1: b
# 2: c
配列の要素とそのインデックスの両方が必要な場合に便利です。
変換系メソッド
ruby# map(別名:collect)- 各要素を変換して新しい配列を生成
numbers = [1, 2, 3, 4, 5]
squares = numbers.map { |n| n ** 2 }
# => [1, 4, 9, 16, 25]
map は最も頻繁に使用される変換メソッドです。元の配列は変更せず、新しい配列を返します。
ruby# map! - 破壊的に配列を変換(元の配列を変更)
numbers = [1, 2, 3]
numbers.map! { |n| n * 10 }
# numbers は [10, 20, 30] に変更される
末尾に ! が付くメソッドは元のオブジェクトを変更します。メモリ効率は良いですが、元のデータが必要な場合は注意が必要です。
選択系メソッド
ruby# select(別名:filter)- 条件に合う要素のみを抽出
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = numbers.select { |n| n.even? }
# => [2, 4, 6, 8, 10]
select は条件にマッチする要素だけを含む新しい配列を返します。フィルタリング処理の定番メソッドですね。
ruby# reject - 条件に合わない要素を抽出(select の逆)
odds = numbers.reject { |n| n.even? }
# => [1, 3, 5, 7, 9]
reject は select の逆で、条件が偽の要素を抽出します。
ruby# find(別名:detect)- 条件に合う最初の要素を返す
first_even = numbers.find { |n| n.even? }
# => 2
find は条件にマッチする最初の要素のみを返します。全要素を処理しないため、効率的です。
検査系メソッド
ruby# all? - 全要素が条件を満たすか判定
numbers = [2, 4, 6, 8]
all_even = numbers.all? { |n| n.even? }
# => true
全ての要素が条件を満たす場合のみ true を返します。
ruby# any? - 少なくとも 1 つの要素が条件を満たすか判定
numbers = [1, 3, 4, 5]
has_even = numbers.any? { |n| n.even? }
# => true(4 が偶数)
1 つでも条件を満たす要素があれば true を返します。
ruby# none? - 全要素が条件を満たさないか判定
numbers = [1, 3, 5, 7]
no_even = numbers.none? { |n| n.even? }
# => true
どの要素も条件を満たさない場合に true を返します。
集約系メソッド
ruby# reduce(別名:inject)- 要素を順次処理して 1 つの値に集約
numbers = [1, 2, 3, 4, 5]
sum = numbers.reduce(0) { |accumulator, n| accumulator + n }
# => 15
reduce は累積値を持ちながら各要素を処理します。0 は初期値で、accumulator には前回の結果が渡されます。
ruby# reduce の省略記法(シンボルを使用)
sum = numbers.reduce(:+)
# => 15
product = numbers.reduce(:*)
# => 120(1 * 2 * 3 * 4 * 5)
シンプルな演算の場合は、シンボルを使った省略記法が使えます。コードがすっきりして読みやすくなりますね。
ruby# sum - 数値の合計(Ruby 2.4 以降)
total = numbers.sum
# => 15
# 初期値を指定することも可能
total_with_base = numbers.sum(100)
# => 115
単純な合計には専用の sum メソッドを使う方が直感的です。
Enumerable モジュールの実践的メソッド
ここからは、実務でよく使う Enumerable モジュールの便利なメソッドをご紹介します。
グループ化・分類
ruby# group_by - 条件ごとにグループ化
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
grouped = numbers.group_by { |n| n.even? ? "偶数" : "奇数" }
# => {"奇数"=>[1, 3, 5, 7, 9], "偶数"=>[2, 4, 6, 8, 10]}
group_by はブロックの戻り値をキーとして、要素をグループ化したハッシュを返します。データの分類に非常に便利です。
ruby# partition - 条件で 2 つのグループに分割
evens, odds = numbers.partition { |n| n.even? }
# evens => [2, 4, 6, 8, 10]
# odds => [1, 3, 5, 7, 9]
partition は条件が真の要素と偽の要素の 2 つの配列を返します。多重代入で受け取ると便利ですね。
ソート・順序操作
ruby# sort_by - 指定した基準でソート
users = [
{ name: "Alice", age: 30 },
{ name: "Bob", age: 25 },
{ name: "Charlie", age: 35 }
]
sorted_by_age = users.sort_by { |user| user[:age] }
# => [
# {:name=>"Bob", :age=>25},
# {:name=>"Alice", :age=>30},
# {:name=>"Charlie", :age=>35}
# ]
sort_by は複雑なオブジェクトのソートに最適です。ブロックで返した値を基準にソートされます。
ruby# reverse_each - 逆順で反復処理
[1, 2, 3].reverse_each { |n| puts n }
# 出力: 3, 2, 1
配列を逆順に処理したい場合に使用します。
要素の取得・抽出
ruby# first - 最初の n 要素を取得
numbers = [1, 2, 3, 4, 5]
first_three = numbers.first(3)
# => [1, 2, 3]
配列の先頭から指定した数の要素を取得します。
ruby# last - 最後の n 要素を取得
last_two = numbers.last(2)
# => [4, 5]
配列の末尾から指定した数の要素を取得します。
ruby# take - 最初の n 要素を取得(first と同じ)
taken = numbers.take(3)
# => [1, 2, 3]
take は first と同じ動作ですが、Enumerable の標準メソッドとして統一的に使えます。
ruby# drop - 最初の n 要素を除外
dropped = numbers.drop(2)
# => [3, 4, 5]
drop は先頭から指定した数の要素を除外した配列を返します。
ruby# take_while - 条件を満たす間は要素を取得
taken_while = numbers.take_while { |n| n < 4 }
# => [1, 2, 3]
条件が偽になるまでの要素を取得します。
ruby# drop_while - 条件を満たす間は要素を除外
dropped_while = numbers.drop_while { |n| n < 4 }
# => [4, 5]
条件が偽になるまでの要素を除外します。
ユニーク・重複処理
ruby# uniq - 重複を除去
numbers = [1, 2, 2, 3, 3, 3, 4, 5, 5]
unique = numbers.uniq
# => [1, 2, 3, 4, 5]
重複する要素を削除して、ユニークな値のみの配列を返します。
ruby# uniq (ブロック付き) - 条件でユニーク判定
words = ["apple", "apricot", "banana", "blueberry", "cherry"]
unique_by_first = words.uniq { |w| w[0] }
# => ["apple", "banana", "cherry"]
ブロックで返した値を基準にユニーク判定を行います。この例では頭文字が同じ単語は 1 つだけ残ります。
組み合わせ・変換
ruby# zip - 複数の配列を組み合わせる
names = ["Alice", "Bob", "Charlie"]
ages = [30, 25, 35]
combined = names.zip(ages)
# => [["Alice", 30], ["Bob", 25], ["Charlie", 35]]
複数の配列を要素ごとにペアにして、新しい配列を作成します。データの結合に便利です。
ruby# flatten - ネストした配列を平坦化
nested = [[1, 2], [3, 4], [5, 6]]
flattened = nested.flatten
# => [1, 2, 3, 4, 5, 6]
多次元配列を 1 次元配列に変換します。
ruby# compact - nil を除去
array_with_nil = [1, nil, 2, nil, 3]
without_nil = array_with_nil.compact
# => [1, 2, 3]
配列から nil 要素のみを削除します。データのクリーニングによく使われます。
メソッドチェーンの実践パターン
Ruby の真価は、複数のメソッドを連鎖させて複雑な処理を簡潔に記述できることです。以下の図で処理の流れを見てみましょう。
mermaidflowchart LR
data["元データ<br/>[1,2,3,4,5,6]"] --> filter["select<br/>偶数のみ"]
filter --> transform["map<br/>2乗"]
transform --> aggregate["sum<br/>合計"]
aggregate --> result["結果<br/>56"]
処理フローの要点:
- 各メソッドが前のメソッドの結果を受け取ります
- データが段階的に変換・絞り込まれていきます
- 最終的に 1 つの結果値が得られます
実践的なチェーン例
ruby# 複雑な処理を 1 つのチェーンで表現
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = numbers
.select { |n| n.even? } # 偶数のみ抽出: [2, 4, 6, 8, 10]
.map { |n| n ** 2 } # 2乗: [4, 16, 36, 64, 100]
.sum # 合計: 220
# => 220
このコードは、偶数の抽出、2 乗への変換、合計の計算を 1 つの流れで表現しています。各ステップが明確で読みやすいですね。
ruby# ハッシュを使った実践例
users = [
{ name: "Alice", age: 30, active: true },
{ name: "Bob", age: 25, active: false },
{ name: "Charlie", age: 35, active: true },
{ name: "David", age: 28, active: true }
]
active_user_names = users
.select { |user| user[:active] } # アクティブユーザーのみ
.sort_by { |user| user[:age] } # 年齢順にソート
.map { |user| user[:name] } # 名前のみ抽出
.join(", ") # カンマ区切りで結合
# => "David, Alice, Charlie"
このパターンは実務で非常によく使います。データベースから取得したレコードの加工などに最適です。
破壊的メソッドと非破壊的メソッド
Ruby では、同じ処理でも元のオブジェクトを変更するかどうかで 2 種類のメソッドが用意されています。
| # | 種類 | 特徴 | メソッド例 | 使い分け |
|---|---|---|---|---|
| 1 | 非破壊的 | 元のオブジェクトは変更せず、新しいオブジェクトを返す | map, select, sort | 元データを保持したい場合 |
| 2 | 破壊的 | 元のオブジェクトを直接変更する(末尾に !) | map!, select!, sort! | メモリ効率を優先する場合 |
非破壊的メソッドの例
ruby# 元の配列は変更されない
original = [3, 1, 4, 1, 5, 9]
sorted = original.sort
# sorted => [1, 1, 3, 4, 5, 9]
# original => [3, 1, 4, 1, 5, 9](変更なし)
非破壊的メソッドは安全性が高く、予期しないデータ変更を防げます。
破壊的メソッドの例
ruby# 元の配列が変更される
original = [3, 1, 4, 1, 5, 9]
original.sort!
# original => [1, 1, 3, 4, 5, 9](変更された)
破壊的メソッドはメモリ効率が良いですが、元のデータが失われる点に注意が必要です。
ruby# 破壊的メソッドは nil を返すことがある
numbers = [1, 2, 3]
result = numbers.select! { |n| n > 5 }
# result => nil(条件に合う要素がない場合)
# numbers => [1, 2, 3](変更なし)
破壊的メソッドは、変更が発生しなかった場合に nil を返すことがあります。この挙動を理解していないとバグの原因になるので注意しましょう。
具体例
実務でよく使うパターン集
ここでは、実際の開発現場でよく登場するユースケースを具体的なコード例とともにご紹介します。
パターン 1: データの集計・統計処理
ユーザーの購入履歴から統計情報を取得する例です。
ruby# 購入履歴データ
purchases = [
{ user: "Alice", amount: 1500, category: "book" },
{ user: "Bob", amount: 3000, category: "electronics" },
{ user: "Alice", amount: 800, category: "book" },
{ user: "Charlie", amount: 5000, category: "electronics" },
{ user: "Bob", amount: 1200, category: "food" }
]
このようなデータ構造から、様々な統計情報を抽出していきます。
ruby# カテゴリー別の合計金額を算出
category_totals = purchases
.group_by { |p| p[:category] }
.transform_values { |items| items.sum { |item| item[:amount] } }
# => {
# "book"=>2300,
# "electronics"=>8000,
# "food"=>1200
# }
group_by でカテゴリーごとにグループ化し、transform_values で各グループの合計を計算しています。transform_values は Ruby 2.4 以降で使えるハッシュの便利なメソッドです。
ruby# ユーザーごとの購入回数と合計金額
user_stats = purchases
.group_by { |p| p[:user] }
.map { |user, items|
{
user: user,
count: items.size,
total: items.sum { |item| item[:amount] }
}
}
# => [
# {:user=>"Alice", :count=>2, :total=>2300},
# {:user=>"Bob", :count=>2, :total=>4200},
# {:user=>"Charlie", :count=>1, :total=>5000}
# ]
ユーザーごとの統計情報を計算し、新しいハッシュの配列として整形しています。
パターン 2: データのフィルタリングと変換
条件に応じたデータの抽出と加工を行う例です。
ruby# 商品データ
products = [
{ name: "ノートPC", price: 120000, stock: 5, category: "electronics" },
{ name: "マウス", price: 3000, stock: 0, category: "electronics" },
{ name: "Ruby入門", price: 2800, stock: 10, category: "book" },
{ name: "キーボード", price: 8000, stock: 3, category: "electronics" },
{ name: "Python実践", price: 3200, stock: 0, category: "book" }
]
在庫がある商品だけを抽出し、表示用にフォーマットします。
ruby# 在庫あり商品をカテゴリー別に整理
available_products = products
.select { |p| p[:stock] > 0 } # 在庫あり
.sort_by { |p| [p[:category], p[:price]] } # カテゴリーと価格でソート
.map { |p| "#{p[:name]}(#{p[:price]}円)" } # フォーマット
# => [
# "Ruby入門(2800円)",
# "キーボード(8000円)",
# "ノートPC(120000円)"
# ]
複数の条件を組み合わせた実践的な処理です。sort_by に配列を渡すことで、複数の基準でソートできます。
パターン 3: ネストしたデータの処理
階層構造を持つデータの扱い方です。
ruby# 部門とメンバーのデータ
departments = [
{
name: "開発部",
members: [
{ name: "Alice", role: "engineer" },
{ name: "Bob", role: "designer" }
]
},
{
name: "営業部",
members: [
{ name: "Charlie", role: "sales" },
{ name: "David", role: "sales" }
]
}
]
ネストした構造から特定のロールのメンバーを抽出します。
ruby# 全エンジニアのリストを取得
engineers = departments
.flat_map { |dept| dept[:members] } # 全メンバーを平坦化
.select { |member| member[:role] == "engineer" } # エンジニアのみ
.map { |member| member[:name] } # 名前のみ
# => ["Alice"]
flat_map は map と flatten を組み合わせたメソッドで、ネストしたデータの処理に便利です。
ruby# 部門ごとのメンバー数を集計
member_counts = departments.map { |dept|
{ department: dept[:name], count: dept[:members].size }
}
# => [
# {:department=>"開発部", :count=>2},
# {:department=>"営業部", :count=>2}
# ]
各部門のメンバー数を計算し、新しいハッシュの配列を生成しています。
パターン 4: 条件分岐とデフォルト値
データの存在確認や条件に応じた処理を行う例です。
ruby# スコアデータ
scores = [85, 92, 78, 95, 88, 72, 90]
様々な条件チェックを実施します。
ruby# 80点以上の人数をカウント
pass_count = scores.count { |score| score >= 80 }
# => 5
count メソッドは条件にマッチする要素の数を返します。
ruby# 最高点と最低点を取得
highest = scores.max
# => 95
lowest = scores.min
# => 72
max と min は配列の最大値と最小値を簡単に取得できます。
ruby# 90点以上の人がいるか確認
has_excellent = scores.any? { |score| score >= 90 }
# => true
any? は条件を満たす要素が 1 つでもあれば true を返します。
ruby# 全員が60点以上か確認
all_passed = scores.all? { |score| score >= 60 }
# => true
all? は全ての要素が条件を満たす場合のみ true を返します。
ruby# 特定の条件を満たす最初の要素を取得(なければデフォルト値)
first_perfect = scores.find { |score| score == 100 } || "満点なし"
# => "満点なし"
find が nil を返した場合、|| 演算子でデフォルト値を設定できます。
パターン 5: ハッシュの高度な操作
ハッシュを使った実践的なデータ処理です。
ruby# 設定データのマージと変換
default_config = {
timeout: 30,
retry: 3,
verbose: false
}
user_config = {
timeout: 60,
debug: true
}
複数の設定をマージして、最終的な設定を作成します。
ruby# 設定のマージ
merged_config = default_config.merge(user_config)
# => {:timeout=>60, :retry=>3, :verbose=>false, :debug=>true}
merge メソッドは、右側のハッシュの値で左側のハッシュを上書きします。
ruby# 特定のキーのみを抽出
essential_config = merged_config.select { |key, value|
[:timeout, :retry].include?(key)
}
# => {:timeout=>60, :retry=>3}
必要なキーだけを抽出して新しいハッシュを作成します。
ruby# 値の変換
formatted_config = merged_config.transform_values { |value|
value.is_a?(Integer) ? "#{value}秒" : value.to_s
}
# => {:timeout=>"60秒", :retry=>"3秒", :verbose=>"false", :debug=>"true"}
transform_values で全ての値を変換できます。キーは維持されます。
パフォーマンスの考慮事項
処理の効率を意識した実装パターンをご紹介します。
| # | 場面 | 非効率な方法 | 効率的な方法 | 理由 |
|---|---|---|---|---|
| 1 | 最初の 1 件のみ必要 | select で全件検索 | find で最初の 1 件 | 条件マッチで即座に終了 |
| 2 | 真偽判定のみ | select + empty? | any? や all? | 専用メソッドで効率化 |
| 3 | 大量データの変換 | 非破壊的メソッド | 破壊的メソッド | メモリ使用量を削減 |
| 4 | 重複チェック | uniq で重複削除 | Set を使用 | 重複チェックが高速 |
効率的なコード例
ruby# 非効率: 全件検索してから判定
result = numbers.select { |n| n > 100 }.any?
この方法では全要素を検索してから any? を呼び出すため、無駄が多いです。
ruby# 効率的: 条件マッチ時点で終了
result = numbers.any? { |n| n > 100 }
any? を直接使えば、条件にマッチした時点で処理が終了します。
ruby# 大量データの処理では破壊的メソッドも検討
large_array = (1..1_000_000).to_a
large_array.map! { |n| n * 2 } # 元の配列を再利用
メモリ使用量を抑えたい場合は、破壊的メソッドの使用も検討しましょう。ただし、元のデータが不要であることを確認してから使用してください。
エラー処理のベストプラクティス
実務では、データの妥当性チェックやエラー処理も重要です。
ruby# データのバリデーション
data = [1, 2, "three", 4, nil, 6]
# 数値のみを抽出して処理
safe_result = data
.compact # nil を除去
.select { |item| item.is_a?(Numeric) } # 数値のみ
.map { |n| n * 2 }
# => [2, 4, 8, 12]
このように、段階的にデータをクリーニングしていけば、エラーを防げます。
ruby# 空配列への対応
empty_array = []
# 安全な集計(デフォルト値を設定)
total = empty_array.sum(0)
# => 0(エラーにならない)
average = empty_array.empty? ? 0 : empty_array.sum / empty_array.size.to_f
# => 0(ゼロ除算を回避)
空配列に対する処理も、適切にハンドリングすることが大切ですね。
処理フローの可視化
複雑な処理を実装する際の思考プロセスを図で表現してみましょう。
mermaidstateDiagram-v2
[*] --> DataInput: データ入力
DataInput --> Validation: バリデーション
Validation --> Filtering: フィルタリング
Filtering --> Transformation: 変換処理
Transformation --> Aggregation: 集約処理
Aggregation --> Output: 結果出力
Output --> [*]
Validation --> ErrorHandle: エラー検出
ErrorHandle --> [*]: エラー終了
処理ステップの要点:
- データは段階的に加工されていきます
- 各ステップで適切なメソッドを選択します
- バリデーションでエラーを早期に検出します
まとめ
本記事では、Ruby のブロック、イテレータ、Enumerable モジュールについて、基本から実践まで幅広くご紹介しました。
重要なポイントを振り返ってみましょう:
- ブロック構文は 1 行なら
{}、複数行ならdo...endを使い分ける - イテレータメソッドは目的に応じて適切なものを選択する(変換なら
map、選択ならselectなど) - Enumerable モジュールの豊富なメソッドを活用すれば、複雑な処理も簡潔に書ける
- メソッドチェーンで処理を連結すれば、読みやすく効率的なコードになる
- 破壊的メソッドは効率的だが、元データの保持が必要な場合は非破壊的メソッドを使う
Ruby のコレクション処理は非常に表現力が高く、慣れればコードが驚くほど短く読みやすくなります。最初は基本的な each、map、select から始めて、徐々に他のメソッドも使いこなせるようになっていきましょう。
実際のコーディングでは、この記事をチートシートとして活用していただければ幸いです。何度も使ううちに、自然と適切なメソッドを選べるようになりますよ。
ぜひ、実際のプロジェクトでこれらのメソッドを試してみてください。Ruby のコードがより洗練され、開発効率も向上するはずです。
関連リンク
articleRuby 構文チートシート:ブロック・イテレータ・Enumerable 早見表
articleRuby とは?2025 年版の特徴・強み・最新エコシステムを徹底解説
article「Windows」に「Ruby」をインストールしてみました。その時のやり方や環境変数などいろいろ
articleWebRTC が「connecting」のまま進まない:ICE 失敗を 5 分で切り分ける手順
articleWeb Components が “is not a constructor” で落ちる時:定義順序と複数登録の衝突を解決
articleVitest モジュールモック技術の基礎と応用:`vi.mock` / `vi.spyOn` を極める
articleVue.js リアクティビティ内部解剖:Proxy/ref/computed を図で読み解く
articleVite CSS HMR が反映されない時のチェックリスト:PostCSS/Modules/Cache 編
articleTailwind CSS 2025 年ロードマップ総ざらい:新機能・互換性・移行の見取り図
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来