Enumerable で一気にドンッ!!
Ruby の Enuerble モジュールについて。
map や reduce など、 Enumerable モジュールのメソッドは、それをincludeしているコレクションが用意している each で順次処理している(コレクションとは、配列やハッシュのこと)。
けど、コードの見た目上は、ループされているイメージではなく、「一気にドンッ!!」と処理しているように表現できる。
同じ結果を得るのに、5行で書いていたところを、1行で書けるというのは、善だ。なぜなら、視界に多くが納まることは、思考を助ける。
問題は、構文を知っているか、知らないかだけ。なので、知っておいた方がいい Enumerable のメソッドをまとめる。
1. それぞれの要素に、同じ作用を与える
コレクションに対して、それぞれの要素に与えたい作用をブロックで与えると、その結果を配列で得られるメソッド。
ちなみ、Enuerble のメソッドには、同じことをするが、異なる名前のものがいくつか存在する。これは、Smalltalk の流れを引き継いだか、Lisp の流れを引き継いだかによるもの。
この場合、Smalltalk の流れを引き継いだのが、collect 。Lisp の流れを引き継いだのが、map。
[1, 2, 3].collect {|x| x + 1} # => [2, 3, 4]
[1, 2, 3].map {|x| x + 1} # => [2, 3, 4]
2. 全ての要素をまとめて、ひとつの結果を得る
コレクションに対して、それぞれの要素をとりまとめるルールをブロックで与えると、その結果をスカラー(単一の値)で得るメソッド。
同じく、2つの名前があり、Smalltalk の流れを引き継いだのが、inject 。Lisp の流れを引き継いだのが、reduce。
[1, 2, 3].inject {|sum, n| sum + n} # => 6
[1, 2, 3].reduce {|sum, n| sum + n} # => 6
# 単純な演算であれば、略記できる
[1, 2, 3].reduce(:+) # => 6
# メソッドへの引数として、初期値を与えられる
[1, 2, 3].reduce(10) {|sum, n| sum + n} # => 16
3. 特定の条件に合致するもののみを抽出する
コレクションの中から、抽出したい条件をブロックで与えると、合致するものを返す。
これも名前が2つあるが、コレクションがハッシュの場合の結果が異なる。
- find_all : コレクションがハッシュの場合でも、結果は配列
- select : コレクションがハッシュの場合は、結果もハッシュ
scores = { 'Alice' => 50, 'Bob' => 60, 'Carol' => 90, 'David' => 40 }
scores.find_all {|k, v| v >= 60 } # => [["Bob", 60], ["Carol", 90]]
scores.select {|k, v| v >= 60 } # => {"Bob"=>60, "Carol"=>90}
4. 正規表現にマッチする要素に対して、同じ作用を与える
- grep(/正規表現/) {ブロック}
ブロックを与えない場合、コレクションの中から、引数の正規表現にマッチする要素を得る。
ブロックを与えた場合、コレクションの中から正規表現にマッチする要素を抽出し、それぞれに対するブロックの結果を得る。
a = ["foo", "bar", "baz"]
# bで始まるものを得る
a.grep(/^b/) # => ["bar", "baz"]
# bで始まるものの文字数を得る
a.grep(/^b/) {|x| x.size} # => [3, 3]
5. 全ての要素が条件に合致するか? / 条件に合致する要素が1つでも含まれるか?
- all? : コレクションの全てが、ブロックで指定する条件を満たすか?
- any? : ブロックで指定する条件を満たす要素が、コレクションに1つでも含まれているか?
# 全ての要素が条件に合致するか?
[2, 4, 6].all? {|x| x % 2 == 0} # => true
# 条件に合致する要素が1つでも含まれるか?
[3, 5, 6].any? {|x| x % 2 == 0} # => true
6. 最大/最小の要素を得る
- max : コレクションの中で最大の要素
- min : コレクションの中で最小の要素
scores = { 'Alice' => 50, 'Bob' => 60, 'Carol' => 90, 'David' => 40 }
# キーの最大
scores.max # => ["David", 40]
# (これも)キーの最大
scores.max {|x, y| x[0] <=> y[0]} # => ["David", 40]
# 値の最大
scores.max {|x, y| x[1] <=> y[1]} # => ["Carol", 90]
7. 要素を並べ替える
コレクションを並び替える。ブロックの適用の仕方により、次の2つがある。
- sort : 要素の比較方法をブロックで与えることができる。
- sort_by : 並び替えのキーをブロックで与えることができる。
- reverse : 逆順で並べる (sortの反対)
a = [7, 6, 5, 4, 3, 2, 1]
# 全て同じ結果
a.sort #=> [1, 2, 3, 4, 5, 6, 7]
a.sort {|x, y| x <=> y} #=> [1, 2, 3, 4, 5, 6, 7]
a.sort_by #=> [1, 2, 3, 4, 5, 6, 7]
a.sort_by {|x| x} #=> [1, 2, 3, 4, 5, 6, 7]
# 逆順で並べる
a.reverse #=> [1, 2, 3, 4, 5, 6, 7]
h = { 3 => "a",1 => "b", 2 => "c" }
# ハッシュをキーで並び替える
h.sort_by {|k, v| v} # => [{3=>"a", 1=>"b", 2=>"c"}]
# ハッシュを値で並び替える
h.sort_by {|k, v| k} # => [{3=>"a", 1=>"b", 2=>"c"}]