【Ruby】compact()/compact!()でnilを排除する

突然ですが、Rubyを書いていて、配列やハッシュにnilが入り込むことはありませんか?

私はそんな場面によく遭遇するのですが、そんな時はcompact()メソッドを使用しましょう。compact()メソッドを使うことで、無用なエラーを回避することはもちろん、配列のサイズを圧縮することができ、ひいてはプログラムを動作させるために必要なメモリを節約できます

今回は、配列を圧縮するのにうってつけな機能である、compact()メソッドの使用方法について解説します。配列で何らかの処理を実装する際に、お役立てください。

なお、Rubyの魅力や特徴については以下の記事で詳しく解説していますので、こちらもぜひご覧くださいね。

おすすめ記事

みなさんこんにちは! 今回の記事は、 悩みを抱えた人 ・Rubyってどんなプログラミング言語なの?・Rubyを勉強すると将来役に立つかな?・Rubyのおすすめの学習方法が知りたい! というお悩みを解決する記事にな[…]

Rubyとは?その特徴や将来性、おすすめの学習方法まで網羅的にご紹介します

compact()メソッドとは?

compact()メソッドは、その名の通り配列をコンパクトに収めるために、配列中に要素として存在するnilを除去する機能を持つメソッドです。

compact()メソッドは、ArrayクラスおよびHashクラスでそれぞれ用意されています。早速、以下のサンプルコードで基本的な使い方を確認しましょう。

# nilが含まれた配列
input_data = [123, nil, 433, 345, nil, 4324, nil, nil]

# nilを除去した配列を生成
comp_data = input_data.compact

p input_data # [123, nil, 433, 345, nil, 4324, nil, nil]
p comp_data  # [123, 433, 345, 4324]

サンプルコードで定義されたinput_dataは、要素数が10であるものの全体の半分がnilで構成されています。このような配列をレシーバとして、compact()メソッドを呼び出すことで、nilが除去された配列を返すのが基本的な動作です。引数は必要ありません。

つづいて、Hashクラスのcompact()メソッドの使用法について、以下のサンプルコードで確認しましょう。

# nilが含まれたハッシュ
input_data = { a:111, b:nil, c:123, d:543, e:nil }

# nilを除去したハッシュを生成
comp_data = input_data.compact

p input_data # { :a=>111, :b=>nil, :c=>123, :d=>543, :e=>nil }
p comp_data  # { :a=>111, :c=>123, :d=>543 }

ハッシュをレシーバにしたとしても、配列をレシーバとした場合と呼び出し方は一緒です。ハッシュの場合は、値がnilの要素をキー名もろとも削除します

compact!()メソッドとは?

compact()メソッドとそっくりな機能を持つメソッドとして、compact!()メソッドが存在します。compact!()メソッドは、破壊的メソッドに分類されるメソッドで、呼び出したことでレシーバの情報を更新するのが、大きな特徴です。

先ほど紹介したcompact()メソッドとどのように違うのか、以下のサンプルコードを見ながら機能を比較してみましょう。サンプルコードでは、Arrayクラスのものを用いて実装していますが、Hashクラスで置き換えても動作の違いはありません。

sample_data_1 = [11, 22, nil, 33, 44, nil, nil, 55, 66, nil, nil, 77]
sample_data_2 = [11, 22, nil, 33, 44, nil, nil, 55, 66, nil, nil, 77]

comp_data_1 = sample_data_1.compact
comp_data_2 = sample_data_2.compact!

# compact, compact!ともに戻り値はnil除去済みの配列
p comp_data_1 # [11, 22, 33, 44, 55, 66, 77]
p comp_data_2 # [11, 22, 33, 44, 55, 66, 77]

# compact呼び出し後もレシーバは変化なし
p sample_data_1 # [11, 22, nil, 33, 44, nil, nil, 55, 66, nil, nil, 77]

# compact!呼び出し後にレシーバは変化する
p sample_data_2 # [11, 22, 33, 44, 55, 66, 77]

ポイントとなるのは、各メソッド呼び出し後にレシーバがどのような状態になっているかです。

compact()メソッドで呼び出した場合は、レシーバに何も変化は生じません。一方、破壊的メソッドであるcompact!()メソッドで呼び出すと、nilを除去した配列がレシーバに対して格納されます。

compact!()メソッドを含む破壊的メソッドは、新しく変数を用意するのが煩わしい場合に利用することが多いので、その点を考慮して使い分けましょう。

また、すでにnilが除去されている配列に対してcompact()メソッドとcompact!()メソッドを呼び出した際は、戻り値に違いがあることも注意してください。以下のサンプルコードを実行して、戻り値を確認してみましょう。

