【初級者向け】rubyで効率のよいコードの書き方例いろいろ【Enumerable編】

# `Enumerable`って?

繰り返しを行うためのMix-in
`Array` とか、`Hash` はこれをMix-inしている

**繰り返しを処理するときは、まずはこのリファレンスを読んで、使えそうなものは無いかな? と調べる習慣をつけると良いとおもいます**

- [module Enumerable (Ruby 2.4.0)](https://docs.ruby-lang.org/ja/latest/class/Enumerable.html)

`Enumerable`を扱う場合、初級者は`#each`の中でぐたぐたコードを書いてしまうことが多いのですが、
`#each`以外のメソッドを上手く使うことでもっとスッキリしたコードがかけますよ!


# 配列を処理した結果を他の配列に入れたい場合は、`#map`を使おう

[instance method Enumerable#collect (Ruby 2.4.0)](https://docs.ruby-lang.org/ja/latest/method/Enumerable/i/collect.html)

悪い例

```.ruby
# 配列の値を10倍した配列を取得する
result = []
[1,2,3].each do |i|
    result << i*10
end
```

良い例

```.ruby
# 配列の値を10倍した配列を取得する
result = [1,2,3].map do |i|
  i*10
end
```

# 状況に応じて配列に値を抽出するならば、`#select`を使おう

[instance method Enumerable#find_all (Ruby 2.4.0)](https://docs.ruby-lang.org/ja/latest/method/Enumerable/i/select.html)


- `select` はblockの結果が**真**である要素を全て含む配列を返す
- 逆に `reject` はblockの結果が**偽**である要素を全て含む配列を返す


## 悪い例

```.ruby
# 偶数を抽出する
result = []
[1,2,3].each do |i|
    result << i if i % 2 == 0
end
```

## 良い例

```.ruby
# 偶数を抽出する
result = [1,2,3].select do |i|
  i % 2 == 0
end
```

# 配列を使って処理した結果が欲しい場合は、`#inject` を使おう

[instance method Enumerable#inject (Ruby 2.4.0)](https://docs.ruby-lang.org/ja/latest/method/Enumerable/i/inject.html)


たとえば、配列の合計が欲しいときとか

- `#inject` は畳み込み演算を行うメソッド
- 引数に初期値を入れる
- blockの戻り値が次の第二引数に、引き渡される

## 悪い例

```.ruby
# 配列の値を合計する
sum = 0
[1,2,3].each do |i|
  sum += i
end
```

## 良い例

```.ruby
# 配列の値を合計する
[1,2,3].inject(0) do |i, sum|
  sum + i
end
```

# `#inject`を使うためだけに、blockの戻り値を明示的にするくらいなら、`#each_with_object` を使おう

[instance method Enumerable#each_with_object (Ruby 2.4.0)](https://docs.ruby-lang.org/ja/latest/method/Enumerable/i/each_with_object.html)

- injectは、各要素が力を合わせて一つのオブジェクトを作る
- each_with_objectは、ターゲットとなるオブジェクトに対して、各要素を作用させる

## 悪い例

```.ruby
# 配列からhashを作る
[[:a,1], [:b,2], [:c,3]].inject({}) do |hash, (k,v)|
  hash[k] = v
  hash
end
```

## 良い例

```.ruby
# 配列からhashを作る
# injectと、blockの引数の順が逆なので注意!
[[:a,1], [:b,2], [:c,3]].each_with_object({}) do |(k,v), hash|
  hash[k] = v
end
```

パクリ元 : [injectとeach_with_objectって何が違うのさ？ - Qiita](http://qiita.com/Kta-M/items/c9781e09d96601687767)


# indexが欲しい場合は、 `#with_index` を使おう!

[instance method Enumerator#with_index (Ruby 2.4.0)](https://docs.ruby-lang.org/ja/latest/method/Enumerator/i/with_index.html)

- `#map`などの繰り返し処理の中で、indexが欲しいときとか
- `#each_with_index` は今後放置されるとMatzが言っているらしい
    - [each_with_index か each.with_index か - ぱろっと・すたじお](http://parrot.hatenadiary.jp/entry/20090908/1252382194)

## 悪い例

```.ruby
# 値とindexの積の配列を作る
result = []
[10, 10, 10].each_with_index do |v, i|
  result << v*i
end
```

## 良い例

```.ruby
# 値とindexの積の配列を作る
[10, 10, 10].map.with_index do |v, i|
  v*i
end
```

ちなみに、`#with_index` は引数で何番目のindexから始めるか指定できるので便利
(ヘッダ情報を飛ばしたいときとか)


# 各要素に単一のメソッドを実行するだけの場合は`&`記法を使おう

`&:メソッド名`を繰り返しに渡すことで、そのメソッドが各要素に実行される

## 悪い例

```.ruby
# 奇数を抽出
[1,2,3].select do |v|
  v.odd?
end
```

## 良い例

```.ruby
# 奇数を抽出
[1,2,3].select(&:odd?)
```

## メソッドの引数に要素を渡したい場合にも使える

## 悪い例

```.ruby
# 全ての値をputsする
[1,2,3].each do |v|
  puts v
end
```
## 良い例

```.ruby
# 全ての値をputsする
[1,2,3].each(&method(:puts))
```

くわしくはこちらを

- [&演算子と、procと、Object#method について理解しなおす - Qiita](http://qiita.com/kasei-san/items/0392097791d3a5998216)


# 処理元が配列or非配列かはっきりしない場合、配列にしてから`#flatten`しよう

if文が不要になる

## 悪い例

```.ruby
# 合計値を返す関数
# ただし、引数に配列ではなく数値が来る場合がある。その場合はその値をそのまま返す
def sum(v)
  if v.is_a?(Enumerable)
    v.inject(&:+)
  else
    v
  end
end

sum(100)       # => 100
sum([100,200]) # => 100
```

## 良い例

```.ruby
# 合計値を返す関数
# ただし、引数に配列ではなく数値が来る場合がある。その場合はその値をそのまま返す
def sum(v)
  [v].flatten.inject(&:+)
end

sum(100)       # => 100
sum([100,200]) # => 300
```

_オブジェクト指向的には、そもそもこんな風に`v`のクラスに依存した実装にするのはあまり良くないかも..._