LoginSignup
21
19

More than 5 years have passed since last update.

扱いにくい階層の深いHashをフラット(1階層)にする

Last updated at Posted at 2014-06-01

APIなどで階層の深いHashを受け取り、そのデータをアプリケーションで利用する際に、レスポンスデータがどんなデータパターンで来てもエラーにならないように設計するのは以外と難しいことがあるかと思います。

そこで、受け取った階層の深いHashをフラットにして1階層に変換する機能を作ったのでニーズがある方は使ってみてください。

/lib/utility/hash_flatter.rb

#今回は/libにutilityフォルダを作成してそこに作りました。

class HashFlatter < Object
  def set(hash)
    @hash = hash
    @flatted = false
  end
  def flat
    @flatted = true
    @ret = {}
    @hash.each do |key, value|
      if value.class == Hash || value.class == ActiveSupport::HashWithIndifferentAccess
        value.each do |key_s, value_s|
          @ret = @ret.merge({key.to_s + '[' + key_s.to_s + ']' => value_s})
          @flatted = false
        end
      else
        if value.class == Array
          value.each_with_index do |element, index|
            @ret = @ret.merge ({key+'['+index.to_s+']' => element})
            @flatted = false
          end
        else
          @ret = @ret.merge ({key => value})
        end
      end
    end
  end
  def get
    @ret
  end
  def flatted?
    @flatted
  end

  def self.flat hash
    hf = HashFlatter.new
    hf.set hash
    hf.flat
    while hf.flatted? == false
      hf.set hf.get
      hf.flat
    end
    hf.get
  end

end

/config/appication.rb

#この一行を追加しておけば、いつでもHashFlatterが使えます。
config.autoload_paths += %W(#{config.root}/lib/utility)


実際に使ってみると、こんな感じに。

hash=
  {"name"=>"Toshiyuki Oka",
   "age"=>"29",
   "location"=>
   {"country"=>"Japan",
    "city"=>"Tokyo",
    "other"=>
    {"detail"=>"Minatoku-Minamiazabu",
     "building"=>"Furukawa building"
    }
   }
  }

HashFlatter.flat(hash)
=> {"name"=>"Toshiyuki Oka", "age"=>"29", "location[country]"=>"Japan", "location[city]"=>"Tokyo", "location[other][detail]"=>"Minatoku-Minamiazabu", "location[other][building]"=>"Furukawa building"}

どんなときに特に役に立つかというと、例えば、locationが必須データではなかったときに、

hash["location"]
=> nil

user.location.country = hash["location"]["city"]
=> NoMethodError: You have a nil object when you didn't expect it!

となってしまい、それを回避するためにはいくつかの条件分岐をしなければなりません。
もちろん可読性が低くなり、エラーの原因にもなります。

HashFlatterを使えば、hash["location"]がnilでも、

#hash["location"]=nilのときは下記のようになり、
hash["location"]
=> nil

HashFlatter.flat(hash)
=> {"name"=>"Toshiyuki Oka", "age"=>"29", "location"=>nil}


#hash["location"]!=nilのときは下記のようになるので、条件分けが必要ありません。
hash["location"]
=> {"country"=>"Japan", "city"=>"Tokyo", "other"=>{"detail"=>"Minatoku-Minamiazabu", "building"=>"Furukawa building"}}

HashFlatter.flat(hash)
=> {"name"=>"Toshiyuki Oka", "age"=>"29", "location[country]"=>"Japan", "location[city]"=>"Tokyo", "location[other][detail]"=>"Minatoku-Minamiazabu", "location[other][building]"=>"Furukawa building"}

以上、ニーズに合致する方がいたら使ってみてください!!

21
19
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
19