comp_data_1 = [123,45,789]
comp_data_2 = [123,45,789]

result_1 = comp_data_1.compact
result_2 = comp_data_2.compact!

p result_1 # [123, 45, 789]
p result_2 # nil

compact()メソッドを呼び出した場合は、除去処理が行われず配列がそのまま返されるのに対し、compact!()メソッドの場合は、nilが返されることが分かります。

compact()メソッドを使うときに気をつけるべき点

配列のサイズを圧縮できるというメリットがあるcompact()メソッドですが、細かな仕様をしっかり理解しておかないと、意図しない動作を起こすおそれがあります。ここでは、compact()メソッドを使用する際に気をつけるべき点について紹介します。

0は除去されない

1つ目の気をつけるべき点は、数値の0が除去されないことです。以下のサンプルコードを確認してください。

number_array = [1, 6, 3, 0, nil, 0]
number_hash = { a: 1, b: 6, c: 3, d: 0, e: nil, f: 0 }

comp_number_array = number_array.compact
comp_number_hash  = number_hash.compact

p comp_number_array # [1, 6, 3, 0, 0]
p comp_number_hash  # { :a = >1, :b => 6, :c => 3, :d => 0, :f => 0 }

0とnilが混在する配列・ハッシュをそれぞれcompact()メソッドで圧縮しています。nilは除去されたのに対し、0はそのまま残っていることが分かります。0というイメージから不要なデータであると考えがちですが、compact()メソッドでは削除されないことに注意してください。

空文字は除去されない

2つ目の気をつけるべき点は、空文字が除去されないことです。以下のサンプルコードで、空文字が削除されないことを確認しましょう。

str_array = ["watch", "belt", "", "socks", nil]
str_hash = { a: "apple", b: "banana", c: "cinnamon", d: "", e: nil }

comp_str_array = str_array.compact
comp_str_hash  = str_hash.compact

p comp_str_array # ["watch", "belt", "", "socks"]
p comp_str_hash  # { :a => "apple", :b => "banana", :c => "cinnamon", :d => "" }

空文字も0と同じように不要なデータのように見えますが、compact()メソッドの除去対象にはならないことを覚えておきましょう。

空配列は除去されない

3つ目の気をつけるべき点は、要素数0の空配列が除去されないことです。以下のサンプルコードを実行しても分かるように、compact()メソッドを呼び出しても空っぽな配列が除去されずに残存しているのが分かります。

sample_array = ["watch", 1.3, true, [], nil, Object.new]
sample_hash = { a: "apple", b: 2.1, c: false, d: [], e: nil, g: Object.new }

comp_sample_array = sample_array.compact
comp_sample_hash  = sample_hash.compact

p comp_sample_array # ["watch", 1.3, true, [], #<Object:0x0000019bc46887e0>]
p comp_sample_hash  # { :a => "apple", :b => 2.1, :c => false, :d => [], :g => #<Object:0x0000019bc4688510> }

入れ子の配列にあるnilは除去されない

4つ目の気をつけるべき点は、配列の要素に配列が存在する場合(入れ子の場合)に、その配列に存在するnilが除去されないことです。ややこしいですが、以下のサンプルコードを実行すれば、入れ子の配列内のnilが除去されていないことが分かります。

sample_array = [nil, [nil]]
sample_hash = { a: nil, b: { A: nil }}

comp_sample_array = sample_array.compact
comp_sample_hash  = sample_hash.compact

p comp_sample_array # [[nil]]
p comp_sample_hash  # { :b => { :A => nil } }

お仕事の途中ですが、少し一休みして、転職独立について考えてみませんか🙌?

現役エンジニアが選ぶおすすめの転職エージェント11選【成功談・失敗談もあります】

レバテックフリーランスの評判ってどう?【現役エンジニアが徹底解説します】

MidWorks(ミッドワークス)の評判ってどう?【現役エンジニアが徹底解説します】

日々の業務に追われて自分を見失わないよう、
定期的にキャリアを振り返るようにしておきましょう🤲

最後に

さて、ここまでRubyを使って配列・ハッシュ内のnilを削除する方法について解説してきましたがいかがでしたか?

Rubyではcompact()・compact!()メソッドが用意されていますが、後者は破壊的メソッドになりますので、その扱いには注意が必要です。

また、Rubyの特性上、数字の0や空文字なども除去されませんので、その点についても留意しておいてくださいね。

このブログを通じて少しでも「傍(はた)を楽(らく)にする」ことができていれば嬉しく思います。

最後まで読んで頂きありがとうございました。