从给定值获取所有哈希键的最有效方法是什么。
my_hash = {"a"=>"aa", "b"=>"bb", "c"=>"bb"}
我想将散列“bb”作为输入值,并将所有键(b,c)作为数组返回
只返回一个键:
my_hash.index("bb")
# returns only b
这有效但似乎效率低下:
my_hash.select{|k,v| v == 'bb' }.map{|i| i[0] }
# returns b and c
我已阅读所有文档。我觉得我有一些显而易见的东西。
谢谢!
更新
我最终切换了哈希创建的键和值,并使用数组来表示值。这是一种更有效的解决方案。如果必要,请参阅下文,了解进行价值观察的最佳方法。
新结构:
my_hash = {"aa"=>["a"],"bb"=>["b","c"]}
答案 0 :(得分:30)
稍快一点:
my_hash.map{ |k,v| v=='bb' ? k : nil }.compact
仅对小哈希和单个查询较慢。如果需要为多个值请求反向映射,则速度更快。如果这对您的应用程序很重要,我建议您保留一张反向地图。
rev = Hash.new{ |h,k| h[k]=[] }
my_hash.each{ |k,v| rev[v] << k }
rev['bb']
基准:
require 'benchmark'
N = 1_000_000
my_hash = {"a"=>"aa", "b"=>"bb", "c"=>"bb"}
Benchmark.bmbm do |x|
x.report('select/map'){ N.times{
my_hash.select{|k,v|v=='bb'}.map{|i| i[0]}
}}
x.report('select/map/destructure'){ N.times{
my_hash.select{|k,v|v=='bb'}.map{|k,v| k}
}}
x.report('map/compact'){ N.times{
my_hash.map{|k,v|v=='bb' ? k : nil}.compact
}}
x.report('reverse map'){ N.times{
rev = Hash.new{|h,k|h[k]=[]}
my_hash.each{ |k,v| rev[v]<<k }
rev['bb']
}}
x.report('reject'){ N.times{
my_hash.reject{|k,v|v != "bb"}.keys
}}
end
#=> Rehearsal ----------------------------------------------------------
#=> select/map 1.950000 0.000000 1.950000 ( 1.950137)
#=> select/map/destructure 1.960000 0.010000 1.970000 ( 1.963740)
#=> map/compact 1.200000 0.000000 1.200000 ( 1.197340)
#=> reverse map 3.660000 0.000000 3.660000 ( 3.658245)
#=> reject 2.110000 0.000000 2.110000 ( 2.115805)
#=> ------------------------------------------------ total: 10.890000sec
#=>
#=> user system total real
#=> select/map 1.950000 0.000000 1.950000 ( 1.948784)
#=> select/map/destructure 1.970000 0.010000 1.980000 ( 1.966636)
#=> map/compact 1.190000 0.000000 1.190000 ( 1.192052)
#=> reverse map 3.670000 0.000000 3.670000 ( 3.664798)
#=> reject 2.140000 0.000000 2.140000 ( 2.135069)
答案 1 :(得分:4)
通过设计,关联数据结构可以快速查找给定密钥的值。它们不能有效地按值查找密钥。
在C ++中,std :: map不能有效地执行此操作,但是boost :: bimap可以。我不知道它是如何在下面工作的,但从逻辑上讲它可以通过创建两个映射(键值和键值)和一个界面使它看起来像是一个来工作。你可以做同样的事情。由于你的地图在一个方向看起来是单值的,而在另一个方向看起来是多值的(即没有一对一的对应关系),我怀疑有很多预建的库可以完全按照你想要的那样做。
免责声明:我不认识Ruby。
答案 2 :(得分:4)
由于哈希值甚至没有按值排序(可能允许使用二进制搜索,因此可能会加速一点),但是如果没有遍历整个哈希值,你就不会找到这样做的方法,所以你的解决方案实际上看起来效率最高。
替代方案可能是:
my_hash.reject{|k,v|v != "bb"}.keys
代码略短,但恕我直言也比你的版本更难理解。此外,reject方法构建了哈希的副本,因此它的内存密集程度更高。如果您不关心原始哈希,请使用就地工作的delete_if:
my_hash.delete_if{|k,v|v != "bb"}.keys
答案 3 :(得分:2)
另一种选择:my_hash.find_all{|k,v|v == "bb"}.map(&:first)
不是最快的,但对人眼很好:)。
使用 Phrogz的基准:
Rehearsal ----------------------------------------------------------
select/map 3.604000 0.000000 3.604000 ( 3.638208)
select/map/destructure 3.682000 0.000000 3.682000 ( 3.678210)
map/compact 2.620000 0.000000 2.620000 ( 2.620150)
reverse map 5.991000 0.000000 5.991000 ( 5.985342)
reject 3.572000 0.000000 3.572000 ( 3.612207)
find_all/map 2.964000 0.000000 2.964000 ( 2.965169)
------------------------------------------------ total: 22.433000sec
user system total real
select/map 3.619000 0.000000 3.619000 ( 3.634207)
select/map/destructure 3.698000 0.000000 3.698000 ( 3.702212)
map/compact 2.589000 0.000000 2.589000 ( 2.620150)
reverse map 5.913000 0.000000 5.913000 ( 6.013344)
reject 3.557000 0.000000 3.557000 ( 3.569204)
find_all/map 2.948000 0.000000 2.948000 ( 2.959169)