处理Ruby中的许多[...]

时间:2014-08-31 17:56:52

标签: ruby json

在纯粹的红宝石脚本中我有这个:

result = JSON.parse result.body_str
count = result && result["ke1"] && result["ke1"]["key2"] && result["ke1"]["key2"]["key3"] && result["ke1"]["key2"]["key3"]["key4"] ? 
        result["key1"]["key2"]["key3"]["key4"].to_i : 
        123

有没有办法简化这个?

4 个答案:

答案 0 :(得分:6)

count = result["key1"]["key2"]["key3"]["key4"].to_i rescue 123

如果你想制作一个更好的可读性的私人方法,你可以做

def count(result)
  result["key1"]["key2"]["key3"]["key4"].to_i 
rescue NoMethodError
  123
end

我添加了NoMethodError来限制救援可以吞下的错误。尽管争论不休 使用流控制的例外,我更喜欢这个以便于阅读。在一个小功能或一个衬管中,它在技术上甚至不会改变流量,因为它们都保留在一个位置。

如果在具有数百万条记录的紧密循环中使用它,您可能希望使用分析器与其他解决方案进行比较,但您必须根据实际使用情况进行调用。如果在一些可能每天运行5次的代码上使用它,请坚持使用更容易阅读和维护的代码。

答案 1 :(得分:2)

我会这样写,然后把它放在一个模块中,根据需要加入。

<强>代码

def value_at_deep_key(hash, path)
  path.each_with_index.reduce(hash) do |current, (segment, i) |
    case c = current[segment]
    when Hash then c
    else (i==path.size-1) ? (current.key?(segment) ? c : :NO_MATCH) : {}
    end
  end      
end

<强>实施例

value_at_deep_key({a: {b: {c: "cat"}}}, [:a, :b, :c]) #=> "cat"
value_at_deep_key({a: {b: {c: false}}}, [:a, :b, :c]) #=> false
value_at_deep_key({a: {b: {c: nil}}},   [:a, :b, :c]) #=> nil
value_at_deep_key({z: {b: {c: "cat"}}}, [:a, :b, :c]) #=> :NO_MATCH
value_at_deep_key({a: {z: {c: "cat"}}}, [:a, :b, :c]) #=> :NO_MATCH
value_at_deep_key({a: {b: {z: "cat"}}}, [:a, :b, :c]) #=> :NO_MATCH
value_at_deep_key({a: {b: {c: "cat"}}}, [:a, :b])     #=> {:c=>"cat"}
value_at_deep_key({a: {b: {c: "cat"}}}, [:a])         #=> {:b=>{:c=>"cat"}}
value_at_deep_key({z: {b: {c: "cat"}}}, [])           #=> {:z=>{:b=>{:c=>"cat"}}}
value_at_deep_key({z: {b: {c: "cat"}}}, [:a, :b, :c]) #=> :NO_MATCH
value_at_deep_key({a: {z: {c: "cat"}}}, [:a, :b, :c]) #=> :NO_MATCH
value_at_deep_key({a: {b: {z: "cat"}}}, [:a, :b, :c]) #=> :NO_MATCH
value_at_deep_key({a: {z: {c: "cat"}}}, [:a, :b, :c, :d]) #=> :NO_MATCH

然后可以写:

val = value_at_deep_key(hash, path)
(val = 123) if (val == :NO_MATCH)

如果最后一个键的值不能是nil

else (i==path.size-1) ? (current.key?(segment) ? c : :NO_MATCH) : {}

可以替换为:

else (i==path.size-1) ? c : {}

在这种情况下,如果没有匹配,将返回nil

答案 2 :(得分:0)

我偶尔会定义一个这样的方法

def value_at_deep_key hash, path, default=nil
  path.inject(hash) {|current,segment| current && current[segment]} || default
end

这使用inject依次获取哈希的每个级别。 你会像这样使用

value_at_deep_key(result, %w(key1 key2 key3 key4), 123)

