Ruby эффективный способ получить несколько хеш-ключей для заданного значения
Какой самый эффективный способ получить все хэш-ключи от заданного значения.
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
Я прочитал все документы. Я чувствую, что там что-то очевидное, что мне не хватает.
Спасибо!
Update:
В итоге я переключил ключи и значения для создания хэша и перешел к массиву для значений. Это более эффективное решение. См. Ниже, где можно найти лучшие способы поиска ценности, если вам нужно.
Новая структура:
my_hash = {"aa"=>["a"],"bb"=>["b","c"]}
Ответы
Ответ 1
Чуть быстрее:
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)
Ответ 2
Ассоциативные структуры данных, по дизайну, позволяют быстро найти значение с учетом ключа. Они не предназначены для эффективного поиска ключей по значению.
В С++ std:: map не делает это эффективно, но boost:: bimap делает. Я не знаю, как это работает под ним, но логически это может сработать, создав две карты (ключ к значению и ценности для ключа) и создав интерфейс, чтобы он выглядел так, будто он один. Вы могли бы сделать то же самое. Так как ваша карта представляется однозначной в одном направлении и многозначной в другом направлении (т.е. Нет однозначного соответствия), я сомневаюсь, что существует множество готовых библиотек, которые делают именно то, что вы хотите.
Отказ от ответственности: я не знаю Ruby.
Ответ 3
Поскольку хэши даже не упорядочиваются по значению (которые МОГУТ ускорить процесс, возможно, позволяя использовать двоичный поиск), вы не найдете способ сделать это, не пройдя весь хэш, поэтому ваше решение на самом деле выглядит наиболее эффективным.
Альтернативой может быть:
my_hash.reject{|k,v|v != "bb"}.keys
Он немного короче кода, но IMHO также немного сложнее понять, чем ваша версия. Также метод отклонения создает копию хэша, поэтому он более интенсивно использует память. Если вам не нужен оригинальный хэш, используйте delete_if, который работает на месте:
my_hash.delete_if{|k,v|v != "bb"}.keys
Ответ 4
Другая альтернатива: 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)