記事内にはプロモーションが含まれています

【Ruby】flattenメソッドで配列やハッシュを平坦化する方法

【Ruby】flattenメソッドで配列やハッシュを平坦化する方法 Ruby

RubyでWebアプリケーションやデータ処理のプログラムを書いていると、「配列の中に配列が入ってしまった(入れ子構造)」という状況によく遭遇します。

APIからのレスポンスや、スクレイピングしたデータなどが多次元配列になっている場合、そのままでは扱いにくく、一度きれいな「1次元配列」に戻したいと考えるはずです。

そんな時に活躍するのが、Rubyの flatten(フラッテン) メソッドです。
これを使えば、どんなに複雑にネスト(入れ子)された配列でも、一瞬でフラットな状態に整えることができます。

この記事では、flatten メソッドの基本的な使い方から、階層の深さを指定する応用テクニック、ハッシュへの利用、さらには似た機能を持つ flat_map との使い分けまでを徹底的に解説します。

【本記事の信頼性】
プロフィール
執筆者:マヒロ
  • 執筆者は元エンジニア
  • SES⇒大手の社内SE⇒独立
  • 現在はこじんまりとしたプログラミングスクールを運営
  • モットーは「利他の精神」

flattenメソッドとは?配列を平坦化する基本の使い方

flatten は、Arrayクラス(配列)に用意されているインスタンスメソッドです。

このメソッドを呼び出すと、配列の中に含まれる「配列」を再帰的に展開し、すべてを平坦な(フラットな)1つの配列にして返します。

多次元配列を1次元配列に戻す

まずは、最も基本的な使い方を見てみましょう。
配列の中に配列が含まれている「2次元配列」や「3次元配列」を、シンプルな1次元配列に変換します。

# 2次元配列
nested_array = [1, [2, 3], 4, [5, 6]]

# flattenで平坦化
flat_array = nested_array.flatten

p flat_array
# => [1, 2, 3, 4, 5, 6]

実行結果のポイント

  • [2, 3][5, 6] といった内側の配列が展開され、外側の配列の要素として並べられます。
  • 元の配列 nested_array は変更されず、新しい配列が返されます(非破壊的メソッド)。

さらに深くネストされている場合でも、flatten は引数なしで呼び出すと、すべての階層を最後まで展開します。

# 3次元配列(配列の中に配列の中に配列)
deep_nested = [1, [2, [3, 4], 5], 6]

# すべて展開される
p deep_nested.flatten
# => [1, 2, 3, 4, 5, 6]

引数で平坦化する「階層の深さ」を指定する

flatten メソッドは、引数に整数を渡すことで「展開する階層の深さ(レベル)」を指定することができます。

すべての入れ子を解消するのではなく、「1階層だけ崩したい」といった場合に便利です。

1階層だけ平坦化する場合

引数に 1 を渡すと、1段階だけネストを解消します。

# 3次元配列
array = [1, [2, [3, 4]], 5]

# 深さ「1」を指定して実行
p array.flatten(1)
# => [1, 2, [3, 4], 5]

[2, [3, 4]] の外側のカッコは外れましたが、内側の [3, 4] は配列のまま残っています。

APIレスポンスなどで「データ構造をある程度保ったまま、外側のラッパーだけ外したい」というシーンで役立ちます。

深さを指定して平坦化する場合

引数の数値を大きくすれば、その分だけ深く展開します。

ただし、引数を省略した場合(デフォルト)は再帰的にすべて展開するため、通常は「特定の深さで止めたい場合」にのみ引数を使用します。

array = [1, [2, [3, [4, 5]]]]

# 深さ「2」を指定
p array.flatten(2)
# => [1, 2, 3, [4, 5]]

flatten(nil) を指定した場合は、引数なしと同じくすべての階層を再帰的に展開します。

破壊的メソッド flatten! の使い方と注意点

Rubyのメソッドには、末尾に ! が付く「破壊的メソッド」が存在します。

flatten にも flatten! が用意されており、これは新しい配列を返すのではなく、レシーバ(元の配列)自身を書き換えます

元の配列を変更する挙動の確認

メモリ効率を気にする場合や、オブジェクトそのものを更新したい場合に使用します。

numbers = [1, [2, 3], 4]

# 破壊的メソッドを実行
numbers.flatten!

# 元の変数が変更されている
p numbers
# => [1, 2, 3, 4]

変更がない場合はnilを返す点に注意

flatten! の注意点は、配列に変更がなかった場合(最初から平坦だった場合)に nil を返すという仕様です。

flat_numbers = [1, 2, 3]

# 変更がないので nil が返る
result = flat_numbers.flatten!

p result
# => nil

p flat_numbers
# => [1, 2, 3]

このため、array.flatten!.each { ... } のようにメソッドチェーンで繋げると、nil に対して each を呼び出してしまいエラー(NoMethodError)になる可能性があります。

メソッドチェーンを使う場合は、安全な非破壊的メソッドの flatten を使うのが無難です。

ハッシュ(Hash)でflattenを使う場合

flatten メソッドは配列だけでなく、ハッシュ(Hash)に対しても使用できます。

