Help us understand the problem. What is going on with this article?

[Ruby入門] 06. 配列を扱う

More than 3 years have passed since last update.

>> 連載の目次は こちら!

■とりあえず普通に書く

arr1 = [1, 2, 3]
arr2 = [99, "aa", 77]

■文字列の配列を%記法ですっきり書く

arr  = %w(waiwai gayagaya heyhey);
name = 'miro'
arr  = %W(ワイワイ 改行\n含むよ 空白と\ 変数#{name}含むよ);
  • 半角空白で区切って、文字列要素をクォーテーションなしで指定できる
  • %w は シングルクォーテーション、%W は ダブルクォーテーション で各要素を囲んでいるのと同じ
  • 囲み記号には、英数字と空白・改行以外の文字が利用できる
  • 半角空白そのものを文字列に含めたいときは、バックスラッシュでエスケープする

■要素数の取得

puts arr1.length
puts arr2.size    # lengthと同じ
puts arr2.count {|item| item.is_a?(String)} # ある条件を満たす要素の数を取得

■要素へのアクセス

[インデックスでのアクセス]

puts arr1[1]
puts arr2.at(2)          # at でも取れる
puts arr1[-1]            # 負のインデックスだと、最後から数える(-1が最後の要素)
puts arr2[-2]            # したがって-2だと、最後から2番めの要素
p arr1[99]               # 範囲外だとnilが返る。
p arr2[1..2]             # 範囲指定で配列として取り出す
arr2[0] = 111            # インデックスを指定して値を代入
arr2[1, 2] = ["bb", 88]  # インデックスと要素数を指定して、複数要素をまとめて代入
arr2[1, 0] = "xx"        # 要素数に0を指定すると、挿入になる
arr2[1, 0] = %w[yy qq]   # このとき複数の要素を挿入できる

[first | last でのアクセス]

arr = %w[aa bb cc]
arr2 = arr.first     # 先頭の要素を取得
arr3 = arr.first(2)  # 先頭のn個の要素を配列で取得
arr4 = arr.last      # 末尾の要素を取得
arr5 = arr.last(2)   # 末尾のn個の要素を配列で取得

[push | pop | shift | unshift でのアクセス(破壊的)]

arr = %w[aa bb cc]
arr.push("dd")        # 末尾に要素を追加
arr.unshift("xx")     # 先頭に要素を追加
poped = arr.pop()     # 末尾の要素を削除し、その要素を返す
shifted = arr.shift() # 先頭の要素を削除し、その要素を返す

■配列に対する繰り返し処理

ループの詳細については、
http://qiita.com/prgseek/items/4140ea928be40f55c68e
を参照

[for 文]

for item in arr do
  puts item
end

[each 文]

arr.each do |item|
  puts item
end

[each_with_index 文]

bros = ["長男", "次男", "三男"]
bros.each_with_index do |name, i| 
  puts "#{i+1}. #{name}"
end

[Enumerator オブジェクトからの with_index ]

bros = ["長男", "次男", "三男"]
bros.each.with_index(1) do |name, i| 
  puts "#{i}. #{name}"
end
  • eachメソッド で Enumerator オブジェトを生成して、そこから with_index メソッドを呼んでループする。これだと、インデックスの初期値を指定することができる!

■配列の要素を検索

arr = %w[aa bb cc d11 cc e22]
puts arr.index("cc")                      # 先頭から探して、みつかったindexを返す。見つからなければnilを返す
puts arr.rindex("cc")                     # 末尾から探して、みつかったindexを返す。見つからなければnilを返す
puts arr.include?("d11")                 # ある要素が含まれているか
puts arr.find {|item| item=~/[a-z]\d{2}/} # ある条件に該当する要素を検索し、`最初に見つかった要素`を返す。detectも同じ。
puts arr.all? {|item| item=~/^[a-z]/}     # すべての要素が、ある条件に該当するか
puts arr.any? {|item| item=~/[0-9]$/}     # いれずかの要素が、ある条件に該当するか
p arr.select {|item| item=~/[a-z]\d{2}/}  # ある条件に該当する要素をすべて拾って、配列で返す

■配列の要素の削除

arr = %w[aa bb cc d12 ee f98 gg]
deleted = arr.delete("bb")              # 要素を指定して、該当する要素(==で判定)を全て削除(破壊的)。削除された要素を返す。
deleted = arr.delete_at(1)              # indexを指定して要素を削除(破壊的)。削除された要素を返す
arr.delete_if {|x| x=~/d[0-9]{2}/ }     # ある条件に該当する要素を全て削除(破壊的)
brr = arr.reject {|x| x=~/f[0-9]{2}/ }  # ある条件に該当する要素を全て削除(非破壊的。!を付ければ破壊的)
arr.clear                               # 全要素を削除。空っぽの配列 [] になる(破壊的)

■配列をソートする

arr = [55, 47, 111, 36, 18, 1]
sorted = arr.sort                        # 1
sorted = arr.sort{|a, b| b <=> a }       # 2
sorted = arr.sort_by{|x| -x}             # 3

arr = %w[xyz bbb 0101 あいうえお test]      # 4
arr1 = arr.sort                          # 5
arr2 = arr.sort.reverse                  # 6

arr = [["c", 10], ["a", 20], ["b", 30]]  # 7
arr2 = arr.sort                          # 8
arr3 = arr.sort {|a,b| a[1] <=> b[1] }   # 9
  1. <=>演算子で判定して小さい順に並べる。数値なら普通に昇順になる。!を付けると破壊的
  2. sortメソッドでルールを指定する。上記は逆順の例。ブロックの戻り値によって要素を比較して並べる(戻り値が-1ならaが小さい、0なら同じ、1ならbが大きい)
  3. sort_by のほうが高速。上記は逆順の例。ブロックは要素を単独で処理して返し、最後に全体を<=>演算子で判定して小さい順に並べる。!を付けると破壊的
  4. 文字列の配列をソートしてみる...
  5. 昇順。["0101", "bbb", "test", "xyz", "あいうえお"]
  6. 降順。["あいうえお", "xyz", "test", "bbb", "0101"]
  7. 多次元配列をソートしてみる...
  8. 普通にソートすると、それぞれの一つ目の要素でソートされる。上記例だと ["a", 20], ["b", 30], ["c", 10]]
  9. ブロックを使って、それぞれの2つめの要素でソートしてみる。上記例だと [["c", 10], ["a", 20], ["b", 30]]

