>> 連載の目次は こちら!
■とりあえず普通に書く
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
- <=>演算子で判定して小さい順に並べる。数値なら普通に昇順になる。!を付けると破壊的
- sortメソッドでルールを指定する。上記は逆順の例。ブロックの戻り値によって要素を比較して並べる(戻り値が-1ならaが小さい、0なら同じ、1ならbが大きい)
- sort_by のほうが高速。上記は逆順の例。ブロックは要素を単独で処理して返し、最後に全体を<=>演算子で判定して小さい順に並べる。!を付けると破壊的
- 文字列の配列をソートしてみる...
- 昇順。["0101", "bbb", "test", "xyz", "あいうえお"]
- 降順。["あいうえお", "xyz", "test", "bbb", "0101"]
- 多次元配列をソートしてみる...
- 普通にソートすると、それぞれの一つ目の要素でソートされる。上記例だと ["a", 20], ["b", 30], ["c", 10]]
- ブロックを使って、それぞれの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 深いコピー
- aは、70134350252440, 70134350252740
- bも、70134350252440, 70134350252740 同じ配列を参照していて、要素も同じオブジェクトを参照している
- cは、70134350251120, 70134350252740 配列自体は別オブジェクトを参照しているが、要素は同じオブジェクトを参照している
- 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) # 値を複数、配列で。