序章

配列を使用すると、プログラム内のデータのリストを表すことができます。 配列にデータが含まれると、そのデータを並べ替えたり、重複を削除したり、順序を逆にしたり、配列のセクションを抽出したり、配列を検索して特定のデータを検索したりできます。 配列を文字列に変換し、データの1つの配列を別の配列に変換し、配列を単一の値にロールアップすることもできます。

このチュートリアルでは、配列に格納されたデータを操作するためにRubyが提供する最も実用的な方法のいくつかを探ります。

このチュートリアルを実行すると、感嘆符(!)で終わるメソッドがいくつか表示されます。 これらのメソッドには、元の値を変更したり、例外を発生させたりするなどの副作用があります。 このチュートリアルで使用する多くのメソッドには、この接尾辞が付いた関連メソッドがあります。

また、疑問符(?)で終わるメソッドにも出くわします。 これらのメソッドはブール値を返します。

これらは、Ruby全体で使用される命名規則です。 これは、プログラムレベルで実施されるものではありません。 これは、メソッドから何を期待できるかを識別するためのもう1つの方法です。

要素にアクセスするいくつかの方法を見て、配列メソッドの調査を始めましょう

要素へのアクセス

チュートリアルRubyで配列を操作する方法を既に実行している場合は、次のようにゼロベースのインデックスを使用して個々の要素にアクセスできることを知っています。

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sharks[0]    # "Tiger"
sharks[1]    # "Great White"
sharks[-1]   # "Angel"

また、firstおよびlastメソッドを使用して、配列の最初と最後の要素を取得できることを思い出してください。

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sharks.first   # "Tiger"
sharks.last    # "Angel"

最後に、存在しない要素にアクセスすると、nilが表示されます。 ただし、代わりにエラーが発生する場合は、fetchメソッドを使用してください。

sharks.fetch(42)
Output
IndexError: index 42 outside of array bounds: -4...4

エラーを発生させるのではなく、独自のデフォルトを指定したい場合は、次のようにすることもできます。

sharks.fetch(42, "Nope")     # "Nope"

次に、配列から複数の要素を取得する方法を見てみましょう。

複数の要素の取得

単一の要素だけでなく、配列から値のサブセットを取得したい場合があります。

開始インデックスを指定し、その後に必要な要素数を指定すると、それらの値を含む新しい配列が取得されます。 たとえば、次のようにsharks配列から2つの中央のエントリを取得できます。

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sharks[1,2]   # ["Great White", "Hammerhead"] 

インデックス1、つまり"Great White"から開始し、2要素が必要であることを指定すると、"Great White""Hammerhead"

sliceメソッドを使用して、同じことを行うことができます。

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sharks.slice(1,2)   # ["Great White", "Hammerhead"] 

sliceメソッドも新しい配列を返し、元の配列は変更されません。 ただし、slice!メソッドを使用すると、元の配列も変更されます。

takeメソッドを使用すると、配列の先頭から指定された数のエントリを取得できます。

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sharks.take(2)  # ["Tiger", "Great White"]

特定の値ではなく、配列からランダムな値を取得したい場合があります。 方法を調べてみましょう。

配列からランダムエントリを取得する

あなたは運が左右するゲームに取り組んでいるか、コンテストの勝者を選ぶプログラムを書いているかもしれません。 そのようなものには、ある種のランダムな値が必要です。 一般的な解決策は、可能な選択肢を配列に入れて、ランダムなインデックスを選択することです。

配列からランダム要素を取得するには、0と配列の最後のインデックスの間にランダムインデックスを生成し、それをインデックスとして使用して値を取得できますが、より簡単な方法があります:sampleメソッドは、配列からランダムなエントリを取得します。

これを使用して、ストック回答の配列からランダムな回答を取得し、Magic8ボールゲームのプリミティブバージョンを作成しましょう。

8ball.rb
answers = ["Yes", "No", "Maybe", "Ask again later"]
print answers.sample
Output
Maybe

sampleメソッドは、ランダムエントリの配列を返す引数も受け入れるため、複数のランダムエントリが必要な場合は、必要な数を指定するだけです。

random_sharks.rb
sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
sample = sharks.sample(2)
print sample
Output
["Whale", "Great White"]

次に、配列内の特定の要素を見つける方法を見てみましょう。

要素の検索とフィルタリング

配列内の特定の要素を探しているときは、通常、探しているものが見つかるまでその要素を繰り返し処理します。 ただし、Ruby配列は、配列を検索するプロセスを簡素化するために特別に設計されたいくつかのメソッドを提供します。

要素が存在するかどうかを確認したいだけの場合は、include?メソッドを使用できます。このメソッドは、指定されたデータが配列の要素である場合にtrueを返します。

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
sharks.include? "Tiger"      # true

["a", "b", "c"].include? 2   # false