■配列を結合する

arr1, arr2 = [1, 2], %w[aa bb]
arr1.concat(arr2)   # [1, 2, "aa", "bb"] 配列の要素の後ろに別の配列の要素を追加。破壊的。
arr3 = arr1 + arr2  # [1, 2, "aa", "bb"] +演算子でも結合できる。こちらは非破壊的。

■ inject メソッドで配列の要素の合計を得る

●結論をさきに...

arr = [3, 4, 5, 6, 7]

# 合計を得る
total = arr.inject(:+)
p total  # 25

●inject メソッドを理解する

inject メソッドは、Enumerable モジュールのメソッド。
集合の要素に対して、繰り返し計算を行うためのメソッド。

inject {|before_result, item| 処理 }

injectにブロックを渡すと、配列の要素を順に巡って、
①要素1と要素2 → ②ブロックの前回の戻り値と要素3 → ③ブロックの前回の戻り値と要素4 ...
のように順に値を渡してくれる( 上記の、|before_result, item| の部分 )
なので、そこで好きに処理をして返せば次のブロック実行に値が渡され、結果として全要素を通じた処理ができる。

また、inject の引数として初期値を渡すと、最初に 初期値と要素1 を渡してくれる

inject(初期値) {|before_result, item| 処理 }

①初期値と要素1 → ②ブロックの前回の戻り値と要素2 → ③ブロックの前回の戻り値と要素3 ...