就我个人而言,我不喜欢使用救援这种东西 - 它可以掩盖错误。

答案 3 :(得分:0)

接受的答案效果不佳:

以下是代码:

def value_at_deep_key(hash, path, default=nil)
  path.inject(hash) {|current,segment| current && current[segment]} || default
end

以下是一些结果:

1)--------------------

h = {
  'key1' => {'key2' => {'key3' => {'key4' => 3}}}
}

p value_at_deep_key(h, %w[key1 key2 key3 key4], 123)

--output:--
3

2)--------------------

h = {
  'key1' => 1,
  'key2' => 2,
  'key3' => 3,
  'key4' => 4,
}

p value_at_deep_key(h, %w[key1 key2 key3 key4], 123)

--output:--
1.rb:16:in `[]': no implicit conversion of String into Integer (TypeError)
    from 1.rb:16:in `block in value_at_deep_key'
    from 1.rb:16:in `each'
    from 1.rb:16:in `inject'
    from 1.rb:16:in `value_at_deep_key'
    from 1.rb:19:in `<main>'

3)---------------------

h = {
  'key1' => {'key2' => {'key3' => 4}}
}

p value_at_deep_key(h, %w[key1 key2 key3 key4], 123)

--output:--
1.rb:16:in `[]': no implicit conversion of String into Integer (TypeError)

以下答案似乎更有效:

def value_at_deep_key(hash, key_sequence, default=nil)
  return "No keys to lookup!" if key_sequence.empty?

  value = hash

  key_sequence.each do |key|
    case value
    when Hash
      value = value[key]
    else
      value = nil
      break
    end
  end

  value.nil? ? default : Integer(value)   #A found value of nil produces the default, which is
                                          #also the case when one of the keys doesn't exist in the Hash.
                                          #Because to_i() will silently convert a found string with no leading numbers to 0, 
                                          #use Integer() instead, which will throw a descriptive error when trying to convert any String(or Hash or Array) to an int.

end

--output:--
p value_at_deep_key({a: {b: {c: "cat"}}}, [:a, :b, :c], 123)  #=> `Integer': invalid value for Integer(): "cat" (ArgumentError)
p value_at_deep_key({a: {b: {c: false}}}, [:a, :b, :c], 123)  #=> `Integer': can't convert false into Integer (TypeError)
p value_at_deep_key({a: {b: {c: nil}}},   [:a, :b, :c], 123)  #=> 123
p value_at_deep_key({z: {b: {c: "cat"}}}, [:a, :b, :c], 123)  #=> 123
p value_at_deep_key({a: {z: {c: "cat"}}}, [:a, :b, :c], 123)  #=> 123
p value_at_deep_key({a: {b: {z: "cat"}}}, [:a, :b, :c], 123)  #=> 123
p value_at_deep_key({a: {b: {c: "cat"}}}, [:a, :b], 123)      #=> `Integer': can't convert Hash into Integer (TypeError
p value_at_deep_key({a: {b: {c: "cat"}}}, [:a], 123)          #=> `Integer': can't convert Hash into Integer (TypeError)
p value_at_deep_key({z: {b: {c: "cat"}}}, [], 123)            #=> "No keys to lookup!"
p value_at_deep_key({z: {b: {c: "cat"}}}, [:a, :b, :c], 123)  #=> 123
p value_at_deep_key({a: {z: {c: "cat"}}}, [:a, :b, :c], 123)  #=> 123
p value_at_deep_key({a: {b: {z: "cat"}}}, [:a, :b, :c], 123)  #=> 123
p value_at_deep_key({a: {z: {c: "cat"}}}, [:a, :b, :c, :d], 123) #=> 123


p value_at_deep_key( 
  {'key1' => {'key2' => {'key3' => {'key4' => "4"}}}},
  %w[key1 key2 key3 key4],
  default=123,
)  #=> 4

p value_at_deep_key( 
  { 'key1' => {'key2' => {'key3' => "4"}}},
  %w[key1 key2 key3 key4],
  default=123,
)  #=> 123