ただし、include?は完全に一致する必要があるため、部分的な単語を検索することはできません。

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
sharks.include? "Tiger"      # true
sharks.include? "tiger"      # false
sharks.include? "ti"         # false

findメソッドは、指定した条件に一致する配列の最初の要素を見つけて返します。

たとえば、文字aを含むsharks配列の最初のエントリを識別するには、eachメソッドを使用して各エントリを比較し、最初のもの、このように:

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
result = nil
sharks.each do |shark|
  if sharks.include? "a"
    result = shark
    break
  end
end

または、findメソッドを使用して、同じことを行うことができます。

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
result = sharks.find {|item| item.include?("a")}
print result
Output
Hammerhead

findは、配列内の各要素に指定したブロックを実行します。 ブロック内の最後の式がtrueと評価された場合、findメソッドは値を返し、反復を停止します。 すべての要素を繰り返し処理しても何も見つからない場合は、nilを返します。

selectメソッドも同様に機能しますが、単一の値を返して停止するのではなく、条件に一致するすべての要素を含む新しい配列を作成します。

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
results = sharks.select {|item| item.include?("a")}
print results
Output
["Hammerhead", "Great White", "Whale"]

rejectメソッドは、条件に一致しない要素を含む新しい配列を返します。 不要な要素を削除するフィルターと考えることができます。 文字aを含むすべてのエントリを拒否する例を次に示します。

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
results = sharks.reject {|item| item.include?("a")}
print results
Output
["Tiger"]

selectrejectはどちらも新しい配列を返し、元の配列は変更されません。 ただし、select!およびreject!メソッドを使用すると、元の配列が変更されます。

find_allメソッドはselectのエイリアスですが、find_all!メソッドはありません。

次に、配列の値を並べ替える方法を見てみましょう。

配列の並べ替え

データの並べ替えは一般的な方法です。 名前のリストをアルファベット順に並べたり、番号を最小から最大に並べ替えたりする必要がある場合があります。

Ruby配列には、配列内の要素の順序を逆にすることができるreverseメソッドがあります。 すでに整理されているデータのリストがある場合は、reverseを使用すると要素をすばやく反転できます。

sharks = ["Angel", "Great White", "Hammerhead", "Tiger"]
reversed_sharks = sharks.reverse
print reversed_sharks
Output
["Tiger", "Hammerhead", "Great White", "Angel"]

reverseメソッドは新しい配列を返し、元の配列を変更しません。 代わりに元の配列を変更する場合は、reverse!メソッドを使用してください。

ただし、配列を逆にすることが、データを並べ替える最も効率的または実用的な方法であるとは限りません。 sortメソッドを使用して、配列内の要素を希望どおりに並べ替えます。

文字列または数値の単純な配列の場合、sortメソッドは効率的であり、探している結果が得られます。

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sorted_sharks = sharks.sort
print sorted_sharks
Output
["Angel", "Great White", "Hammerhead", "Tiger"]

ただし、別の方法で並べ替える場合は、sortメソッドにその方法を指示する必要があります。 sortメソッドは、配列内の要素へのアクセスを提供するRubyブロックを取得して、それらを比較できるようにします。

比較を行うには、比較演算子<=>)を使用します。これは、宇宙船演算子と呼ばれることもあります。 この演算子は、2つのRubyオブジェクトを比較し、左側のオブジェクトが小さい場合は-1、オブジェクトが同じ場合は0、左側のオブジェクトが小さい場合は1を返します。より大きい。

1 <=> 2    # -1
2 <=> 2    #  0
2 <=> 1    #  1

Rubyのsortメソッドは、-10、または1を返す必要があるブロックを受け入れ、配列内の値を並べ替えるために使用します。

配列内のエントリを明示的に比較して昇順で並べ替える例を次に示します。

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sorted_sharks = sharks.sort{|a,b| a <=> b }
print sorted_sharks

a変数とb変数は、比較される配列内の個々の要素を表します。 結果は次のようになります。

Output
["Angel", "Great White", "Hammerhead", "Tiger"]

サメを逆の順序で並べ替えるには、比較でオブジェクトを逆にします。

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sorted_sharks = sharks.sort{|a,b| b <=> a }
print sorted_sharks
Output
["Tiger", "Hammerhead", "Great White", "Angel"]

sortメソッドは、整数、浮動小数点数、文字列などの単純なデータ型を含む配列に最適です。 ただし、配列にさらに複雑なオブジェクトが含まれている場合は、もう少し作業を行う必要があります。

これがハッシュの配列で、各ハッシュはサメを表しています。

sharks = [
  {name: "Hammerhead"},
  {name: "Great white"},
  {name: "Angel"}
]

sortでこれを並べ替えるのは簡単ではありません。 アレイでsortを呼び出すと失敗します。

