从 ruby 哈希中的匹配键检索值的更好方法
Better way to retrieve values from matching keys in a ruby hash
我正在尝试为 SOAP API 创建一个更快的解析器,它将 xml 转换为散列,并将键与基于 YML 结构的内存加载模式相匹配.我使用 Nori 将 xml 解析为散列:
hash1 = { :key1 => { :@attr1=> "value1", :key2 => { :@attribute2 => "value2" }}}
(旧的 ruby 语法使键的属性清晰)
同时我有一个常量加载到内存中并存储我的操作所需的相关键:
hash2 = {:key1 => { :key2 => { :@attribute2 => nil }}}
(旧的 ruby 语法使键的属性清晰)
我需要以最有效的方式将第一个哈希值与第二个哈希值匹配。根据我的理解,有一些方法可以做到:
同时迭代两个散列键,但使用第二个作为原点:
def iterate(hash2, hash1)
hash2.each do |k, v|
if v.is_a? Hash
iterate(hash2[k], hash1[k])
else
hash2[k] = hash1[k]
end
end
end
(多行语法,?清晰?)
我想到了一些问题:
- 有没有更有效的方法来做到这一点而不必迭代
我所有的钥匙?
- 这比直接访问密钥更有效吗?
- 是否有更好的方法使用 hash2 将 XML 解析为散列
在访客模式中?
没有迭代的解决方案可以递归select:
hash1 = { :key1 => { :@attr1=> "value1",
:key2 => { :@attribute2 => "value2" },
:key3 => { :@attribute4 => "value4" } },
:key2 => { :@attribute3 => "value3" }
}
hash2 = { :key1 => { :key2 => { :@attribute2 => nil }},
:key2 => { :@attribute3 => nil }
}
def deep_select h1, h2
h1.select do |k, _|
h2.keys.include? k
end.map do |k, v|
v.is_a?(Hash) ? [k, deep_select(v, h2[k])] : [k, v]
end.to_h
end
puts deep_select hash1, hash2
#⇒ {:key1=>{:key2=>{:@attribute2=>"value2"}}, :key2=>{:@attribute3=>"value3"}}}
一般来说,select
应该比 each
好,因为复杂的 selection 算法。实际上,差别只有20%左右。
require 'benchmark'
hash = (1..1_000_000).map { |i| ["key#{i}", i] }.to_h
n = 5
Benchmark.bm do |x|
garbage = 0
x.report { hash.each { |_, v| garbage += v } }
x.report { hash.select { |_, v| (v % 1000).zero? } }
end
# user system total real
# 0.400000 0.000000 0.400000 ( 0.391305)
# 0.320000 0.000000 0.320000 ( 0.321312)
我正在尝试为 SOAP API 创建一个更快的解析器,它将 xml 转换为散列,并将键与基于 YML 结构的内存加载模式相匹配.我使用 Nori 将 xml 解析为散列:
hash1 = { :key1 => { :@attr1=> "value1", :key2 => { :@attribute2 => "value2" }}}
(旧的 ruby 语法使键的属性清晰)
同时我有一个常量加载到内存中并存储我的操作所需的相关键:
hash2 = {:key1 => { :key2 => { :@attribute2 => nil }}}
(旧的 ruby 语法使键的属性清晰)
我需要以最有效的方式将第一个哈希值与第二个哈希值匹配。根据我的理解,有一些方法可以做到:
同时迭代两个散列键,但使用第二个作为原点:
def iterate(hash2, hash1)
hash2.each do |k, v|
if v.is_a? Hash
iterate(hash2[k], hash1[k])
else
hash2[k] = hash1[k]
end
end
end
(多行语法,?清晰?)
我想到了一些问题:
- 有没有更有效的方法来做到这一点而不必迭代 我所有的钥匙?
- 这比直接访问密钥更有效吗?
- 是否有更好的方法使用 hash2 将 XML 解析为散列 在访客模式中?
没有迭代的解决方案可以递归select:
hash1 = { :key1 => { :@attr1=> "value1",
:key2 => { :@attribute2 => "value2" },
:key3 => { :@attribute4 => "value4" } },
:key2 => { :@attribute3 => "value3" }
}
hash2 = { :key1 => { :key2 => { :@attribute2 => nil }},
:key2 => { :@attribute3 => nil }
}
def deep_select h1, h2
h1.select do |k, _|
h2.keys.include? k
end.map do |k, v|
v.is_a?(Hash) ? [k, deep_select(v, h2[k])] : [k, v]
end.to_h
end
puts deep_select hash1, hash2
#⇒ {:key1=>{:key2=>{:@attribute2=>"value2"}}, :key2=>{:@attribute3=>"value3"}}}
一般来说,select
应该比 each
好,因为复杂的 selection 算法。实际上,差别只有20%左右。
require 'benchmark'
hash = (1..1_000_000).map { |i| ["key#{i}", i] }.to_h
n = 5
Benchmark.bm do |x|
garbage = 0
x.report { hash.each { |_, v| garbage += v } }
x.report { hash.select { |_, v| (v % 1000).zero? } }
end
# user system total real
# 0.400000 0.000000 0.400000 ( 0.391305)
# 0.320000 0.000000 0.320000 ( 0.321312)