Ruby:将嵌套的Ruby哈希转换为非嵌套的Ruby哈希

现在,我有一个服务器调用踢回以下Ruby哈希:

{ "id"=>"-ct", "factualId"=>"", "outOfBusiness"=>false, "publishedAt"=>"2012-03-09 11:02:01", "general"=>{ "name"=>"A Cote", "timeZone"=>"EST", "desc"=>"À Côté is a small-plates restaurant in Oakland's charming Rockridge district. Cozy tables surround large communal tables in both the main dining room and on the sunny patio to create a festive atmosphere. Small plates reflecting the best of seasonal Mediterranean cuisine are served family-style by a friendly and knowledgeable staff.\nMenu items are paired with a carefully chosen selection of over 40 wines by the glass as well as a highly diverse bottled wine menu. Specialty drinks featuring fresh fruits, rare botaniques and fine liqueurs are featured at the bar.", "website"=>"http://acoterestaurant.com/" }, "location"=>{ "address1"=>"5478 College Ave", "address2"=>"", "city"=>"Oakland", "region"=>"CA", "country"=>"US", "postcode"=>"94618", "longitude"=>37.84235, "latitude"=>-122.25222 }, "phones"=>{ "main"=>"510-655-6469", "fax"=>nil }, "hours"=>{ "mon"=>{"start"=>"", "end"=>""}, "tue"=>{"start"=>"", "end"=>""}, "wed"=>{"start"=>"", "end"=>""}, "thu"=>{"start"=>"", "end"=>""}, "fri"=>{"start"=>"", "end"=>""}, "sat"=>{"start"=>"", "end"=>""}, "sun"=>{"start"=>"", "end"=>""}, "holidaySchedule"=>"" }, "businessType"=>"Restaurant" } 

它有几个嵌套的属性,例如:

 "wed"=>{"start"=>"", "end"=>""} 

我需要在Ruby中将此对象转换为unnested哈希。 理想情况下,我想检测属性是否嵌套,并相应地做出响应,当IE确定属性’ wed ‘嵌套时,它会将其数据拉出并存储在字段’ wed-start ‘和’ wed-end ‘,或类似的东西。

任何人都有关于如何开始的任何提示?

这是完整解决方案的第一次切入。 我相信你可以更优雅地写它,但这似乎相当清楚。 如果将其保存在Ruby文件中并运行它,您将获得我在下面显示的输出。

 class Hash def unnest new_hash = {} each do |key,val| if val.is_a?(Hash) new_hash.merge!(val.prefix_keys("#{key}-")) else new_hash[key] = val end end new_hash end def prefix_keys(prefix) Hash[map{|key,val| [prefix + key, val]}].unnest end end p ({"a" => 2, "f" => 5}).unnest p ({"a" => {"b" => 3}, "f" => 5}).unnest p ({"a" => {"b" => {"c" => 4}, "f" => 5}}).unnest 

输出:

 {"a"=>2, "f"=>5} {"ab"=>3, "f"=>5} {"abc"=>4, "af"=>5} 

编辑: 稀疏gem被发布作为这个问题的一般解决方案。


这是我几个月前开始实施的一个实现。 您需要将JSON解析为哈希值,然后使用Sparsify稀疏哈希值。

 # Extend into a hash to provide sparse and unsparse methods. # # {'foo'=>{'bar'=>'bingo'}}.sparse #=> {'foo.bar'=>'bingo'} # {'foo.bar'=>'bingo'}.unsparse => {'foo'=>{'bar'=>'bingo'}} # module Sparsify def sparse(options={}) self.map do |k,v| prefix = (options.fetch(:prefix,[])+[k]) next Sparsify::sparse( v, options.merge(:prefix => prefix ) ) if v.is_a? Hash { prefix.join(options.fetch( :separator, '.') ) => v} end.reduce(:merge) || Hash.new end def sparse! self.replace(sparse) end def unsparse(options={}) ret = Hash.new sparse.each do |k,v| current = ret key = k.to_s.split( options.fetch( :separator, '.') ) current = (current[key.shift] ||= Hash.new) until (key.size<=1) current[key.first] = v end return ret end def unsparse!(options={}) self.replace(unsparse) end def self.sparse(hsh,options={}) hsh.dup.extend(self).sparse(options) end def self.unsparse(hsh,options={}) hsh.dup.extend(self).unsparse(options) end def self.extended(base) raise ArgumentError, "<#{base.inspect}> must be a Hash" unless base.is_a? Hash end end 

用法:

 external_data = JSON.decode( external_json ) flattened = Sparsify.sparse( external_data, :separator => '-' ) 

这最初创建是因为我们正在处理在Mongo中存储一组内容,这允许我们在更新时使用稀疏键(点分隔)来更新嵌套哈希的一些内容而不覆盖不相关的键。

还有一个选择:

 class Hash def smash(prefix = nil) inject({}) do |acc, (k, v)| key = prefix.to_s + k if Hash === v acc.merge(v.smash(key + '-')) else acc.merge(key => v) end end end end hash = { 'f' => 100, 'z' => {'j' => 25}, 'a' => {'b' => {'c' => 1}} } puts hash.smash # => {"f"=>100, "zj"=>25, "abc"=>1} 

解决这个问题的另一种方法是不要将哈希变平,而是将其视为扁平化。 例如,给定此哈希:

 h = { 'a' => 1, 'b' => { 'c' => 2, 'd' => 3, }, } 

然后这个function:

 NESTED_KEY_SEPARATOR = '-' NESTED_KEY_REGEX = /^(.*?)(?:#{NESTED_KEY_SEPARATOR}(.*))?$/ def nested_fetch(key, hash) return hash if key.empty? first_part_of_key, rest_of_key = NESTED_KEY_REGEX.match(key).captures value = hash[first_part_of_key] if value.is_a?(Hash) nested_hash_fetch(value, rest_of_key || '') elsif rest_of_key nil else value end end 

通过将各个哈希键与KEY_SEPARATOR连接在一起(在此设置为破折号,但可以是任何从未作为您需要搜索的哈希中的键出现的字符),可以检索嵌套哈希元素:

 p nested_fetch('a', h) # => 1 p nested_fetch('b-c', h) # => 2 

如果您提供部分限定键,则会获得在该点匹配的哈希:

 p nested_fetch('b', h) # => {"c"=>2, "d"=>3} 

如果你给一个不存在的密钥,你就得零:

 p nested_fetch('b-x', h) # => nil 

如果需要,可以通过简单地将上面的代码包含在Hash类中,并将self作为参数hash的默认值,将其修补到Hash上:

 class Hash NESTED_KEY_SEPARATOR = '-' NESTED_KEY_REGEX = /^(.*?)(?:#{KEY_SEPARATOR}(.*))?$/ def nested_fetch(key, hash = self) ... end