Ruby

[Ruby]Enumerableモジュールのメソッド

1年くらい前にRubyの勉強のためにEnumerableモジュールの便利メソッドをまとめたもの

参考
パーフェクトRuby
https://docs.ruby-lang.org/ja/latest/class/Enumerable.html

繰り返し

each_with_index
繰り返しの度にインクリメントされる値と一緒に繰り返す

%w(Alpha Bravo Charlie).each_with_index do |code, index|
    puts "#{index}: #{code}"
end # => "0: Alpha" "1: Bravo" "2: Charlie"

reverse_each
末尾から逆順に繰り返す

(1..3).reverse_each { |val| puts val } # => 3 2 1

each_slice
要素をn個ずつ区切って繰り返す

(1..5).each_slice 2 { |a, b| p [a, b] }
# => [1, 2] [3, 4] [5, nil]

each_cons
n個の連続した要素を1つずつずらしながら繰り返す

(1..4).each_cons 3 { |a, b, c| p [a, b, c] }
# => [1, 2, 3] [2, 3, 4] nil

cycle
レシーバの要素を先頭から末尾まで繰り返しを永遠に行う

(1..3).cycle { |n| p n } # => 1 2 3 1 2 …

プロックを評価した結果の配列

map
ブロックの戻り値を集めた配列を返す

['ruby', 'rails'].map { |str| str.upcase } # => ["RUBY", "RAILS"]

ブロックの返り値を連結した配列

flat_map
各要素をブロックに渡し、その返り値を連結した配列を返す

[[1,2], [3,4]].flat_map{|i| i.map{|j| j * 2 }} # => [2,4,6,8]

複数の要素を組み合わせる

zip
複数の要素を組み合わせて配列の配列を作る

nums = (1..4)
langs = %w(ruby python java)
arr = [true, false]

nums.zip(arr, langs) 
# => [[1, true, "ruby"], [2, false, "python"], [3, nil, "java"], [4, nil, nil]]

arr.zip(nums, langs)
# => [[true, 1, "ruby"], [false, 2, "python"]]

要素が特定の条件を満たしているか

all?
すべての要素が真ならtrue
none?
すべての要素が偽ならtrue
any?
1つでも要素が真ならtrue
one?
1つだけ要素が真ならtrue

[true, true, true].all? # => true
[true, true, false].all? # => false

[false, false, false].none? # => true
[true, true, false].none? # => false

[false, false, true].any? # => true
[false, false, false].any? # => false

[false, false, true].one? # => true
[true, true, false].one? # => false

部分的な要素の取得

grep
正規表現にマッチする各要素を検索する

 %w(Alpha Bravo Charlie).grep(/l/i)
# => ["Alpha", "Charlie"]

detect(find)
最初に真となった要素を返す

select
真となった要素をすべて返す

array = [4, 6, 2, 1]

array.detect {|v| v.even? } # => 4
array.select {|v| v.even? } # => 4 6 2

take
先頭から任意の数の要素を配列として返す

drop
先頭から任意の数の要素をスキップして残りの要素を配列として返す

array = %w(a b c d e)

array.take(2) # => ["a", "b"]
array.drop(2) # => ["c", "d", "e"]

take_while
ブロックが最初に偽を返すまでの要素の配列を返す

drop_while
ブロックが最初に偽を返してからの要素の配列を返す

array = %w(a b c d e)

array.take_while { |n| n != 'c' } # => ["a", "b"]
array.drop_while { |n| n != 'c' } # => ["c", "d", "e"]

畳み込み演算

inject(reduce)
ブロックを受け取りブロックは各要素に対して実行する

[1, 2, 5].inject do |result, num|
    result + num
end # => 8

[1, 2, 5].inject(:+) # => 8

繰り返しとオブジェクトの更新

each_with_object
引数のオブジェクトを繰り返しの度にブロック引数として渡し最終的には戻り値とする。

numbers = [4, 3, 9]
numbers.each_with_object([]) do |number, result|
    result << number + 3
end

要素のグルーピング

group_by
ブロックを評価した結果の戻り値をキーにして新しいハッシュを返す
同一条件ごとに総まとめする。

array = [1, 'a', 2, 'b', 3, 'c']

array.group_by {|val| val.class }
# => {Integer=>[1, 2, 3], String=>["a", "b", "c"]}

partition
ブロックの戻り値が真か偽かによって分けた2つのグループを配列にして返す

array = [1, 'a', 2, 'b', 3, 'c']

array.partition {|val| val.is_a?(String) }
# => [["a", "b", "c"], [1, 2, 3]]

chunk
ブロックの戻り値ごとに要素の並びをまとめた配列を作る。
同一条件ごちに部分まとめする。

array = [1, 'a', 2, 'b', 3, 'c']

enum = array.chunk {|val| val.class }
enum.each {|key, arr| puts "#{key}: #{arr}" }

# 出力結果
Integer: [1]
String: ["a"]
Integer: [2]
String: ["b"]
Integer: [3]
String: ["c"]

slice_before
マッチ要素またはブロックが真を返した要素から次にマッチ or 真の要素までをチャンクしたものを返す。

[0, 2, 4, 1, 8, 3, 5, 7, 10].slice_before(&:even?).to_a
# => [[0], [2], [4, 1], [8, 3, 5, 7], [10]]

最小値と最大値

min
最小値を返す

max
最大値を返す

minmax
最小値と最大値を配列にして返す

range = (1..4)

range.min # => 1
range.max # => 4
range.minmax # => [1, 4]

min_by, max_by, minmax_by
ブロックを評価した結果で比較する

array = %w(a bb ccc)

array.min_by{|str| str.length } # => "a"
array.max_by{|str| str.length } # => "ccc"
array.minmax_by{|str| str.length } # => ["a", "ccc"]

ソート

sort, sort_by
ソートした結果を新しい配列で返す

array = %w(cc a bbb)

# 要素でソート
array.sort # => ["a", "bbb", "cc"]

# 要素の長さでソート1
array.sort {|a, b| a.length <=> b.length } # => ["a", "cc", "bbb"]

# 要素の長さでソート2
array.sort_by { |str| str.length } # => ["a", "cc", "bbb"]

上記の場合、sort_byのほうが高速

sortは比較の度に2つの要素に対してメソッドを呼んでいる。
sort_byは要素ごとにメソッドを1回だけ呼んでいる。