处理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)
但也许以下答案会更适合你:
如果你必须:
- 找到的字符串看起来像一个数字 – 转换为int,或
- 默认值
…换句话说没有错误,你可以这样做:
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
- 如何使用RSpec测试`rand()`?
- I18n:使用’t(:test_key)’,’t(’test_key’)’和’t(’。test_key’)’有什么区别?
- 错误“无法加载此类文件 – 2.0 / Console_ext”在RubyMine中运行unit testing
- Ruby的put和write方法有什么区别?
- Rails 5 db migration:如何修复ActiveRecord :: ConcurrentMigrationError
- 如何将命名参数传递给Rake任务?
- Google地理编码API错误:超出查询限制。 – 铁路
- Rails ActiveStorage错误 – MessageVerifier-InvalidSignature
- 使用Class vs Module在Ruby中打包代码