如何展平哈希值,使每个键成为唯一值?

wil*_*cro 13 ruby hash flatten

我想采用嵌套哈希和数组的哈希,并将其展平为具有唯一值的单个哈希.我一直试图从不同的角度来解决这个问题,但后来我让它变得比它需要的更复杂,让自己迷失在正在发生的事情中.

示例源哈希:

{
  "Name" => "Kim Kones",
  "License Number" => "54321",
  "Details" => {
    "Name" => "Kones, Kim",
    "Licenses" => [
      {
        "License Type" => "PT",
        "License Number" => "54321"
      },
      {
        "License Type" => "Temp",
        "License Number" => "T123"
      },
      {
        "License Type" => "AP",
        "License Number" => "A666",
        "Expiration Date" => "12/31/2020"
      }
    ]
  }
}
Run Code Online (Sandbox Code Playgroud)

示例所需哈希:

{
  "Name" => "Kim Kones",
  "License Number" => "54321",
  "Details_Name" => "Kones, Kim",
  "Details_Licenses_1_License Type" => "PT",
  "Details_Licenses_1_License Number" => "54321",
  "Details_Licenses_2_License Type" => "Temp",
  "Details_Licenses_2_License Number" => "T123",
  "Details_Licenses_3_License Type" => "AP",
  "Details_Licenses_3_License Number" => "A666",
  "Details_Licenses_3_Expiration Date" => "12/31/2020"
}
Run Code Online (Sandbox Code Playgroud)

对于它的价值,这是我最近的尝试,然后才放弃.

def flattify(hashy)
    temp = {}
    hashy.each do |key, val|
        if val.is_a? String
            temp["#{key}"] = val
        elsif val.is_a? Hash
            temp.merge(rename val, key, "")
        elsif val.is_a? Array
            temp["#{key}"] = enumerate val, key
        else
        end
        print "=> #{temp}\n"
    end
    return temp
end

def rename (hashy, str, n)
    temp = {}
    hashy.each do |key, val|
        if val.is_a? String
            temp["#{key}#{n}"] = val
        elsif val.is_a? Hash
            val.each do |k, v|
                temp["#{key}_#{k}#{n}"] = v
            end
        elsif val.is_a? Array
            temp["#{key}"] = enumerate val, key
        else

        end
    end
    return flattify temp
end

def enumerate (ary, str)
    temp = {}
    i = 1
    ary.each do |x|
        temp["#{str}#{i}"] = x
        i += 1
    end
    return flattify temp
end
Run Code Online (Sandbox Code Playgroud)

Eri*_*nil 12

有趣的问题!

理论

这是一个解析数据的递归方法.

  • 它跟踪它找到的键和索引.
  • 它将它们附加在一个tmp数组中.
  • 一旦找到了叶子对象,它就会以哈希值的形式写入,并使用join tmpas键.
  • 然后,这个小散列以递归方式合并回主散列.

def recursive_parsing(object, tmp = [])
  case object
  when Array
    object.each.with_index(1).with_object({}) do |(element, i), result|
      result.merge! recursive_parsing(element, tmp + [i])
    end
  when Hash
    object.each_with_object({}) do |(key, value), result|
      result.merge! recursive_parsing(value, tmp + [key])
    end
  else
    { tmp.join('_') => object }
  end
end
Run Code Online (Sandbox Code Playgroud)

举个例子:

require 'pp'
pp recursive_parsing(data)
# {"Name"=>"Kim Kones",
#  "License Number"=>"54321",
#  "Details_Name"=>"Kones, Kim",
#  "Details_Licenses_1_License Type"=>"PT",
#  "Details_Licenses_1_License Number"=>"54321",
#  "Details_Licenses_2_License Type"=>"Temp",
#  "Details_Licenses_2_License Number"=>"T123",
#  "Details_Licenses_3_License Type"=>"AP",
#  "Details_Licenses_3_License Number"=>"A666",
#  "Details_Licenses_3_Expiration Date"=>"12/31/2020"}
Run Code Online (Sandbox Code Playgroud)

调试

这是一个带有老派调试的修改版本.它可能会帮助您了解正在发生的事情:

def recursive_parsing(object, tmp = [], indent="")
  puts "#{indent}Parsing #{object.inspect}, with tmp=#{tmp.inspect}"
  result = case object
  when Array
    puts "#{indent} It's an array! Let's parse every element:"
    object.each_with_object({}).with_index(1) do |(element, result), i|
      result.merge! recursive_parsing(element, tmp + [i], indent + "  ")
    end
  when Hash
    puts "#{indent} It's a hash! Let's parse every key,value pair:"
    object.each_with_object({}) do |(key, value), result|
      result.merge! recursive_parsing(value, tmp + [key], indent + "  ")
    end
  else
    puts "#{indent} It's a leaf! Let's return a hash"
    { tmp.join('_') => object }
  end
  puts "#{indent} Returning #{result.inspect}\n"
  result
end
Run Code Online (Sandbox Code Playgroud)

调用时recursive_parsing([{a: 'foo', b: 'bar'}, {c: 'baz'}]),显示:

Parsing [{:a=>"foo", :b=>"bar"}, {:c=>"baz"}], with tmp=[]
 It's an array! Let's parse every element:
  Parsing {:a=>"foo", :b=>"bar"}, with tmp=[1]
   It's a hash! Let's parse every key,value pair:
    Parsing "foo", with tmp=[1, :a]
     It's a leaf! Let's return a hash
     Returning {"1_a"=>"foo"}
    Parsing "bar", with tmp=[1, :b]
     It's a leaf! Let's return a hash
     Returning {"1_b"=>"bar"}
   Returning {"1_a"=>"foo", "1_b"=>"bar"}
  Parsing {:c=>"baz"}, with tmp=[2]
   It's a hash! Let's parse every key,value pair:
    Parsing "baz", with tmp=[2, :c]
     It's a leaf! Let's return a hash
     Returning {"2_c"=>"baz"}
   Returning {"2_c"=>"baz"}
 Returning {"1_a"=>"foo", "1_b"=>"bar", "2_c"=>"baz"}
Run Code Online (Sandbox Code Playgroud)