【Ruby/Rails】シンプルに書ける小技メモ

自分がほえーと思った小技などをメモしておく。
全てがベストプラクティスというわけではないので、状況に応じて使い分けたい。

（4/18追記）
コメントより、「もっと簡単な書き方がある」とご教示いただきましたので追加しました。ありがとうございました。


## Safe Navigation Operator（ぼっち演算子）

```rb:Bad
articles = user.articles if user
```
```rb:Good
articles = user&.articles
```

`user.articles`だと、`user`がnilになる場合にNoMethodErrorエラーが出る。そのため、userがあるかどうかifで判定しようとするが、ぼっち演算子を使うとシンプルに表記できる。
`user`がnilの場合は`user&.articles`もnilになる。

`user&.articles&.take&.tag&.name`のようにメソッドチェーンが長くなってもif文がネストすることがない。
ただし、`user&.articles.take.tag.name`のようにすると、結局`articles.take`のところでNoMethodErrorになるので注意。
[NEWS for Ruby 2.3.0 (Ruby 2.7.0 リファレンスマニュアル)](https://docs.ruby-lang.org/ja/latest/doc/news=2f2_3_0.html)


## Hash#dig

```rb
data = { user: { name: '太郎',
                 tag: { id: 1,
                        name: "タグ" }}}
```
```rb:[]をつなげた場合
data[:user][:tag][:name]
=> "タグ"

data[:user][:article][:name] # :articleが存在しない=>nilの場合
Traceback (most recent call last):
        1: from (irb):9
NoMethodError (undefined method '[]' for nil:NilClass)
```
```rb:digを用いた場合
data.dig(:user, :tag, :name)
=> "タグ"

data.dig(:user, :article, :name)
=> nil
```
ネストされたハッシュやjsonからデータを取り出したいときに使える。途中のデータがnilのときに自動的にnilを返してくれる。
ぼっち演算子とイメージは少し似ている。
[Hash#dig (Ruby 2.7.0 リファレンスマニュアル)](https://docs.ruby-lang.org/ja/latest/method/Hash/i/dig.html)

## hoge.presence || 'hogeは何も書いてないよ'

```rb
if hoge.present?
  hoge
else
  'hogeは何も書いてないよ'
end

hoge.present? ? hoge : 'hogeは何も書いてないよ'
```

↓

```rb
hoge.presence || 'hogeは何も書いてないよ'
```

`presence`は該当のオブジェクトがあればそれを返し、blankな値の場合は`nil`を返す。

```rb
[4] pry(main)> 'aaa'.presence
=> 'aaa'
[5] pry(main)> nil.presence
=> nil
[6] pry(main)> ''.presence
=> nil
[7] pry(main)> false.presence
=> nil
```


## render(objects) || message

```erb
<% if @articles %>
  <%= render @articles %>
<% else %>
  <%= "記事はありません" %>
<% end %>
```

```erb
<%= render(@articles) || "記事はありません" %>
```

記事がないときのメッセージを`if`を使わず簡単に表示できる。
`a = b || c`の応用っぽい動き。


## map(&:method)
```rb
numbers = ["1", "2", "3"]

numbers.map{ |n| n.to_i }
↓
numbers.map(&:to_i)
```

`map`や`each`のような配列のメソッドの書き方。`(&:メソッド名)`で中身のオブジェクトそれぞれにメソッドを実行できる。
[&演算子と、procと、Object#method について理解しなおす](https://qiita.com/kasei-san/items/0392097791d3a5998216)


## if array.each do end.empty? end

```rb
if articles.empty?
  articles.each do |article|
    # ループ処理
  end
else
   # articlesに何も入っていない時の処理
end
```
```rb
if articles.each do |article|
  # ループ処理
end.empty?
  # articlesに何も入っていない時の処理
end
```

`articles ... end`を固まりとして、`empty?`メソッドで判定している。

これの是非については、[rubyでeachをする際に、中身が空であれば別の処理をさせる](https://qiita.com/Tterima/items/eaa429a7bb90f1fdab6b)のコメント欄で議論がされていました。あまりわかりやすい書き方ではないようです。

## 配列をフィルタリングする

元々nilが入っている配列からnilを除く場合は`compact`を使う。

```rb
[1, 2, nil, 4, nil, 6].compact
=> [1, 2, 4, 6]
```

[Array#compact (Ruby 2.7.0 リファレンスマニュアル)](https://docs.ruby-lang.org/ja/latest/method/Array/i/compact.html)

### Enumerable#select/filter
filterはselectのエイリアス（Ruby2.6から）

```rb
tags.select{ |tag| tags.reload.include?(tag) }
```
条件などでフィルタリングしたい場合は、`select`を使うと、`{}`内の条件に合うものだけを格納した配列として返す。
[Enumerable#filter (Ruby 2.7.0 リファレンスマニュアル)](https://docs.ruby-lang.org/ja/latest/method/Enumerable/i/filter.html)

### Enumerable#filter_map（Ruby2.7から）

条件でフィルタリングした後で配列の中身に編集を加える場合はこのように書く。
（例は不適かもしれません）

```rb
tags.map do |tag|
  tag.user if tags.reload.include?(tag)
end.compact
```

Ruby2.7では`filter_map`というメソッドが追加されたので、このように書くことができる。

```rb
tags.filter_map { |tag| tag.user if tags.reload.include?(tag) }
```

[Module: Enumerable (Ruby 2.7.0)](https://ruby-doc.org/core-2.7.0/Enumerable.html#method-i-filter_map)
[Ruby 2.7がEnumerable＃filter_mapを追加| Saelounブログ](https://blog.saeloun.com/2019/05/25/ruby-2-7-enumerable-filter-map.html)