その他
    ホーム 技術発信 DoRuby flattenで多次元を1次元に
    flattenで多次元を1次元に
     

    flattenで多次元を1次元に

    この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

    今回は、rubyのflattenメソッドの紹介をしていきます。

    flattenメソッドとは

    flattenは、配列とハッシュに使用することができます。
    配列に対してflattenメソッドを使用すると、2次元配列や3次元配列などの階層の深い配列を1次元配列に再構築してくれます。

    [ [1, 2], [3, 4], [5, 6] ].flatten
    => [1, 2, 3, 4, 5, 6]
    
    [ [ [1, 2], [3, 4] ], [ [5, 6], [7, 8] ] ].flatten
    => [1, 2, 3, 4, 5, 6, 7, 8]
    

    ハッシュに対してflattenメソッドを実行すると、ハッシュのキーと値の一次元配列が返ります。

    { "apple" => 3, "grape" => 2, "peach" => 5 }.flatten
    => ["apple", 3, "grape", 2, "peach", 5]
    

    しかし、ハッシュの値がハッシュである場合は値のハッシュは変換されません。

    { "red" => { "apple" => 3 }, "purple" => { "grape" => 2 } }.flatten
    => ["red", {"apple"=>3}, "purple", {"grape"=>2}]
    

    mapメソッドとの組み合わせて使用する

    ここで紹介しているflattenメソッドですが、mapメソッドと組み合わせて使用することで階層の深い関連データを1次元の配列で取得することができます。
    例をProject Programmer Workモデルで説明します。目的は、Projectに関係のあるProgrammerのWorkの一覧を取得することです。
    enter image description here

    以下のコードがProjectに関係のあるProgrammerのWork一覧を取得するコードです。

    project = Project.find(1)
    project.programmers.map(&:works).flatten
    

    このコードが何をしているのか説明していきます。
    まず、project.programmersでprojectに紐づくprogrammerを参照しています。

    project.programmers
    => [#<Programmer id: 1, name: "taro">, #<Programmer id: 2, name: "jiro">]
    

    次にproject.programmers.map(&:works)でprogrammerに紐づくworkを参照しています。ただし、2次元配列で返ってきます。

    project.programmers.map(&:works)
    => [[#<Work id:1, status: "new">, #<Work id:2, status: "fin">], [#<Work id:3, status: "fin">, #<Work id:4, status: "fin">]]
    

    最後にproject.programmers.map(&:works).flattenで2次元配列を1次元配列に再構築しています。

    project.programmers.map(&:works).flatten
    => [#<Work id: 1, status: "new">, #<Work id: 2, status: "fin">, #<Work id: 3, status: "fin">, #<Work id: 4, status: "fin">]
    

    まとめ

    eachなどのループを使用すれば、この記事で紹介したように階層の深いデータを取得することができますが、mapとflattenを使用すれば1行で納めることができます。自分の経験上、関連データの関連データを一覧で取得する機会があまりないですが、rubocopで行数を抑えたいときに使えるかと思います。