T-CREATOR

Ruby 構文チートシート:ブロック・イテレータ・Enumerable 早見表

 Ruby 構文チートシート:ブロック・イテレータ・Enumerable 早見表

Ruby のコードを書いていると、配列やハッシュの操作で「あれ、これってどう書くんだっけ?」と手が止まることはありませんか。ブロック、イテレータ、Enumerable モジュールは Ruby の魅力的な機能ですが、種類が豊富で使い分けに迷うこともあるでしょう。

本記事では、Ruby のブロック構文の基礎から Enumerable モジュールの実践的なメソッドまで、すぐに使えるチートシート形式でご紹介します。初心者の方にもわかりやすく、実務でよく使うパターンを中心に解説していきますね。

クイックリファレンス早見表

ブロック構文早見表

#記法使用例用途特徴
1{...}array.map { |n| n * 2 }単一行の処理優先順位が高い、メソッドチェーン向き
2do...endarray.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グループ化partition2 つの配列条件で 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 非破壊的メソッド

#非破壊的メソッド破壊的メソッド違い使い分け
1mapmap!元の配列を変更しない / する元データ保持が必要 / メモリ効率優先
2selectselect!元の配列を変更しない / する元データ保持が必要 / メモリ効率優先
3rejectreject!元の配列を変更しない / する元データ保持が必要 / メモリ効率優先
4sortsort!元の配列を変更しない / する元データ保持が必要 / メモリ効率優先
5reversereverse!元の配列を変更しない / する元データ保持が必要 / メモリ効率優先
6uniquniq!元の配列を変更しない / する元データ保持が必要 / メモリ効率優先

よく使うパターン一覧

#パターンコード例説明
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 の利用
1Array配列(順序付きコレクション)★★★
2Hashハッシュ(キーと値のペア)★★★
3Range範囲オブジェクト★★☆
4Set集合(重複なし)★★☆

課題

よくある困りごと

Ruby 初心者が直面する典型的な課題をまとめてみました。

1. ブロック構文の使い分けが難しい

{...}do...end の 2 つの書き方があり、いつどちらを使うべきか迷うことがあります。優先順位の違いを理解していないと、思わぬバグの原因になることもあるでしょう。

2. メソッドの選択肢が多すぎる

Enumerable モジュールには 50 種類以上のメソッドがあります。mapcollectselectfilter など、同じ動作をする別名メソッドも存在するため、どれを使えばよいか判断に困ることもあるのではないでしょうか。

3. 破壊的メソッドと非破壊的メソッドの混乱

mapmap!selectselect! のように、末尾に ! が付くメソッドは元のオブジェクトを変更します。この違いを理解していないと、データが意図せず書き換わってしまうリスクがあります。

課題の発生パターン

以下の図は、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 行で書ける短い処理高い
2do...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]

rejectselect の逆で、条件が偽の要素を抽出します。

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]

takefirst と同じ動作ですが、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_mapmapflatten を組み合わせたメソッドで、ネストしたデータの処理に便利です。

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

maxmin は配列の最大値と最小値を簡単に取得できます。

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 } || "満点なし"
# => "満点なし"

findnil を返した場合、|| 演算子でデフォルト値を設定できます。

パターン 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 のコレクション処理は非常に表現力が高く、慣れればコードが驚くほど短く読みやすくなります。最初は基本的な eachmapselect から始めて、徐々に他のメソッドも使いこなせるようになっていきましょう。

実際のコーディングでは、この記事をチートシートとして活用していただければ幸いです。何度も使ううちに、自然と適切なメソッドを選べるようになりますよ。

ぜひ、実際のプロジェクトでこれらのメソッドを試してみてください。Ruby のコードがより洗練され、開発効率も向上するはずです。

関連リンク