injectを利用して、配列の要素の合計を得ることができる。

arr = [3, 4, 5, 6, 7]

# 合計を得る
total = arr.inject {|before_result, item| before_result + item }
p total  # 25

# 合計を得る(初期値に0を渡す)
total = arr.inject(0) {|before_result, item| before_result + item }
p total  # 25

# 合計の取得に関しては、以下のように簡潔に書ける
total = arr.inject(:+)
p total  # 25

■Arrayクラスでnewする

あまりやらないかも...

arr = Array.new    # []
arr = Array.new(3) # [nil, nil, nil]

arr = Array.new(3, "Ya!") #["Ya!", "Ya!", "Ya!"]
arr[0].concat("!!")       # このとき全部同じオブジェクトなので、値を破壊的に変更すると...
puts arr[1]               # Ya!!! 他の要素も変わる

arr = Array.new(3){"Ya!"} #["Ya!", "Ya!", "Ya!"]
arr[0].concat("!!")       # こちらは同値だが別オブジェクトなので、値を破壊的に変更しても...
puts arr[1]               # Ya! 他の要素の値は変わらない

■配列の複製

・浅いコピー

arr1 = %w[aaa bbb ccc]
arr2 = arr1.dup           # 複製。このとき、それぞれのオブジェクトは同一なので...
arr1[1].upcase!           # 値を破壊的に変更すると...
puts arr2[1]              # BBB もう一方の配列の要素も変更されている

・深いコピー(ネストした配列で試してみる)

arr1 = [%w[aaa bbb ccc], %w[xxx yyy zzz], %w[ooo ppp qqq]]
arr2 = Marshal.load(Marshal.dump(arr1))    # 要素も複製してくれる
arr1[1][1].upcase!                         # 値を破壊的に変更しても...
puts arr2[1][1]                            # yyy もう一方は影響を受けていない

・オブジェクトIDを確認してみる

a = %w[aa, bb]; puts a.object_id, a[1].object_id                     # 1 コピー元の配列を用意
b = a;          puts b.object_id, b[1].object_id                     # 2 参照の代入
c = a.dup;      puts c.object_id, c[1].object_id                     # 3 浅いコピー
d = Marshal.load(Marshal.dump(a));  puts d.object_id, d[1].object_id # 4 深いコピー
  1. aは、70134350252440, 70134350252740
  2. bも、70134350252440, 70134350252740 同じ配列を参照していて、要素も同じオブジェクトを参照している
  3. cは、70134350251120, 70134350252740 配列自体は別オブジェクトを参照しているが、要素は同じオブジェクトを参照している
  4. dは、70134350250840, 70134350250780 配列自体も、要素も別のオブジェクトを参照している

■その他のよく使いそうなメソッド

・配列が空かどうか判定する

arr1 = [1, 2]; p arr1.empty?  # false
arr2 = []; p arr2.empty?      # true

・指定文字区切りで繋いで文字列にする

str = %w[aa bb cc].join(',')  # 引数無しだと、べたっとくっつける

・最大値/最小値

puts arr.max
puts arr.min

・重複排除

arr = %w[aa bb bb cc aa aa dd]
arr2 = arr.uniq  # !を付けると破壊的

・nil の要素を取り除く

arr = [1, nil, 2, nil, nil, 3, 4, 5]
arr2 = arr.compact  # !を付けると破壊的

・配列の全ての要素を編集する

arr = [1, 2, 3, 4, 5]
arr2 = arr.map {|item| item*10}  # collect でも同じ。!を付けると破壊的

・要素を逆に並べ替える

arr_r = arr.reverse

・ランダムに要素を取り出す

puts [1, 2, 3, 4, 5].sample  # 値を一つ
p [1, 2, 3, 4, 5].sample(2)  # 値を複数、配列で。
prgseek
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした