Array(配列)

配列 (Array) オブジェクト

  • 配列名は、英単語の複数形にする
    • △ : fruit = ["apple", "banana", "grape", "peach"]
    • ○ : fruits = ["apple", "banana", "grape", "peach"]
fruits = ["apple", "banana", "grape", "peach"]

puts fruits[-1] # peach
puts fruits[-2] # grape
  • 配列の範囲外の要素を指定すると nil が返る
l = [1, 2, 3]
p l[5] # nil
  • オブジェクトに対して直接メソッドを呼び出す
    • これは 3.to_s5.times なども同じ仕組み
l = [1, 2, 3]
p l.last

p [1, 2, 3].last

先頭と末尾の要素を取得 first last

fruits = ["apple", "banana", "grape", "peach"]

puts fruits.first # apple
puts fruits.last # peach

要素の追加・削除

要素を追加 unshift push << メソッド

参考画像

  • unshift : 先頭に追加
  • push << : 末尾に追加
l = [2, 3]

p l.push(4) # [2, 3, 4]

p l.unshift(1) # [1, 2, 3, 4]

p l << 5 # [1, 2, 3, 4, 5]

要素を削除 shift pop メソッド

  • shift : 先頭を削除
  • pop : 末尾を削除
l = [1, 2, 3, 4, 5]

l.pop
p l # [1, 2, 3, 4]

l.shift
p l # [2, 3, 4]
  • pop shift は削除した要素を返す
l = [1, 2, 3, 4, 5]

p l.pop # 5
p l # [1, 2, 3, 4]

p l.shift # 1
p l # [2, 3, 4]

ネスト構造の配列に複数の配列を追加

  • 以下のように書くのは冗長
parent = []

child_1 = [1, 2, 3]
child_2 = [4, 5, 6]
child_3 = [7, 8, 9]

parent << child_1
parent << child_2
parent << child_3

p parent
# [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
  • 以下のように書くとスッキリ
parent = []

child_1 = [1, 2, 3]
child_2 = [4, 5, 6]
child_3 = [7, 8, 9]

parent += [child_1, child_2, child_3]

p parent
# [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
  • 要素を取りまとめるために新規の(親)配列を作るなら、いきなり代入すればよい
child_1 = [1, 2, 3]
child_2 = [4, 5, 6]
child_3 = [7, 8, 9]

parent = [child_1, child_2, child_3]

p parent
# [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

配列の足し算 / 引き算

足し算

  • 配列をつなげた新しい配列を返す
l1 = [1, 2, 3]
l2 = [4, 5, 6]

p l1 + l2 # [1, 2, 3, 4, 5, 6]

引き算

  • もとの配列から要素を取り除いた新しい配列を返す
    • もとの配列だけにある要素を得る」際に便利
l1 = [1, 2, 3]
l2 = [4, 1, 3]

p l1 - l2 # [2]

size / length (要素の数をカウント)

l = [1, 2, 3]

p l.size # 3
p l.length # 3

sum (配列の要素の合計値を計算)

l = [1, 2, 3]

p l.sum # 6
  • 配列内の数字の平均値を求める
l = [1, 2, 4]

p l.sum / l.size # 2

sort (並び替え)

  • 昇順に並び替え
p [4, 2, 8].sort
# [2, 4, 8]
  • 文字列の場合、アルファベット順にソートされる
p ["banana", "grape", "apple"].sort
# ["apple", "banana", "grape"]
  • 大文字を含むと、大文字が優先されてソートされる
p ["Banana", "grape", "apple"].sort
# ["Banana", "apple", "grape"]

reverse (逆順に並び替え)

  • 単純に配列の並び順を逆にする
p [4, 2, 8, 5].reverse
# [5, 8, 2, 4]
  • sort とメソッドチェーンにすることで、降順に並び替え
p [4, 2, 8, 5].sort.reverse
# [8, 5, 4, 2]

文字列を逆順に

  • 文字列の並びを逆にすることもできる
    • p で表示すると、日本語がエンコードされた
p "abc".reverse
# "cba"
p "ひらがな".reverse
# "\u306A\u304C\u3089\u3072"
p "漢字".reverse
# "\u5B57\u6F22"
  • ファイルの冒頭で下記を宣言するとOKな様子
    • Encoding.default_internal = __ENCODING__

Windowsにてp "日本語"で"日本語"を表示させる方法 - Qiita

Encoding.default_internal = __ENCODING__

p "abc".reverse
# "cba"
p "ひらがな".reverse
# "ながらひ"
p "漢字".reverse
# "字漢"
  • puts で出力すると問題はない
