Tech Racho エンジニアの「?」を「!」に。
  • 開発

RubyのArray(配列)の使い方

morimorihogeです。そとがさむい。

たまには初心者向けの記事を書いてみようということで、Rubyの配列であるArrayクラスについて、基本的な部分に絞った内容を書いてみようと思います。PHP等の他言語からRubyを勉強している人なんかには参考になるのではないかと思います。
※超初心者向けです。確認環境は2.4.0準拠ですが1.9.3以降のそこそこ最近のRubyであればほとんど問題なく利用できると思います。

基本的には公式のArrayドキュメント に書いてあることばかりですが、リファレンス形式だと読みにくい、という人はどうぞ。
なお、引用サンプルではpry の出力結果を出していますが、これはIRBでも同じことができます。手元で動かしてみたい方はrubyが入っていればとりあえずirbして以下を追いかけてもらうと良いでしょう。

Arrayの初期化

RubyにおけるArrayは、リテラルを使って初期化できます。空配列であれば

arr_var = []

で良いですし、明示的にArray.newを使って

arr_var = Array.new

とすることもできます。

RubyのArrayはPHP等と同じように、中にどんなオブジェクトも要素として持つことができますので、

[1, "piyo", nil, nil]

といったArrayも定義可能です。また、Rubyらしい初期化方式だと、Array.newにブロックを渡して

[3] pry(main)> Array.new(5) {|index| "hoge_#{index}"}
=> ["hoge_0", "hoge_1", "hoge_2", "hoge_3", "hoge_4"]

みたいなこともできます。

Arrayの要素アクセス

要素へのアクセスは、他の言語でも一般的な添字を使った

arr_var[1]

といったアクセス(添字は0番目から始まる)もありますし、#fetchメソッドを使い

arr_var.fetch(1)

としても良いです。通常は添字アクセスだけ覚えていれば良いように思いますが、#fetchは第二引数に値が見つからなかった場合のデフォルト値指定ができるため、

[12] pry(main)> arr_var[10000]
=> nil
[13] pry(main)> arr_var.fetch(10000, 0)
=> 0

という感じで指定した添字の値がない場合にnil以外を受け取りたい、といったときに覚えておくと便利です。
また、Rubyらしいアクセス方式として、添字にRangeオブジェクト(n..m)を渡すことによって部分Arrayを取り出せます。n番目の要素からm番目の要素まで取り出す感じですね。

[60] pry(main)> arr_var = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
[61] pry(main)> arr_var[2..3]
=> [3, 4]

Arrayの大きさ取得 #size #length

Arrayの要素数を取得するには#sizeまたは#lengthを使います。最大の添字番号ではないので注意です

[53] pry(main)> arr_var = [1,2,3]
=> [1, 2, 3]
[54] pry(main)> arr_var.size
=> 3
[55] pry(main)> arr_var.length
=> 3

Arrayの要素に対する繰り返し

どんな入門書にも必ず載っている#eachとブロックを使えば良いですね。Rubyではcollection(集合)オブジェクトの中身を順番に取り出したければ基本#eachだと思っていて良いです。
Rubyにfor文は構文としてはありますが、ほとんど使われません。他言語から来た人が一番違和感を覚える所かもしれませんが、ブロックを使った記法に慣れるためにもforは使わず#eachを使うようにしていくのが良いと思います。

中括弧を使ったブロックの書き方では

arr_var.each{|val| puts "value: #{val}"}

do-endを使った書き方であれば

arr_var.each do |val|
  puts "value: #{val}"
end

とすれば良いです。上記二つは全く同じ意味になりますが、Ruby Style GuideというRubyの好ましい書き方まとめ に沿うのであれば、ブロックの中身が1行で終わるなら中括弧記法、2行以上あるならdo-end記法となります。
とりあえず動けばいいや、という話であればどちらでも構いませんが、よりRubyらしい書き方、美しい(他人が読みやすいとされる)コードを目指すのであれば少しずつ追いかけていくと良いと思います。

Arrayの要素削除

Arrayから特定の要素を削除したい場合には#deleteまたは#delete_atを使います。
#deleteは引数と==マッチする要素全てを削除します。添字番号を指定して削除するのは#delete_atです。間違えると悲しいことになるので気をつけましょう。

[14] pry(main)> arr_var = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
[15] pry(main)> arr_var.delete(4)
=> 4
[16] pry(main)> arr_var
=> [1, 2, 3, 5]
[17] pry(main)> arr_var = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
[18] pry(main)> arr_var.delete_at(4)
=> 5
[19] pry(main)> arr_var
=> [1, 2, 3, 4]

Ruby的なメソッドとしては#delete_ifがあります。これは要素を順番にブロック内で評価し、ブロックの評価結果(最後に評価された式)がtrueになった要素を削除します。
他の言語だとforやforeachループを書いて削除していく処理を、組み込みメソッドで書けるのがシンプルで良いですね。

[20] pry(main)> arr_var = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
[21] pry(main)> arr_var.delete_if{|var| 2 < var && var < 5}
=> [1, 2, 5]
[22] pry(main)> arr_var
=> [1, 2, 5]

