select と reject で絞り込もう

配列から条件に合う要素だけを抽出して、新しい配列を作る方法を学びます。

.select メソッドとは

.select メソッドは、配列から条件に合う要素だけを抽出して、新しい配列を作るためのメソッドです。

numbers = [1, 2, 3, 4, 5, 6] evens = numbers.select { |num| num.even? } p evens # => [2, 4, 6]

この例では、numbers 配列から偶数だけを抽出した新しい配列 [2, 4, 6] が作られています。

.even? メソッドは数値が偶数かどうかを判定し、偶数なら true、奇数なら false を返します。同様に .odd? メソッドは奇数かどうかを判定します。

.select の別名として .filter があります。どちらを使っても同じ動作をします。

numbers = [1, 2, 3, 4, 5] p numbers.select { |n| n > 2 } # => [3, 4, 5] p numbers.filter { |n| n > 2 } # => [3, 4, 5](同じ結果)

ブロックが真を返す要素を抽出する仕組み

.select はブロック内の条件を各要素に対して評価し、ブロックが真を返した要素だけを集めた新しい配列を返します

numbers = [1, 2, 3, 4, 5, 6] result = numbers.select do |num| puts "#{num} を評価中..." num > 3 end # => 1 を評価中... # => 2 を評価中... # => 3 を評価中... # => 4 を評価中... # => 5 を評価中... # => 6 を評価中... p result # => [4, 5, 6]

この例では、各要素に対して num > 3 が評価され、true になる 456 だけが結果の配列に含まれます。

.map と .select の違い

前のレッスンで学んだ .map.select は、どちらも新しい配列を返しますが、用途が異なります

  • .map は各要素を変換して新しい配列を作る
  • .select は条件に合う要素を絞り込んで新しい配列を作る
numbers = [1, 2, 3, 4, 5] # map:各要素を2倍に変換 doubled = numbers.map { |num| num * 2 } p doubled # => [2, 4, 6, 8, 10] # select:3より大きい要素を絞り込み large = numbers.select { |num| num > 3 } p large # => [4, 5]

.map は要素の数は変わらず中身が変わり、.select は中身は変わらず要素の数が変わります。

.reject メソッド

.reject メソッドは .select の逆で、条件に合わない要素だけを抽出します。

numbers = [1, 2, 3, 4, 5, 6] # select:偶数だけを抽出 evens = numbers.select { |num| num.even? } p evens # => [2, 4, 6] # reject:偶数を除外(= 奇数だけを抽出) odds = numbers.reject { |num| num.even? } p odds # => [1, 3, 5]

.select.reject は表裏一体の関係です。「〜を含める」と考えるなら .select、「〜を除外する」と考えるなら .reject を使います。

実用的な例

words = ["apple", "", "banana", "", "cherry"] # 空文字列を除外 non_empty = words.reject { |word| word.empty? } p non_empty # => ["apple", "banana", "cherry"]

.empty? メソッドは文字列や配列が空かどうかを判定し、空なら true、そうでなければ false を返します。

.find メソッド

.find メソッドは、条件に合う最初の1つの要素だけを返します。配列ではなく、単一の要素を返す点が .select との違いです。

numbers = [1, 2, 3, 4, 5, 6] # 最初の偶数を取得 first_even = numbers.find { |num| num.even? } p first_even # => 2

.select は条件に合うすべての要素を配列で返しますが、.find は最初に見つかった1つだけを返します。

numbers = [1, 2, 3, 4, 5, 6] # select:条件に合うすべての要素(配列) all_evens = numbers.select { |num| num.even? } p all_evens # => [2, 4, 6] # find:条件に合う最初の要素(単一の値) first_even = numbers.find { |num| num.even? } p first_even # => 2

条件に合う要素が見つからない場合、.findnil を返します。

numbers = [1, 3, 5, 7] result = numbers.find { |num| num.even? } p result # => nil

.find の別名として .detect があります。どちらを使っても同じ動作をします。

numbers = [1, 2, 3, 4, 5] p numbers.find { |n| n > 2 } # => 3 p numbers.detect { |n| n > 2 } # => 3(同じ結果)

破壊的メソッド .select! と .reject!

.select.reject は元の配列を変更せず、新しい配列を返します。元の配列自体を変更したい場合は、破壊的メソッドの .select!.reject! を使います。

numbers = [1, 2, 3, 4, 5, 6] # select! は元の配列を変更する numbers.select! { |num| num.even? } p numbers # => [2, 4, 6](変更された)
words = ["apple", "", "banana", "", "cherry"] # reject! は元の配列を変更する words.reject! { |word| word.empty? } p words # => ["apple", "banana", "cherry"](変更された)

まとめ

  • .select メソッドは条件に合う要素だけを抽出して新しい配列を作る
  • .reject メソッドは条件に合わない要素だけを抽出する(.select の逆)
  • .find メソッドは条件に合う最初の1つの要素を返す(配列ではなく単一の値)
  • .map は変換、.select は絞り込みという違いがある
  • .select!.reject! は元の配列を直接変更する破壊的メソッド