p value_at_deep_key( 
   {
    'key1' => "1",
    'key2' => "2",
    'key3' => "3",
    'key4' => "4",
  },
  %w[key1 key2 key3 key4],
  default=123,
)  #=> 123

p value_at_deep_key( 
  { 'key1' => {'key2' => {'key3' => {'key4' => nil}}}},
  %w[key1 key2 key3 key4],
  default=123,
)  #=> 123

p value_at_deep_key( 
  {'key1' => {'key2' => {'key3' => {'key4' => 'hello'}}}},
  %w[key1 key2 key3 key4],
  default=123,
)  #=> `Integer': invalid value for Integer(): "hello" (ArgumentError)

但也许以下答案会更适合你:

如果你必须:

  1. 找到一个看起来像数字的字符串 - 转换为int或
  2. 默认
  3. ...换句话说没有错误,你可以这样做:

    def value_at_deep_key(hash, key_sequence, default=nil)
      value = hash
    
      key_sequence.each do |key|
        case value
        when Hash
          value = value[key]
        else
          value = hash.object_id  #Some unique value to signal that the Hash lookup failed.
          break
        end
      end
    
      begin
        value == hash.object_id ? default : Integer(value)
      rescue TypeError, ArgumentError #If the Hash lookup succeeded, but the value is: nil, true/false, a String that is not all numbers, Array, Hash, an object that neither responds to to_int() nor to_i()
        default
      end
    end
    
    p value_at_deep_key({a: {b: {c: "cat"}}}, [:a, :b, :c], 123)  #=> 123
    p value_at_deep_key({a: {b: {c: false}}}, [:a, :b, :c], 123)  #=> 123
    p value_at_deep_key({a: {b: {c: nil}}},   [:a, :b, :c], 123)  #=> 123
    p value_at_deep_key({z: {b: {c: "cat"}}}, [:a, :b, :c], 123)  #=> 123
    p value_at_deep_key({a: {z: {c: "cat"}}}, [:a, :b, :c], 123)  #=> 123
    p value_at_deep_key({a: {b: {z: "cat"}}}, [:a, :b, :c], 123)  #=> 123
    p value_at_deep_key({a: {b: {c: "cat"}}}, [:a, :b], 123)      #=> 123
    p value_at_deep_key({a: {b: {c: "cat"}}}, [:a], 123)          #=> 123
    p value_at_deep_key({z: {b: {c: "cat"}}}, [], 123)            #=> 123
    p value_at_deep_key({z: {b: {c: "cat"}}}, [:a, :b, :c], 123)  #=> 123
    p value_at_deep_key({a: {z: {c: "cat"}}}, [:a, :b, :c], 123)  #=> 123
    p value_at_deep_key({a: {b: {z: "cat"}}}, [:a, :b, :c], 123)  #=> 123
    p value_at_deep_key({a: {z: {c: "cat"}}}, [:a, :b, :c, :d], 123) #=> 123
    
    
    p value_at_deep_key( 
      {'key1' => {'key2' => {'key3' => {'key4' => "4"}}}},
      %w[key1 key2 key3 key4],
      default=123,
    )  #=> 4
    
    p value_at_deep_key( 
      { 'key1' => {'key2' => {'key3' => "4"}}},
      %w[key1 key2 key3 key4],
      default=123,
    )  #=> 123
    
    p value_at_deep_key( 
       {
        'key1' => "1",
        'key2' => "2",
        'key3' => "3",
        'key4' => "4",
      },
      %w[key1 key2 key3 key4],
      default=123,
    )  #=> 123
    
    p value_at_deep_key( 
      { 'key1' => {'key2' => {'key3' => {'key4' => nil}}}},
      %w[key1 key2 key3 key4],
      default=123,
    )  #=> 123
    
    p value_at_deep_key( 
      {'key1' => {'key2' => {'key3' => {'key4' => [1, 2, 3] }}}},
      %w[key1 key2 key3 key4],
      default=123,
    )  #=> 123