Arrayのソート(並べ替え)

Arrayをソートしたい場合には#sortを使います。

[23] pry(main)> arr_var = [-1,3,0,-10,1]
=> [-1, 3, 0, -10, 1]
[24] pry(main)> arr_var.sort
=> [-10, -1, 0, 1, 3]

正しいソート結果を得たい場合には、要素の型を揃える必要があります。文字列と数値など、相互に比較できないものが混じるとエラーになります。

[25] pry(main)> arr_var = [-1,"5",2,"0",-10]
=> [-1, "5", 2, "0", -10]
[26] pry(main)> arr_var.sort
ArgumentError: comparison of Integer with String failed
from (pry):26:in `sort'

しかし、こんなときも#sortメソッドのブロックにオブジェクトの比較条件を渡してやることで、ソートさせることが可能です。
以下は#to_iメソッドを使うことで文字列も数値も全て数値に変換した上で比較するようにした例です。

[28] pry(main)> arr_var.sort{|a,b| a.to_i <=> b.to_i}
=> [-10, -1, "0", 2, "5"]

なお、逆順にしたければ#reverseを使えば良いです。逆順に#eachしたければ#eachの代わりに#reverse_eachを使うことができます。

[31] pry(main)> arr_var
=> [-1, "5", 2, "0", -10]
[32] pry(main)> arr_var.reverse
=> [-10, "0", 2, "5", -1]

スタックアクセス

Arrayをスタック的なデータ置き場として使いたい場合は#push#popを使えば良いです。要素が無い状態で#popすると、nilが返るようになっています。

[34] pry(main)> arr_var = []
=> []
[35] pry(main)> arr_var.push 'a'
=> ["a"]
[36] pry(main)> arr_var.push 'b'
=> ["a", "b"]
[37] pry(main)> arr_var.push 'c'
=> ["a", "b", "c"]
[38] pry(main)> arr_var.pop
=> "c"
[39] pry(main)> arr_var.pop
=> "b"
[40] pry(main)> arr_var.pop
=> "a"
[41] pry(main)> arr_var.pop
=> nil

その他よく使われる・便利なメソッド達

ここまでは他の言語でも一般的に実装されている機能ですが、ここからはあまり他言語では見ないメソッドを紹介してみます。

要素追加 <<

<<を使って要素の追加ができます。#pushと違い同時に1要素しか追加できませんが、Arrayに新しく要素を追加する、という意味合いでわかりやすいためか、Rubyではよく使われます。

[42] pry(main)> arr_var = []
=> []
[43] pry(main)> arr_var << 1
=> [1]
[44] pry(main)> arr_var << 2
=> [1, 2]
[45] pry(main)> arr_var << 3
=> [1, 2, 3]

1番目の要素を返す #first、最後の要素を返す #last

Arrayの先頭の要素を取るには添字アクセスで[0]を取ってももちろん良いのですが、最初と最後の要素については#first#lastによってアクセスすることができます(Rails環境だとActiveSupportが入るため、#secondなんかも使えますがRuby標準ではこの二つのみ)。

[46] pry(main)> arr_var = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
[47] pry(main)> arr_var.first
=> 1
[48] pry(main)> arr_var.last
=> 5

また、Arrayの末尾要素は「後ろから数えて1番目」なので添字[-1]でも取得することが可能です。

[50] pry(main)> arr_var[-1]
=> 5

入れ子になったArrayを全部展開する#flatten

入れ子になっているArrayをフラットなArrayに解きほぐして展開します。これは実際の処理結果を見るのが早いと思います。

[51] pry(main)> arr_var = [[1,2,3], 4,[5,[6,[7,8],9]],10]
=> [[1, 2, 3], 4, [5, [6, [7, 8], 9]], 10]
[52] pry(main)> arr_var.flatten
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

どんな時に使うかこれだけだとピンと来ないかもしれませんが、例えば公開APIを利用していて入れ子になったデータ構造を取得したときに、階層関係なく全ての要素について処理したいといった場合、何階層あるのかよく分からないデータをとりあえず処理したいといった場合にあると嬉しいメソッドです。

空かどうかを調べる#empty?

Arrayの中身が空かどうかを調べるには、要素数が0であるかを#lengthで調べても良いのですが、Rubyはより自然言語的な書き方が好まれることから#empty?を使うことが多いです。この辺りは文化的なものもあるので、他言語から移行してくる人には違和感があるかもしれませんが、こういうものだと思って慣れましょう。

[1] pry(main)> arr_var = [1,2]
=> [1, 2]
[2] pry(main)> arr_var.empty?
=> false
[3] pry(main)> arr_var = []
=> []
[4] pry(main)> arr_var.empty?
=> true

まとめ

そんなわけで、公式のArrayドキュメント からRubyを使う上でよく使うメソッド群を紹介してみました。
まだ紹介していないメソッドも多数ありますが、とりあえずここに挙げたものが使えれば他言語から移行してきた人でも最低限やりたいことはできるのではないかと思います。


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。