2010-12-04 7 views
14

与えられた値からすべてのハッシュキーを取得する最も効率的な方法は何ですか?ruby​​指定された値に対して複数のハッシュキーを取得する効率的な方法

私は入力値としてハッシュに "BB" を与えるとバックのみ1つのキー配列

戻り値として、すべての彼らは、キー(B、C)を取得したい

my_hash = {"a"=>"aa", "b"=>"bb", "c"=>"bb"} 

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"]} 

答えて

27

少し速く:

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

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) 
+1

+1リバースマップソリューションの場合はニース、私は別の+1を追加します。 – Ernest

+1

ベンチマークのための+1 –

4

連想データ構造は、設計によって、それは速いキー与えられた値をルックアップするために作ります。値でキーを検索するのに効率的ではありません。

C++では、std :: mapはこれを効率的に実行しませんが、boost :: bimapはこれを行いません。私はそれがどのように下に動作するのかわかりませんが、論理的には、2つのマップ(キーと値とキーのキー)を作成し、それらが1つのように見えるようにするインターフェイスを作成することによって動作する可能性があります。あなたは同じことをすることができます。マップは一方向の単一値であり、他の方向の多値(すなわち、一対一対応)ではないように見えるので、あなたが望むものを正確に行う多数のライブラリが存在するのではないでしょうか。

免責事項:私はRubyを知らない。

4

ハッシュ値は値で順序付けされていません(おそらく二分探索の使用を許可することで少し速くなります)とにかく、ハッシュ全体を歩かずにこれを行う方法はありませんしたがって、あなたのソリューションは実際には最も効率的なソリューションに見えます。

選択肢は次のようになります。

my_hash.reject{|k,v|v != "bb"}.keys 

それは少し短いコードワイズが、あなたのバージョンよりも理解するのも少し難しく私見です。また、rejectメソッドはハッシュのコピーを作成して、よりメモリを消費します。あなたは、元のハッシュ気にしないのであれば、その場で働くdelete_ifを使用します。

my_hash.delete_if{|k,v|v != "bb"}.keys 
2

別の方法:

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) 
:人間の目:)

Phrogzのベンチマークを使用するmy_hash.find_all{|k,v|v == "bb"}.map(&:first)

ない最速のが、素敵な

+0

'Symbol#to_proc'を使わないことで、小さな毛を素早く作ることができます:' .map(&:first)=> 1.372s; .map {| x | x.first} => 1.357s; .map {| x | x [0]} => 1.28s' – Phrogz

+0

@Phrogz、お勧めします、thnx。 – Ernest

関連する問題