sharks.sort
Output
ArgumentError: comparison of Hash with Hash failed

比較を行うには、sortに何を比較するかを指示する必要があります。 そこで、ハッシュ内の:nameキーの値を比較します。

sorted_sharks.sort{|a, b| a[:name] <=> b[:name]}
print sorted_sharks
Output
[{:name=>"Angel"}, {:name=>"Great white"}, {:name=>"Hammerhead"}]

より複雑な構造で作業している場合は、代わりにsort_byメソッドを検討することをお勧めします。このメソッドは、より効率的なソートアルゴリズムを使用します。 sort_byは、配列内の現在の要素への参照である1つの引数のみを必要とするブロックを取ります。

sharks = [
  {name: "Hammerhead"},
  {name: "Great white"},
  {name: "Angel"}
]

sorted_sharks = sharks.sort_by{|shark| shark[:name] }
print sorted_sharks
Output
[{:name=>"Angel"}, {:name=>"Great white"}, {:name=>"Hammerhead"}]

sort_byメソッドは、シュワルツ変換を実装します。これは、特定のキーの値に基づいてオブジェクトを比較するのに最適な並べ替えアルゴリズムです。 したがって、オブジェクトのコレクションを比較するときは、より効率的であるため、sort_byを使用していることに気付くでしょう。

sortsort_byはどちらも新しい配列を返し、元の配列はそのまま残します。 元のアレイを変更する場合は、代わりにsort!sort_by!を使用してください。

値の並べ替えに加えて、重複を取り除くこともできます。

重複する要素の削除

重複しているデータのリストが表示される場合があります。 配列を反復処理して重複を除外することもできますが、Rubyのuniqメソッドを使用すると非常に簡単になります。 uniqメソッドは、重複する値がすべて削除された新しい配列を返します。

[1,2,3,4,1,5,3].uniq   # [1,2,3,4,5]

2つのデータセットをマージすると、重複してしまうことがあります。 サメのこれらの2つの配列を取ります:

sharks = ["Tiger", "Great White"]
new_sharks = ["Tiger", "Hammerhead"]

それらを一緒に追加すると、重複するエントリが取得されます。

sharks + new_sharks
# ["Tiger", "Great White", "Tiger", "Hammerhead"]

uniqを使用して重複を削除することもできますが、完全に導入することは避けたほうがよいでしょう。 配列を一緒に追加する代わりに、パイプ演算子|を使用して、配列を一緒にマージします。

sharks | new_sharks
# ["Tiger", "Great White", "Hammerhead"]

Ruby配列は減算もサポートしています。つまり、sharksからnew_sharksを減算して、新しい値のみを取得できます。

sharks = ["Tiger", "Great White"]
new_sharks = ["Tiger", "Hammerhead"]
sharks - new_sharks   # ["Great White"]

次に、各要素の値を操作する方法を見てみましょう。

データの変換

mapメソッドとそのエイリアスcollectは、配列の内容を変換できます。つまり、配列内の各要素に対して操作を実行できます。

たとえば、mapを使用して、配列の各エントリに対して算術演算を実行し、新しい値を含む新しい配列を作成できます。

numbers = [2,4,6,8]

# square each number
squared_numbers = numbers.map {|number| number * number}

print squared_numbers

squared_numbers変数は、元の数値の配列であり、2乗されています。

[4, 16, 36, 64]

mapは、配列をHTMLドロップダウンリストの要素に変換するためにWebアプリケーションでよく使用されます。 これは、それがどのように見えるかについての非常に単純化されたバージョンです。

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]

options = sharks.map {|shark| "<option>#{shark}</option>"}

print options

options配列では、各サメが<option></option>HTMLタグでラップされています。

["<option>Hammerhead</option>", "<option>Great White</option>", "<option>Tiger</option>", "<option>Whale</option>"]

mapは新しい配列を返し、元の配列は変更されません。 map!を使用すると、既存のアレイが変更されます。 また、mapにはcollectというエイリアスがあることに注意してください。 一貫性を保ち、コードでどちらか一方を使用する必要があります。

mapは新しい配列を返すため、配列をさらに変換して操作したり、文字列に変換したりすることもできます。 次にそれを見てみましょう。

配列を文字列に変換する

Rubyのすべてのオブジェクトには、オブジェクトを文字列に変換するto_sメソッドがあります。 これは、printステートメントが使用するものです。 sharksの配列を考えると:

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]

to_sメソッドを呼び出すと、次の文字列が作成されます。

"[\"Hammerhead\", \"Great White\", \"Tiger\", \"Whale\"]"

これはデバッグには最適ですが、実際のプログラムではあまり役に立ちません。

joinメソッドは、配列を文字列に変換しますが、要素をどのように組み合わせるかをより細かく制御できます。 joinメソッドは、区切り文字として使用する文字を指定する引数を取ります。 サメの配列をスペースで区切られたサメの名前の文字列に変換するには、次のようにします。