ただし、配列の flatten とは挙動やデフォルト設定が異なるため注意が必要です。

Hash#flatten の挙動

ハッシュに対して flatten を呼び出すと、まずハッシュを [[key1, value1], [key2, value2], ...] という配列に変換してから平坦化します。

また、引数なし(デフォルト)の場合の展開深さは「1」となります。

Array#flatten のように無限に再帰するわけではありません。

user_data = { name: "Tanaka", age: 25, city: "Tokyo" }

# ハッシュを平坦化(デフォルトは深さ1)
flat_data = user_data.flatten

p flat_data
# => [:name, "Tanaka", :age, 25, :city, "Tokyo"]

キーと値が交互に並んだ1次元配列が作成されます。

【応用:ネストしたハッシュの場合】
ハッシュの中にハッシュや配列がある場合、デフォルト(深さ1)では内側の構造は展開されません。
さらに深く展開したい場合は、引数で深さを指定する必要があります。

nested_hash = { user: { name: "Sato", scores: [80, 90] } }

# 引数なし(デフォルト)では、値の中身までは展開されない
p nested_hash.flatten
# => [:user, {:name=>"Sato", :scores=>[80, 90]}]

# 引数で深さを指定すると、さらに内側も展開される
p nested_hash.flatten(2)
# => [:user, :name, "Sato", :scores, [80, 90]]

このように、ハッシュの flatten は配列のそれとはデフォルトの挙動が異なる点を押さえておきましょう。

flattenとflat_mapの違いと使い分け

Rubyには flatten と似た用途で使われる flat_map というメソッドがあります。

これは map メソッドと flatten(1) を組み合わせたような働きをしますが、パフォーマンス面で優れています。

mapしてからflattenするならflat_mapが高速

「配列の各要素に対して処理を行い、その結果(配列になることが多い)を平坦化して一つの配列にしたい」というケースは頻繁にあります。

例えば、文字列の配列を単語ごとに分解してリストにする場合を考えてみましょう。

map + flatten の場合:

sentences = ["Hello World", "Ruby is fun"]

# splitで分割すると入れ子の配列になるため、最後にflattenする
words = sentences.map { |s| s.split(" ") }.flatten

p words
# => ["Hello", "World", "Ruby", "is", "fun"]

この処理は flat_map を使うと1回で記述でき、中間配列を作らないためメモリ効率も良くなります。

flat_map の場合:

sentences = ["Hello World", "Ruby is fun"]

# mapの処理結果を自動的に連結(平坦化)してくれる
words = sentences.flat_map { |s| s.split(" ") }

p words
# => ["Hello", "World", "Ruby", "is", "fun"]

flat_mapは1階層のみ展開する

重要な違いとして、flat_map1階層のみ 平坦化します。

flatten(引数なし)のように深い階層まで再帰的に展開することはしません。

# 深いネストがある場合
array = [[1, [2, 3]], [4, [5, 6]]]

# flat_map は1段階だけ展開する
p array.flat_map { |x| x }
# => [1, [2, 3], 4, [5, 6]]

処理の流れとして「変換して1段階平坦化」を行う場合は、積極的に flat_map を使いましょう。

実務で役立つ応用テクニック

最後に、開発現場でよく使われる flatten の応用パターンを紹介します。

nilを含む配列の平坦化とcompactの併用

flatten で展開した後、不要な nil が混じることがあります。

その場合は compact メソッドをチェーンさせることで、平坦化と同時に nil 除去を行えます。

この時、メソッドの順序が重要です。

data = [1, [2, nil], 3, [nil, 4]]

# 【推奨】平坦化してから nil を取り除く
clean_data = data.flatten.compact
p clean_data
# => [1, 2, 3, 4]

# 【注意】先にcompactすると、内側の配列にあるnilは除去されない
failed_clean = data.compact.flatten
# ネストされた配列 [2, nil] などの中身は compact の対象外
# => [1, 2, nil, 3, nil, 4]

ネストされたデータ内の nil もまとめて除去したい場合は、必ず flatten.compact の順序で使用しましょう。

Rubyのスキルを活かして年収を上げる方法

以上、Rubyで文字列を連結する方法について解説してきました。

なお、Rubyのスキルがある場合には、「転職して年収をアップさせる」「副業で稼ぐ」といった方法を検討するのがおすすめです。

Rubyエンジニアの年収や単価は高い傾向にあるため、転職によって数十万円の年収アップはザラで、100万円以上年収が上がることも珍しくありません。

なお、転職によって年収を上げたい場合は、エンジニア専門の転職エージェントサービスを利用するのが最適です。

今すぐ転職する気がなくとも、とりあえず転職エージェントに無料登録しておくだけで、スカウトが届いたり、思わぬ好待遇の求人情報が送られてきたりするというメリットがあります。

併せて、副業案件を獲得できるエージェントにも登録しておくと、空いている時間を活かして稼げるようなRubyの案件を探しやすくなります。

転職エージェントも副業エージェントも、登録・利用は完全無料なので、どんな求人や副業案件があるのか気になる方は、気軽に利用してみるとよいでしょう。