2011-12-12 91 views
2

让我们说我从API取回一个JSON嵌套散列(或散列数组)用nils遍历ruby嵌套散列?

@example = {"results" = > {{"poop" => "shoop"},{"foo" => {"shizz" => "fizz", "nizzle"=>"bizzle"}}} 

YAML标记中嵌套的哈希上述

- poop: shoop 
    - foo: 
    shizz: fizz 
    nizzle: bizzle 

现在让我们去作数据库条目与ActiveRecord哈希。这工作正常。

Thing.create!(:poop => @example["results"]["poop"], 
       :shizz => @example["results"]["foo"]["shizz"], 
       :nizzle=> @example["results"]["foo"]["nizzle"]) 

但是如果'foo'为空或无?例如,如果一个API结果有一个带有“名字”,“姓氏”#等的“person”散列,那么如果没有数据,那么“person”散列通常是空的,这意味着它内部的散列不会“不存在。

@example = {"results" = > {{"poop" => "shoop"},{"foo" => nil }} 

    Thing.create!(:poop => @example["results"]["poop"], 
       :shizz => @example["results"]["foo"]["shizz"], 
       :nizzle=> @example["results"]["foo"]["nizzle"]) 

    NoMethodError: You have a nil object when you didn't expect it! 
    You might have expected an instance of Array. 
    The error occurred while evaluating nil.[] 

处理这个问题的最好方法是什么?

回答

5

我来过一个nil敏感Hash#get方法一会儿回来。

class Hash 
    def get(key, default=nil) 
    key.split(".").inject(self){|memo, key_part| memo[key_part] if memo.is_a?(Hash)} || default 
    end 
end 

h = { 'a' => { 'b' => { 'c' => 1 }}} 
puts h.get "a.b.c" #=> 1 
puts h.get "a.b.c.d" #=> nil 
puts h.get "not.here" #=> nil 

对于这种JSON钻井来说非常方便。

否则你必须做这样的东西:

h['a'] && h['a']['b'] && h['a']['b']['c'] 

而这只是吮吸。

+0

确实很有趣,如何“耗尽”备忘录散列 – maprihoda

+0

光滑 - 这将派上用场 – klochner

+0

所以我会制作一个自定义的类来扩展系统的哈希类,或者我会将这个'get'方法的猴子修补成“哈希”? – thoughtpunch

2

如果您使用的轨道(不知道它在红宝石1.9):

h = {"a"=>1} 
h.try(:[],"a") #1 
h.try(:[],"b") #nil 

h2 = {"c"=>{"d"=>1}} 
h2.try(:[],"c").try(:[],"d") #1 
h2.try(:[],"a").try(:[],"foo") #nil 

# File activesupport/lib/active_support/core_ext/object/try.rb, line 28 
def try(*a, &b) 
    if a.empty? && block_given? 
    yield self 
    else 
    __send__(*a, &b) 
    end 
end 

+0

我喜欢Rails应用程序的这种解决方案,但我正在寻找更低级别的纯Ruby解。我会试试这个,谢谢! – thoughtpunch

1

我继续开始通过所有的Hash结果为Hashie Mash。这样他们的行为就像Ruby对象,并像冠军一样对nils做出响应!

0

红宝石2.3.0引入a new method called digHashArray完全解决了这个问题。

value = hash.dig(:a, :b) 

如果密钥在任何级别缺失,则返回nil