rubyruby-hash

Ruby convert flattened hash to nested hash


I have the following:

{ :a_b_c => 42, :a_b_d => 67, :a_d => 89, :e => 90 }

How do I convert this as below

{ a: { b: { c: 42, d: 67 }, d: 89 }, e: 90 } 

Solution

  • Rails with ActiveSupport have Hash#deep_merge and Hash#deep_merge!

    If you haven't them, you can define

    class Hash
      def deep_merge(other_hash, &block)
        dup.deep_merge!(other_hash, &block)
      end
    
      def deep_merge!(other_hash, &block)
        merge!(other_hash) do |key, this_val, other_val|
          if this_val.is_a?(Hash) && other_val.is_a?(Hash)
            this_val.deep_merge(other_val, &block)
          elsif block_given?
            block.call(key, this_val, other_val)
          else
            other_val
          end
        end
      end
    end
    

    Or just require these methods

    require 'active_support/core_ext/hash/deep_merge'
    

    And finally

    hash =
      { :a_b_c => 42, :a_b_d => 67, :a_d => 89, :e => 90 }
    
    hash.each_with_object({}) do |(k, v), h|
      h.deep_merge!(k.to_s.split('_').map(&:to_sym).reverse.reduce(v) { |assigned_value, key| { key => assigned_value } })
    end
    
    # => {:a=>{:b=>{:c=>42, :d=>67}, :d=>89}, :e=>90}