😍
【Rails】compact / compact_blank / empty? の使い分け
目次
- はじめに
- なぜこれらのメソッドを理解する必要があるのか
- compact - nil だけを削除する
- compact_blank - 空っぽなものすべてを削除する
- empty? - 空かどうかを判定する
- 3つのメソッドの比較表
- 実務でよくあるユースケース
- よくある間違いと注意点
- まとめ
はじめに
Railsアプリケーション開発では、配列やハッシュから不要な値を取り除く処理が頻繁に発生します。この記事では、似ているようで動作が異なる compact、compact_blank、empty? の3つのメソッドについて、実務を想定した使い分けを詳しく解説します。
なぜこれらのメソッドを理解する必要があるのか
実務では以下のような場面に日常的に遭遇します:
- フォーム送信時: ユーザーが入力しなかった項目が空文字やnilとして送られてくる
- API連携時: 外部APIからのレスポンスに不要な空データが含まれている
- バリデーション前: データが空かどうかで処理を分岐させる必要がある
- 検索機能: 空の検索条件を除外してクエリを構築する
これらの状況に応じて、適切なメソッドを選択することでコードの品質と保守性が向上します。
compact - nil だけを削除する
基本動作
compact は nil のみを配列やハッシュから取り除くRuby標準メソッドです。
# 配列の場合
array = [1, nil, 2, "", [], false, 0]
array.compact
# => [1, 2, "", [], false, 0]
重要なポイント
- ✅
nilは削除される - ❌ 空文字
""は残る - ❌ 空配列
[]は残る - ❌
falseは残る(false は nil ではない!) - ❌
0は残る
実務での使用例
ケース1: データベースのカラムに nil だけを許可しない場合
# ユーザー登録時の追加情報(空文字は許可するが nil は保存したくない)
user_attributes = {
name: "太郎",
nickname: nil,
bio: "",
website: nil
}
user_attributes.compact
# => { name: "太郎", bio: "" }
# nickname と website(nil)は削除されるが、bio(空文字)は残る
ケース2: 配列の要素から nil だけを除去
# APIから取得したIDリストに nil が混ざっている場合
user_ids = [1, 2, nil, 3, nil, 4]
User.where(id: user_ids.compact)
# => SELECT * FROM users WHERE id IN (1, 2, 3, 4)
compact と compact! の違い
array = [1, nil, 2]
# compact(非破壊的)- 元の配列は変更されない
result = array.compact
# result => [1, 2]
# array => [1, nil, 2](元のまま)
# compact!(破壊的)- 元の配列が変更される
array.compact!
# array => [1, 2](変更される)
compact_blank - 空っぽなものすべてを削除する
基本動作
compact_blank は Rails が追加した便利メソッドで、blank? が true になるものをすべて取り除きます。
blank? とは?
Railsの blank? メソッドは以下を true と判定します:
nil- 空文字
"" - 空白文字列
" "(スペースのみ) - 空配列
[] - 空ハッシュ
{}
⚠️ 注意: false と 0 は blank ではありません!
使用例
# 配列の場合
array = [1, nil, 2, "", " ", [], false, 0]
array.compact_blank
# => [1, 2, false, 0]
# ハッシュの場合
params = {
name: "太郎",
email: "",
age: nil,
tags: [],
active: false,
score: 0
}
params.compact_blank
# => { name: "太郎", active: false, score: 0 }
実務での使用例
ケース1: フォームパラメータのクリーニング(最頻出!)
# app/controllers/users_controller.rb
def create
# フォームから送信されたパラメータには空文字が含まれることが多い
# compact_blank で一括削除すると便利
cleaned_params = user_params.compact_blank
@user = User.create(cleaned_params)
end
private
def user_params
params.require(:user).permit(:name, :email, :bio, :website, :phone)
end
実際の動作イメージ:
# フォームからの送信データ
{
name: "太郎",
email: "taro@example.com",
bio: "", # 入力されなかった
website: "", # 入力されなかった
phone: "090-1234-5678"
}
# compact_blank 後
{
name: "太郎",
email: "taro@example.com",
phone: "090-1234-5678"
}
# 空文字の bio と website が削除される
ケース2: 検索条件の構築
# app/controllers/products_controller.rb
def index
# 検索フォームから送られた条件で絞り込み
search_params = {
category: params[:category], # => ""(未選択)
min_price: params[:min_price], # => nil
max_price: params[:max_price], # => "5000"
keyword: params[:keyword] # => "Ruby"
}.compact_blank
# => { max_price: "5000", keyword: "Ruby" }
# 空の条件が自動的に除外されるので、クエリがシンプルになる
@products = Product.where(search_params)
end
ケース3: JSON APIレスポンスのクリーニング
# app/controllers/api/v1/users_controller.rb
def show
user_data = {
id: @user.id,
name: @user.name,
bio: @user.bio, # => nil の可能性
website: @user.website, # => "" の可能性
avatar_url: @user.avatar_url
}.compact_blank
render json: user_data
# 空のフィールドを含まないクリーンなJSONを返せる
end
empty? - 空かどうかを判定する
基本動作
empty? は、オブジェクトが空かどうかを判定するメソッドで、真偽値(true / false)を返します。
⚠️ 重要: compact や compact_blank と違い、要素を削除しません。
使用例
# 文字列
"".empty? # => true
"hello".empty? # => false
# 配列
[].empty? # => true
[1, 2].empty? # => false
# ハッシュ
{}.empty? # => true
{ a: 1 }.empty? # => false
実務での使用例
ケース1: 条件分岐での使用
# app/controllers/orders_controller.rb
def create
@cart_items = current_user.cart_items
if @cart_items.empty?
redirect_to products_path, alert: "カートが空です"
return
end
# カートに商品がある場合の処理
@order = Order.create_from_cart(@cart_items)
end
ケース2: ビューでの条件表示
<!-- app/views/users/show.html.erb -->
<% if @user.posts.empty? %>
<p>まだ投稿がありません</p>
<% else %>
<% @user.posts.each do |post| %>
<%= render post %>
<% end %>
<% end %>
ケース3: バリデーション前のチェック
# app/models/order.rb
class Order < ApplicationRecord
validate :must_have_items
private
def must_have_items
if order_items.empty?
errors.add(:base, "注文には最低1つの商品が必要です")
end
end
end
empty? vs blank? vs present?
Railsには似たようなメソッドが他にもあります:
value = ""
# empty? - 空かどうか(Rubyネイティブ)
value.empty? # => true
# blank? - 空っぽかどうか(Rails拡張、より広範囲)
value.blank? # => true
# present? - 値があるかどうか(blank? の逆)
value.present? # => false
# nil の場合の違い
nil.empty? # => NoMethodError(nil には empty? がない!)
nil.blank? # => true
nil.present? # => false
# 実務では blank? / present? を使うことが多い
if params[:email].present?
# メールアドレスが入力されている場合の処理
end
3つのメソッドの比較表
| メソッド | 対象 | 動作 | 返り値 | 削除されるもの |
|---|---|---|---|---|
compact |
Array / Hash |
nil を削除 |
新しい配列/ハッシュ |
nil のみ |
compact_blank |
Array / Hash |
blank? なものを削除 |
新しい配列/ハッシュ |
nil, "", " ", [], {}
|
empty? |
単一オブジェクト | 空かどうか判定 |
true / false
|
削除しない(判定のみ) |
値ごとの動作比較
| 値 | compact で削除? | compact_blank で削除? | empty? の結果 |
|---|---|---|---|
nil |
✅ Yes | ✅ Yes | エラー |
"" |
❌ No | ✅ Yes | true |
" " |
❌ No | ✅ Yes | false |
[] |
❌ No | ✅ Yes | true |
{} |
❌ No | ✅ Yes | true |
false |
❌ No | ❌ No | エラー |
0 |
❌ No | ❌ No | エラー |
実務でよくあるユースケース
ユースケース1: フォーム送信データの処理
# 悪い例 ❌
def create
@article = Article.new(article_params)
# 空文字がそのまま保存されてしまう
@article.save
end
# 良い例 ✅
def create
cleaned_params = article_params.compact_blank
@article = Article.new(cleaned_params)
@article.save
end
ユースケース2: 複数の検索条件
def search
conditions = {}
# 悪い例 ❌ - 一つ一つチェックして追加
conditions[:category] = params[:category] if params[:category].present?
conditions[:status] = params[:status] if params[:status].present?
conditions[:user_id] = params[:user_id] if params[:user_id].present?
# 良い例 ✅ - compact_blank で一括処理
conditions = {
category: params[:category],
status: params[:status],
user_id: params[:user_id]
}.compact_blank
@results = Product.where(conditions)
end
ユースケース3: 配列の空チェックと処理
# 悪い例 ❌ - エラーの可能性
def process_items
items = fetch_items
items.each do |item| # items が nil ならエラー
process(item)
end
end
# 良い例 ✅ - 安全な処理
def process_items
items = fetch_items
return if items.nil? || items.empty? # ガード節
items.compact.each do |item| # nil も除外
process(item)
end
end
ユースケース4: APIレスポンスの整形
# app/serializers/user_serializer.rb
class UserSerializer
def as_json
{
id: user.id,
name: user.name,
email: user.email,
profile: {
bio: user.bio,
website: user.website,
twitter: user.twitter,
github: user.github
}.compact_blank, # 空のSNSアカウントは含めない
stats: {
posts_count: user.posts.count,
followers_count: user.followers.count
}
}
end
end
よくある間違いと注意点
間違い1: false を削除してしまう
# 危険な例 ❌
settings = {
notifications: false, # 通知オフ
public_profile: true,
auto_save: false # 自動保存オフ
}.compact_blank
# => { public_profile: true }
# false が削除されてしまい、設定が失われる!
# 正しい例 ✅
# false も有効な値なので compact_blank は使わない
settings = {
notifications: false,
public_profile: true,
auto_save: false
}.compact # nil だけ削除
間違い2: nil に対して empty? を呼ぶ
# エラーになる例 ❌
value = nil
value.empty? # => NoMethodError
# 正しい例 ✅
value.nil? || value.empty? # nil チェックを先に
# または Rails なら
value.blank? # nil でも安全
間違い3: 破壊的メソッドの誤用
# 意図しない動作 ❌
original = [1, nil, 2]
filtered = original.compact! # 元の配列を変更してしまう
puts original # => [1, 2](変更されている)
# 正しい例 ✅
original = [1, nil, 2]
filtered = original.compact # 新しい配列を返す
puts original # => [1, nil, 2](元のまま)
puts filtered # => [1, 2]
間違い4: 数値の 0 を削除してしまう
# 問題のある例 ❌
scores = [100, 0, 50, nil, 80]
valid_scores = scores.compact_blank
# compact_blank は 0 を削除しない(0 は blank ではない)
# => [100, 0, 50, 80] # 正しく動作
# でも、0 を「無効なスコア」として扱いたい場合は別の方法が必要
valid_scores = scores.compact.reject(&:zero?)
# => [100, 50, 80]
まとめ
使い分けの判断フロー
データをクリーニングしたい?
├─ Yes → nil だけ削除したい?
│ ├─ Yes → compact を使う
│ └─ No → 空文字・空配列も削除したい?
│ └─ Yes → compact_blank を使う
│
└─ No → 空かどうか判定したい?
└─ Yes → empty? を使う
(Rails なら blank? / present? も検討)
想定使用
-
フォームパラメータのクリーニング →
compact_blank -
検索条件の構築 →
compact_blank -
nil だけを除外したい →
compact -
条件分岐での判定 →
empty?(またはblank?/present?) -
false や 0 も有効な値として扱う →
compact(compact_blankは使わない)
最後に
これらのメソッドを適切に使い分けることで、コードがより安全で読みやすくなります。特に compact_blank はRails開発で非常に便利なので、ぜひ活用してください。
実務では、まず動作を確認してから使用することをお勧めします。rails console で実際に試してみると理解が深まります:
# rails console で試してみよう
irb(main):001:0> [1, nil, "", false].compact
irb(main):002:0> [1, nil, "", false].compact_blank
irb(main):003:0> [].empty?
Happy coding! 🚀
Discussion