shark_join.rb
sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
result = sharks.join(" ")
print result
Output
Hammerhead Great White Tiger Whale

各サメの名前をコンマのスペースで区切る場合は、区切り文字としてコンマとスペースを使用します。

shark_join.rb
sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
result = sharks.join(", ")
print result
Output
Hammerhead, Great White, Tiger, Whale

joinメソッドに引数を指定しない場合でも、文字列は取得されますが、区切り文字はありません。

shark_join.rb
sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
result = sharks.join
print result
Output
HammerheadGreat WhiteTigerWhale

joinmapと組み合わせて使用すると、データの配列を出力に変換する簡単な方法です。 mapを使用してデータ内の各要素を変換してから、joinを使用して全体を印刷可能な文字列に変換します。 sharks配列をHTML要素の配列に変換する例を覚えていますか? これも同じ例ですが、今回はjoinを使用して、要素の配列を改行文字を区切り文字として持つ文字列に変換します。

map.rb
sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
options = sharks.map {|shark| "<option>#{shark}</option>"}
output = options.join("\n")
print output
Output
<option>Hammerhead</option> <option>Great White</option> <option>Tiger</option> <option>Whale</option>

配列を文字列に変換する代わりに、その内容の合計を取得したり、単一の値になる他の種類の変換を実行したりすることができます。 次はそれです。

配列を単一の値に減らす

データのセットを操作しているときに、データを合計などの単一の値にまとめる必要がある場合があります。 これを行う1つの方法は、変数とeachメソッドを使用することです。

result = 0
[1, 2, 3].each {|num| result += num}
print result
Output
6

代わりに、reduceメソッドを使用してこれを行うことができます。 reduceメソッドは、配列を反復処理し、要素ごとに2項演算を実行することにより、現在の合計を維持します。

reduceメソッドは、結果の初期値と、結果への参照と現在の要素への参照の2つのローカル値を持つブロックを受け入れます。 ブロック内で、最終結果を計算するロジックを指定します。

配列を合計したいので、結果を0に初期化してから、現在の値をブロックの結果に追加します。

output = [1,2,3].reduce(0) {|result, current| result += current }
print output
Output
6

結果を0に初期化する場合は、引数を省略してブロックを渡すだけです。 これにより、結果が配列の最初の値に自動的に設定されます。

output = [1,2,3].reduce {|result, current| result += current }
print output
Output
6

reduceメソッドは、 binaryメソッド、または引数として別のオブジェクトを受け入れる1つのオブジェクトのメソッドも指定します。これは、配列の各エントリに対して実行されます。 reduceは、結果を使用して単一の値を作成します。

Rubyで2 + 2と書くと、実際には整数2に対して+メソッドが呼び出されます。

2.+(2)   # 4

Rubyはシンタックスシュガーを使用しているため、2 + 2として表現できます。

reduceメソッドでは、名前をシンボルとして渡すことにより、バイナリメソッドを指定できます。 つまり、:+reduceメソッドに渡して、配列を合計できます。

output = [1, 2, 3].reduce(:+)   
print output
Output
6

reduceを使用すると、数字のリストを合計するだけでは不十分です。 これを使用して値を変換できます。 reduceは配列を単一の値に減らすことに注意してください。 ただし、単一の値を別の配列にすることはできないという規則はありません。

整数に変換する必要のある値のリストがあるとしましょう。 ただし、整数に変換できる値のみが必要です。

rejectを使用して非数値を破棄し、mapを使用して残りの値を整数に変換することができます。 ただし、reduceを使用すると、すべてを1つのステップで実行できます。 方法は次のとおりです。

初期化値として空の配列を使用します。 次に、ブロック内で、Integerメソッドを使用して現在の値を整数に変換します。 値を整数に変換できない場合、Integerは例外を発生させ、nilをキャッチして値に割り当てることができます。

次に、値を取得して配列に配置しますが、nilでない場合に限ります。

コードは次のようになります。 これを試してみてください:

convert_array_of_values.rb
values = ["1", "2", "a", "3"]
integers = values.reduce([]) do |array, current|
  val = Integer(current) rescue nil
  array.push(val) unless val.nil?
  array
end
print integers
Output
[1,2,3]

単一の値に変換する必要のある要素のリストがある場合は常に、reduceで解決できる可能性があります。

結論

このチュートリアルでは、いくつかの方法を使用して配列を操作しました。 個々の要素を取得し、配列を検索して値を取得し、要素を並べ替え、データを変換して、新しい配列、文字列、合計を作成しました。 これらの概念を適用して、Rubyに関する多くの一般的なプログラミングの問題を解決できます。

Rubyでデータを操作する方法を引き続き検討するには、これらの関連チュートリアルを必ず確認してください。