What's the most efficient way to get all the hash keys from a given value.
从给定值获取所有哈希键的最有效方法是什么?
my_hash = {"a"=>"aa", "b"=>"bb", "c"=>"bb"}
I want to give the hash "bb" as an input value and get all they keys (b,c) back as an array
我想将散列“bb”作为输入值,并将所有键(b,c)作为数组返回
Returns only one key:
只返回一个关键:
my_hash.index("bb")
# returns only b
This works but seems inefficient:
这是可行的,但似乎效率低下:
my_hash.select{|k,v| v == 'bb' }.map{|i| i[0] }
# returns b and c
I've read all the docs. I feel like there's something obvious I'm missing.
我读过所有的文档。我觉得有些明显的东西我错过了。
Thanks!
谢谢!
Update:
更新:
I ended up switching the keys and values for the hash creation and going with an array for the values. This is a more efficient solution. See below for best ways to do value look ups if you have to.
最后,我切换了创建散列的键和值,并使用了值的数组。这是一个更有效的解决方案。如果有必要,请参阅下面的最佳方法来进行有价值的查找。
New structure:
新结构:
my_hash = {"aa"=>["a"],"bb"=>["b","c"]}
4 个解决方案
#1
28
Slightly faster:
稍快:
my_hash.map{ |k,v| v=='bb' ? k : nil }.compact
Slower for a small hash and single query only. Faster if you need to request the reverse mapping for multiple values. I'd recommend maintaining a reverse map if this is important to your application.
对于一个小的散列和单个查询,速度要慢一些。如果需要为多个值请求反向映射,那么速度会更快。如果这对您的应用程序很重要,我建议您维护一个反向映射。
rev = Hash.new{ |h,k| h[k]=[] }
my_hash.each{ |k,v| rev[v] << k }
rev['bb']
Benchmarked:
基准测试:
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)
#2
4
Associative data structures, by design, make it fast to look up a value given a key. They aren't intended to be efficient for looking up keys by value.
通过设计,关联数据结构可以快速查找给定键的值。它们并不是为了高效地按值查找键。
In C++ std::map does not do this efficiently, but boost::bimap does. I don't know how it works underneath, but logically it could work by creating two maps (key to value and value to key) and making an interface to make it look like they're one. You could do the same thing. Since your map appears to be single-valued in one direction and multivalued in the other direction (i.e., no one-to-one correspondence) I doubt there are many prebuilt libraries that do precisely what you want.
在c++ std:::map并不能有效地做到这一点,但是boost:::bimap做到了。我不知道它在底层是如何工作的,但从逻辑上来说,它可以通过创建两个映射(键值和值键值)和创建一个接口使其看起来像一个映射来工作。你也可以这么做。因为您的映射在一个方向上是单值的,而在另一个方向上是多值的。我怀疑是否有许多预先构建的库可以精确地完成您想要的任务。
Disclaimer: I do not know Ruby.
免责声明:我不认识Ruby。
#3
4
As hashes aren't even ordered by value (which MIGHT speed things up a bit by maybe allowing use of a binary search), you're not going to find a way of doing this without walking through the entire hash anyway, so your solution actually looks like the most efficient one.
由于散列甚至不是按值排序的(这可能会通过允许使用二进制搜索来加快速度),所以无论如何,如果不遍历整个散列,您将无法找到这样做的方法,因此您的解决方案实际上看起来是最有效的。
An alternative might be:
另一种可能是:
my_hash.reject{|k,v|v != "bb"}.keys
It's slightly shorter code-wise but IMHO also a bit harder to understand than your version. Also the reject method builds a copy of the hash so it's more memory-intensive. If you don't care about the original hash, use delete_if which works in-place:
它的代码稍短,但IMHO也比您的版本更难理解。同时,拒绝方法构建了散列的副本,因此更需要内存。如果您不关心原始的散列,请使用delete_if(如果有的话):
my_hash.delete_if{|k,v|v != "bb"}.keys
#4
2
Another alternative: my_hash.find_all{|k,v|v == "bb"}.map(&:first)
另一个选择:my_hash。find_all { | k、v | = =“bb”} . map(&:第一)
Not the fastest, but nice to human eyes :)
不是最快的,但对人眼来说很好。
Using Phrogz's benchmark:
使用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)
#1
28
Slightly faster:
稍快:
my_hash.map{ |k,v| v=='bb' ? k : nil }.compact
Slower for a small hash and single query only. Faster if you need to request the reverse mapping for multiple values. I'd recommend maintaining a reverse map if this is important to your application.
对于一个小的散列和单个查询,速度要慢一些。如果需要为多个值请求反向映射,那么速度会更快。如果这对您的应用程序很重要,我建议您维护一个反向映射。
rev = Hash.new{ |h,k| h[k]=[] }
my_hash.each{ |k,v| rev[v] << k }
rev['bb']
Benchmarked:
基准测试:
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)
#2
4
Associative data structures, by design, make it fast to look up a value given a key. They aren't intended to be efficient for looking up keys by value.
通过设计,关联数据结构可以快速查找给定键的值。它们并不是为了高效地按值查找键。
In C++ std::map does not do this efficiently, but boost::bimap does. I don't know how it works underneath, but logically it could work by creating two maps (key to value and value to key) and making an interface to make it look like they're one. You could do the same thing. Since your map appears to be single-valued in one direction and multivalued in the other direction (i.e., no one-to-one correspondence) I doubt there are many prebuilt libraries that do precisely what you want.
在c++ std:::map并不能有效地做到这一点,但是boost:::bimap做到了。我不知道它在底层是如何工作的,但从逻辑上来说,它可以通过创建两个映射(键值和值键值)和创建一个接口使其看起来像一个映射来工作。你也可以这么做。因为您的映射在一个方向上是单值的,而在另一个方向上是多值的。我怀疑是否有许多预先构建的库可以精确地完成您想要的任务。
Disclaimer: I do not know Ruby.
免责声明:我不认识Ruby。
#3
4
As hashes aren't even ordered by value (which MIGHT speed things up a bit by maybe allowing use of a binary search), you're not going to find a way of doing this without walking through the entire hash anyway, so your solution actually looks like the most efficient one.
由于散列甚至不是按值排序的(这可能会通过允许使用二进制搜索来加快速度),所以无论如何,如果不遍历整个散列,您将无法找到这样做的方法,因此您的解决方案实际上看起来是最有效的。
An alternative might be:
另一种可能是:
my_hash.reject{|k,v|v != "bb"}.keys
It's slightly shorter code-wise but IMHO also a bit harder to understand than your version. Also the reject method builds a copy of the hash so it's more memory-intensive. If you don't care about the original hash, use delete_if which works in-place:
它的代码稍短,但IMHO也比您的版本更难理解。同时,拒绝方法构建了散列的副本,因此更需要内存。如果您不关心原始的散列,请使用delete_if(如果有的话):
my_hash.delete_if{|k,v|v != "bb"}.keys
#4
2
Another alternative: my_hash.find_all{|k,v|v == "bb"}.map(&:first)
另一个选择:my_hash。find_all { | k、v | = =“bb”} . map(&:第一)
Not the fastest, but nice to human eyes :)
不是最快的,但对人眼来说很好。
Using Phrogz's benchmark:
使用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)