[Ruby]配列に対する繰り返し処理まとめ


よく使う配列のメソッド

each

ブロックに繰り返し要素を渡す。

each
# すべての要素の和を求める
numbers = [1, 2, 3, 4, 5]
sum = 0
numbers.each { |n| sum += n }

sum #=> 15

delete_if

指定した条件に一致する要素を削除する。

delete_if
# 奇数を削除する
numbers = [1, 2, 3, 4, 5]
numbers.delete_if { |n| n.odd? }

numbers #=> [2, 4]

map

ブロックが返す値からなる配列を作成する。

map
# すべて2倍にした配列を返す
numbers = [1, 2, 3, 4, 5]
double_numbers = numbers.map { |n| n * 2 }

double_numbers = [2, 4, 6, 8, 10]

Note:
mapcollectと同義

select

ブロックが真を返す要素からなる配列を作成する。

select
# 奇数を集める
numbers = [1, 2, 3, 4, 5]
odd_numbers = numbers.select { |n| n.odd? }

odd_numbers #=> [1, 3, 5]

Note:
selectfind_allと同義```

reject

ブロックが偽を返す要素からなる配列を作成する。

reject
# 2で割れる数を除く
numbers = [1, 2, 3, 4, 5]
odd_numbers = numbers.reject { |n| n % 2 == 0 }

odd_numbers #=> [1, 3, 5]

find

ブロックが真となる最初の要素を返す。

find
# 最初の偶数を探す
numbers = [1, 2, 3, 4, 5]
even_number = numbers.find { |n| n.even? }

even_number #=> 2

Note:
finddetectと同義
finddetectEnumerableモジュールのメソッド

inject

たたみ込み演算をおこなう。

indect
# すべての要素の和を求める
numbers = [1, 2, 3, 4, 5]
sum = numbers.inject(0) { |result, n| result + n }

sum #=> 15

ブロックの第一引き数は、

  • 1回目のみinjectの引き数
  • 2回目以降は前回のブロックの返り値

ブロックの第二引き数は、配列の各要素が順に入る。

上のコードの処理の流れ

  1. result = 0, n = 1 → 0 + 1 = 1
  2. result = 1, n = 2 → 1 + 2 = 3
  3. result = 3, n = 3 → 3 + 3 = 6
  4. result = 6, n = 4 → 7 + 4 = 10
  5. result = 10, n = 5 → 10 + 5 = 15

Note:
injectreduceと同義

簡潔に書く

メソッドにブロックを渡す代わりに、&:メソッド名を引き数として渡す。


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

# これを
odd_numbers = numbers.select { |n| n.odd? }

# こう
odd_numbers = numbers.select(&:odd?)

次の条件を満たす場合に使うことができる。

条件

  • ブロック引数が1つだけ
  • ブロック内で呼び出すメソッドに引数がない
  • ブロック内は、ブロック引数に対してメソッドを1回呼び出す処理のみ

(追記)正確には、、ブロック引き数が2つでも使える場合があります。
以下コメントからの引用

「メソッドにブロックを渡す代わりに、&:メソッド名を引き数として渡す。」についてですが,

a = [1, 2, 3]
a.inject(&:+) # => 6

という例もあります。
inject(&:+) はほぼinject{ |r, x| r.+(x) }です。
つまり,ブロックパラメーターが二つ以上の場合にもこういう記法が使えます。

添字付きの繰り返し

  • each_with_index
  • with_index

each_with_index

ブロックに繰り返し要素と添字を渡す。

each_with_index
# i には 0, 1, 2 がはいる
animals = ["dog", "cat", "mouse"]
animals.each_with_index { |animal, i| puts "#{i + 1}. #{animal}" }

#=>
# 1. dog
# 2. cat
# 3. mouse

with_index

each以外のメソッドに添え字を付ける場合は、各メソッドとwith_indexメソッドを組み合わせる。

map.with_index
# map に添え字を付ける
animals = ["dog", "cat", "mouse"]
animals.map.with_index { |animal, i| "#{i + 1}. #{animal}" }

#=> ["1. dog", "2. cat", "3. mouse"]

添字を0以外から始める

with_indexに引き数を渡す。

with_index(1)
# 添字を 1 から始める
animals = ["dog", "cat", "mouse"]
animals.map.with_index(1) { |animal, i| "#{i}. #{animal}" }

#=> ["1. dog", "2. cat", "3. mouse"]

配列の配列に対する繰り返し

ブロック引き数に配列が渡ってくるときは、ブロック引き数を複数個用意する。

dimensions = [
    # [縦, 横]
    [10, 20],
    [30, 40],
    [50, 60],
]

# それぞれの面積を求める
areas = []

# これを
dimentions.each { |dimention| areas << dimention[0] * dimention[1] }

# こう
dimentions.each { |length, width| areas << length * width }

areas #=> [200, 1200, 3000]

添字を付ける

with_index + ブロック引き数を()で囲む。

dimensions = [
    # [縦, 横]
    [10, 20],
    [30, 40],
    [50, 60],
]

dimensions.each.with_index do |(length, width), i|
    puts "#{length}, #{width}, #{i}"
end

#=>
# 10, 20, 0
# 30, 40, 1
# 50, 60, 2

参考文献

プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで|技術評論社