我需要Ruby中的双向哈希表。例如:
h = {:abc => 123, :xyz => 789, :qaz => 789, :wsx => [888, 999]}
h.fetch(:xyz) # => 789
h.rfetch(123) # => abc
h.rfetch(789) # => [:xyz, :qaz]
h.rfetch(888) # => :wsx
方法rfetch
表示反向提取,仅为我的提案。
注意三件事:
rfetch
将返回所有这些键,以数组形式打包。rfetch
会在数组的元素中查找它的参数。fetch
和rfetch
都应该在固定时间内执行。Ruby中是否存在这样的结构(包括外部库)?
我考虑过使用两个单向Hashes同步修改它们中的一个(并将其打包到类中以避免同步问题),但是我可以使用现有的解决方案吗?
答案 0 :(得分:7)
你可以很容易地自己构建一些东西,只需使用一个包含两个哈希的简单对象(一个用于向前方向,一个用于反向)。例如:
class BiHash
def initialize
@forward = Hash.new { |h, k| h[k] = [ ] }
@reverse = Hash.new { |h, k| h[k] = [ ] }
end
def insert(k, v)
@forward[k].push(v)
@reverse[v].push(k)
v
end
def fetch(k)
fetch_from(@forward, k)
end
def rfetch(v)
fetch_from(@reverse, v)
end
protected
def fetch_from(h, k)
return nil if(!h.has_key?(k))
v = h[k]
v.length == 1 ? v.first : v.dup
end
end
查找将像普通哈希查找一样运行(因为它们是正常哈希查找)。添加一些运算符,可能是不错的to_s
和inspect
实现,你很好。
这样的事情是这样的:
b = BiHash.new
b.insert(:a, 'a')
b.insert(:a, 'b')
b.insert(:a, 'c')
b.insert(:b, 'a')
b.insert(:c, 'x')
puts b.fetch(:a).inspect # ["a", "b", "c"]
puts b.fetch(:b).inspect # "a"
puts b.rfetch('a').inspect # [:a, :b]
puts b.rfetch('x').inspect # :c
puts b.fetch(:not_there).inspect # nil
puts b.rfetch('not there').inspect # nil
在您需要时构建工具没有任何问题。
答案 1 :(得分:5)
Ruby中没有内置的这种结构。
请注意Hash#rassoc
执行类似的操作,但它只返回第一个匹配项并且是线性时间:
h = {:abc => 123, :xyz => 789, :qaz => 789, :wsx => [888, 999]}
h.rassoc(123) # => [:abc, 123]
此外,无法以完全安全的方式在Ruby中满足您的要求,因为您将无法检测到数组值的更改。 E.g:
h = MyBidirectionalArray.new(:foo => 42, :bar => [:hello, :world])
h.rfetch(:world) # => :bar
h[:bar].shift
h[:bar] # => [:world]
h.rfetch(:world) # => should be nil, but how to detect this??
每次计算哈希以检测更改都会使您的查找成为线性时间。你可以复制数组值并冻结它们(就像Ruby对于字符串的哈希键一样!)
您似乎需要的是一个Graph类,它可能具有与Hash
不同的API,不是吗?您可以查看rgl或类似内容,但我不知道它们是如何实施的。
答案 2 :(得分:1)
有一个Hash#invert
方法(http://www.ruby-doc.org/core-2.1.0/Hash.html#method-i-invert)来实现这一目标。但它不会将多个值映射到数组。
答案 3 :(得分:0)
试试这个:
class Hash
def rfetch val
select { |k,v| v.is_a?(Array) ? v.include?(val) : v == val }.map { |x| x[0] }
end
end
答案 4 :(得分:0)
如果您没有对此哈希进行大量更新,则可以使用inverthash。