puts "abc".reverse
# cba
puts "ひらがな".reverse
# ながらひ
puts "漢字".reverse
# 字漢

join (配列の要素を連結 → 文字列にする)

  • ["apple", "banana", "grape"]"apple & banana & grape" として出力したい場合
  • 単に配列をループで回しても意図した通りにならない
    • 最後の配列の要素にも & が付与されるため、条件分岐するなどもうひと手間が必要になる
l = ["apple", "banana", "grape"]

result = ""
l.each { |t| result += t + " & " }
p result
# "apple & banana & grape & "
  • join メソッドを使うと簡単に表現できる
    • 引数
      • 「メソッドへ渡すオブジェクトのこと」
    • 引数に区切り文字を入れる
      • 区切り文字によって連結される
    • 引数なし
      • 区切りなしでそのまま連結される
l = ["apple", "banana", "grape"]

p l.join(" & ")
# "apple & banana & grape"

p l.join
# "applebananagrape"
  • 配列の要素が 1 つでも意図した通りに動く
l = ["apple"]

p l.join(" & ")
# "apple"

split (文字列を分割 → 配列にする)

l = "apple banana grape"

p l.split
# ["apple", "banana", "grape"]
l = "apple & banana & grape"

p l.split(" & ")
# ["apple", "banana", "grape"]

map (配列の各要素を変換 → 新たな配列を作る)

l = [1, 2, 3]

result = l.map do |n|
  n * 2
end

p result
# [2, 4, 6]
l = [100, 200, 300]

result = l.map do |n|
  "#{n} yen"
end

p result
# ["100 yen", "200 yen", "300 yen"]
  • do ~ end を省略した記法
l = [100, 200, 300]

result = l.map { |n| "#{n} yen" }
p result
# ["100 yen", "200 yen", "300 yen"]

シンボルを使った簡略記法

  • ブロック内で特定のメソッドを呼び出すだけの場合(←重要)」に限り、シンボルを使った簡略記法もある
    • 以下は result = l.map { |t| t.reverse } と等価
l = ["apple", "banana", "grape"]

result = l.map(&:reverse)
# result = l.map { |t| t.reverse }

p result
# ["elppa", "ananab", "eparg"]

eachmap の違い

  • 戻り値」の違い
    • each → 元の配列のまま
    • map → 処理結果を反映した配列を返す
l = [1, 2, 3]

r1 = l.each { |n| n * 2 }
p r1
# [1, 2, 3]

r2 = l.map { |n| n * 2 }
p r2
# [2, 4, 6]
  • each で処理結果を反映した配列を得ようとすると
    • 空の配列を用意 → ループを回して格納していく必要がる
l = [1, 2, 3]
r = []

l.each { |n| r << n * 2 }
p r
  • eachは繰り返し処理に使う
  • mapは繰り返し処理の結果を配列にしたいときに使う

【Ruby】eachとmapの違い - Qiita

break で処理を中断

l = [1, 2, 3]

l.each do |n|
  break if n == 2
  p n
end

next で処理をスキップ

l = [1, 2, 3]

l.each do |n|
  next if n == 2
  p n
end
# 1
# 3

範囲 Range

l = (3..5)

puts l
# 3..5
puts l.class
# Range

l.each {|i| p i}
# 3
# 4
# 5

配列のスライス

l = [1, 2, 3, 4, 5]

p l[1..3]
# [2, 3, 4]

p l[2..-1]
# [3, 4, 5]

多重配列

ループ

  • each を入れ子にする方法があるが
array = [[1,2,3], [4,5,6], [7,8,9]]
array.each do |ary|
  ary.each do |elm|
    puts elm
  end
end

ブロック引数に複数指定すると中の配列の要素がそのまま取り出せる

二次元配列は1回のeachで回せる

array = [[1,2,3], [4,5,6], [7,8,9]]
array.each do |elm1, elm2, elm3|
  puts elm1, elm2, elm3
end
# 1, 2, 3, 4, 5, 6, 7, 8, 9

zip メソッド

p [1,2,3].zip([4,5,6], [7,8,9])
# [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

任意の要素を基準にソート sort_by

  • sort でも実現できるが、sort_by を使った方が感覚的に分かりやすい
  • 処理速度も sort_by の方が良さげ
l = [[1, 3, 185, 4], [5, 4, 64, 2], [2, 4, 504, 8]]

p l.sort_by {|x| x[2] }.reverse
# [[2, 4, 504, 8], [1, 3, 185, 4], [5, 4, 64, 2]]