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 になる 4、5、6 だけが結果の配列に含まれます。
.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
条件に合う要素が見つからない場合、.find は nil を返します。
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!は元の配列を直接変更する破壊的メソッド
演習に挑戦