处理Ruby中的许多

在纯ruby脚本中我有这个:

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 

有什么方法可以简化这个吗?

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

 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) 

就个人而言,我不喜欢使用救援这类事情 – 它可以掩盖错误。

 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来限制救援可以吞下的错误。 尽管有关使用流量控制exception的争论,但我更喜欢这一点以便于阅读。 在一个小function或一个衬管中,它在技术上甚至不会改变流量,因为它们都保留在一个位置。

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

我会像这样写它,并把它放在一个模块中,根据需要包含它。

 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

接受的答案不能很好地运作:

这是代码:

 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 `
'

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. 默认值

…换句话说没有错误,你可以这样做:

 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