はじめに
プログラムを書くときに配列は欠かせない要素です。
ですので、配列で利用できるメソッドをおさえておくことはアルゴリズムを組む際に大いに助けになります。
配列のリーチできる範囲を把握して、より精細なコードを書けるようになりましょう。
では早速メソッドをまとめていきます。
unshift
配列の先頭に要素を追加します。
greeting_informal = ["How's everything?", "What's up?", "How's it going?", "Hi, what's new?"]
p greeting_informal.unshift("Hi, good to see you.")
#=> ["Hi, good to see you.", "How's everything?", "What's up?", "How's it going?", "Hi, what's new?"]
shift
配列の先頭の要素を取り出します。破壊的なメソッド。
greeting_informal = ["How's everything?", "What's up?", "How's it going?", "Hi, what's new?"]
p greeting_informal.shift
#=> "How's everything?"
push
配列の末尾に要素を追加します。
greeting_formal = ["Hello, how do you do?", "Hi, it's a pleasure to meet you.", "Hi, good to see you again."]
p greeting_formal.push("How are you?")
#=> ["Hello, how do you do?", "Hi, it's a pleasure to meet you.", "Hi, good to see you again.", "How are you?"]
pop
配列の末尾の要素を取り出します。破壊的なメソッド。
greeting_formal = ["Hello, how do you do?", "Hi, it's a pleasure to meet you.", "Hi, good to see you again."]
p greeting_formal.pop
#=> "Hi, good to see you again."
上記4つのメソッドを理解した上で、「列」として配列をみた場合のキュー
、スタック
というデータ構造を理解しましょう。
キュー
は、要素を取り出す際、要素を追加した順に取り出すことができるデータ構造です。これはFIFO(First-in First-out)とも呼ばれています。「最初に入れたものを最初に取り出す」という意味ですね。また、何かを待つ人が、列を作って並んでいる状態と同じなので、待ち行列と呼ばれることもあります。
キューは、push
メソッドとshift
メソッドを使って実現します。
スタック
は、要素を追加した順と逆の順序で要素を取り出していきます。こちらは、LIFO(Last-in First-out)、つまり「最後に入れたものを最初に取り出す」というデータ構造です。要素を追加するときは一番後ろに加え、要素を取り出すときは一番後ろから取る、という感じです。
スタックは、push
メソッドとpop
メソッドを使って実現します。
ここで、shiftやpopメソッドは配列の要素を取り出すだけではなく、配列からその要素を削除します。それでは困る場合のために、先頭か末尾の要素を参照するだけのメソッド、first
、last
メソッドが用意されています。
first
配列の先頭の要素を参照する
greeting_informal = ["How's everything?", "What's up?", "How's it going?", "Hi, what's new?"]
p greeting_informal.first
#=> "How's everything?"
last
配列の末尾の要素を参照する
greeting_formal = ["Hello, how do you do?", "Hi, it's a pleasure to meet you.", "Hi, good to see you again."]
p greeting_formal.last
#=> "Hi, good to see you again."
concat
配列aに配列bを連結します。破壊的なメソッド。concatenateの略。
pleasure_phrase = ["Yes!", "I did it."]
pleasure_phrase.concat(["Bingo!", "Hooray!"])
p pleasure_phrase
#=> ["Yes!", "I did it.", "Bingo!", "Hooray!"]
freeze
オブジェクトの内容変更を禁止します。これを使うとオブジェクトは凍結され、変更しようとするとエラーになります。
a = [1, 2, 3, 4]
a.freeze
p a.pop
#=> in `pop': can't modify frozen Array (FrozenError)
dup
レシーバのオブジェクトのコピーを作成して返します。
先ほどのfreeze
メソッドを使い、一度凍結したオブジェクトを元に戻す方法はありませんが、dupメソッドでコピーしたオブジェクトは凍結されていない状態となります。duplicateの略。
a = [1, 2, 3, 4]
a.freeze
b = a.dup
p a.pop
#=> in `pop': can't modify frozen Array (FrozenError)
p b.pop
#=> 4
campact
配列aの中から要素がnilのものを取り除きます。compact
は新しい配列を作り、compact!
は元の配列を置き換えます。compact!メソッドはnilを取り除いた後のaを返しますが、何も取り除けなかったときはnilを返します。
「コンパクトにする」と捉えれば理解しやすいですね。
a = [1, nil, 3, nil, nil]
a.compact
p a
#=> [1, nil, 3, nil, nil]
a.compact!
p a
#=> [1, 3]
delete(x)
配列aから要素xを取り除きます。
a = [1, nil, 3, nil, nil]
a.delete(nil)
p a
#=> [1, 3]
a.delete(1)
p a
delete_at(x)
配列a[x]の要素を取り除きます。
a = [1, nil, 3, nil, nil]
a.delete_at(2)
p a
#=> [1, nil, nil, nil]
reject
配列aの各要素について、ブロックを実行した結果、偽になった要素を配列にして返します。
reject!は破壊的なメソッドであり、delete_if
と同じ挙動を示します。
a = [1, 2, 3, 4, 5]
a.reject{|i| i > 3}
p a
#=> [1, 2, 3, 4, 5]
a.reject!{|i| i > 3}
p a
#=> [1, 2, 3]
slice
配列aから指定された部分を取り除き、取り除いた値を返します。
a = [1, 2, 3, 4, 5]
p a.slice(1, 2)
#=> [2, 3]
p a
#=> [1, 2, 3, 4, 5]
p a.slice!(1, 2)
#=> [2, 3]
p a
#=> [1, 4, 5]
uniq
配列aの重複する要素を削除します。
a = [1, 2, 3, 4, 3, 2, 1]
p a.uniq!
#=> [1, 2, 3, 4]
fill(value)
配列aの要素をvalueに置き換えます。
引数が1つの場合は、aの要素全てをvalueにします。
引数が2つの場合は、第二引数から、(第三引数)個までをvalueにします。
a = [1, 2, 3, 4, 5]
p a.fill(0)
#=> [0, 0, 0, 0, 0]
p a
#=> [0, 0, 0, 0, 0]
p [1, 2, 3, 4, 5].fill(0, 2)
#=> [1, 2, 0, 0, 0]
p [1, 2, 3, 4, 5].fill(0, 2, 2)
#=> [1, 2, 0, 0, 5]
flatten
配列aを平坦化します。「平坦化」というのは、配列の中に配列が入れ子になっているような場合に、その入れ子を展開して、1つの大きな配列にする操作です。
a = [1, [2, [3]], [4], 5]
a.flatten!
p a
#=> [1, 2, 3, 4, 5]
reverse
配列aの要素を逆順に並べ替えます。
a = [1, 2, 3, 4, 5]
a.reverse!
p a
#=> [5, 4, 3, 2, 1]
sort
配列aの各要素を並べ替えます。並べ替え方は、ブロックで指定できます。
ブロックを指定しない場合には、<=>
演算子を使って比較します。
a = [2, 4, 3, 5, 1]
a.sort!
p a
#=> [1, 2, 3, 4, 5]
sort_by
配列aの要素を並べ替えます。並べ替えは全ての要素についてブロックを評価した結果をソートした順に行われます。
a = [2, 4, 3, 5, 1]
a.sort_by!{|i| -i}
p a
#=> [5, 4, 3, 2, 1
find_all(or select)
各要素に対してブロックを実行し、結果が真になる要素だけを配列にして返します。
a = [2, 4, 3, 5, 1]
p a.find_all{|i| i == 2 || i == 3}
#=> [2, 3]
inject(or reduce)
injectはeachやmapと同じように繰り返しを行うメソッドです。
ブロックを使って繰り返し計算を行うことが特徴で、
配列オブジェクト.inject {|前の値, 次の値| ブロック処理 }
のように記述します。
繰り返し順にブロックの要素が配列分加算されていき、ブロックの処理にて計算を行っていきます。
injectは第1引数にinjectが内部で保持している変数の初期値を設定できて、その内部変数がブロックの第1ブロック変数となります。
array = 1..5
array.inject(0){ |sum,num| p sum + num}
# 1
# 3
# 6
# 10
# 15
array.inject(3){ |sum,num| p sum + num}
# 4
# 6
# 9
# 13
# 18
count
引数がなければ要素数を、引数があれば引数と同じ要素数を返します。
a = [2, 4, 3, 5, 1, 2]
p a.count(2)
#=> 2
empty
empty?は「入れ物」は存在するのが前提で、配列の中身が空か、文字列の中身が空の場合にtrueを返す。
配列や文字列そのもの(入れ物)が無い場合にはNoMethodErrorが発生する。空ですか?と聞いてるわけだから、少なくとも「入れ物」が存在しないとダメってことですね。
a = [2, 4, 3, 5, 1, 2]
b = []
p a.empty?
#=> false
p b.empty?
#=> true
Ruby関連資料
おわりに
配列で利用する主なメソッドをまとめてみました。
読んでくださった方がアルゴリズムを組む際に、成功までの道のりを描くにあたって、一助になれば幸いです。