Ruby:如何让哈希表接受多个键?

6
我正在使用5个字符串(协议、源IP和端口、目标IP和端口)将它们用于在哈希表中存储一些值。问题是,如果源IP或端口与目标IP或端口交换,则键应该相同。
如果我在C#/Java/其他语言中进行此操作,我必须创建一个新类并重写hashcode()/equals()方法,但从我所读的内容来看,这似乎容易出错。因此,我想知道是否有更好的替代方案。

这个问题的措辞有些令人困惑。如果我理解正确,你并不是想让哈希表具有“多个键”(这很简单:{:a => "foo", :b => "bar"})。而是你希望在某些情况下,两个具有相同键但不同值的哈希表仍然可以被视为相等。是这样吗? - John Feminella
基本上,我想让hash["john doe"]等于hash["doe john"]。但是由于我有5个键,所以这有点复杂。 - zxcvbnm
3个回答

7
我直接从《Programming Ruby 1.9》一段文字中复制了一个段落:
哈希键必须响应消息 `hash` 并返回哈希码,给定键的哈希码不能更改。 哈希中使用的键还必须使用 `eql?` 进行比较。 如果两个键的 `eql?` 返回 `true`,则这些键的哈希码也必须相同。 这意味着某些类(例如 Array 和 Hash)不能方便地用作键,因为它们的哈希值可以根据其内容而变化。
因此,您可以生成这样的哈希表:`["#{source_ip} #{source_port}", "#{dest_ip} #{dest_port}", protocol.to_s].sort.join.hash`,使得当源和目标互换时结果是相同的。
例如:
source_ip = "1.2.3.4"
source_port = 1234

dest_ip = "5.6.7.8"
dest_port = 5678

protocol = "http"

def make_hash(s_ip, s_port, d_ip, d_port, proto)
    ["#{s_ip} #{s_port}", "#{d_ip} #{d_port}", proto.to_s].sort.join.hash
end

puts make_hash(source_ip, source_port, dest_ip, dest_port, protocol)
puts make_hash(dest_ip, dest_port, source_ip, source_port, protocol)

即使两次调用的参数顺序不同,这将输出相同的哈希值。正确地封装此功能到类中留给读者作为练习。


我不明白。我正在使用字符串,但只提到了数组和哈希表。虽然我是 Ruby 的新手。字符串是数组吗?还是我漏掉了其他什么东西? - zxcvbnm
代码片段 ["#{source_ip} #{source_port}", "#{dest_ip} #{dest_port}", protocol.to_s].sort.join 创建了一个临时数组,然后对其进行排序(这允许源和目标被交换),然后将其join成一个字符串。最后,在该字符串上调用hash应该足以在哈希中使用。 - Mark Rushakoff

0
你可以使用以下代码:
def create_hash(prot, s_ip, s_port, d_ip, d_port, value, x = nil)
  if x
    x[prot] = {s_ip => {s_port => {d_ip => {d_port => value}}}}
  else
    {prot => {s_ip => {s_port => {d_ip => {d_port => value}}}}}
  end
end

# Create a value
h = create_hash('www', '1.2.4.5', '4322', '4.5.6.7', '80', "Some WWW value")

# Add another value
create_hash('https', '1.2.4.5', '4562', '4.5.6.7', '443', "Some HTTPS value", h)

# Retrieve the values
puts h['www']['1.2.4.5']['4322']['4.5.6.7']['80']
puts h['https']['1.2.4.5']['4562']['4.5.6.7']['443']   

0

我认为这就是你的意思...

irb(main):001:0> traffic = []
=> []
irb(main):002:0> traffic << {:src_ip => "10.0.0.1", :src_port => "9999", :dst_ip => "172.16.1.1", :dst_port => 80, :protocol => "tcp"}
=> [{:protocol=>"tcp", :src_ip=>"10.0.0.1", :src_port=>"9999", :dst_ip=>"172.16.1.1", :dst_port=>80}]
irb(main):003:0> traffic << {:src_ip => "10.0.0.2", :src_port => "9999", :dst_ip => "172.16.1.1", :dst_port => 80, :protocol => "tcp"}
=> [{:protocol=>"tcp", :src_ip=>"10.0.0.1", :src_port=>"9999", :dst_ip=>"172.16.1.1", :dst_port=>80}, {:protocol=>"tcp", :src_ip=>"10.0.0.2", :src_port=>"9999", :dst_ip=>"172.16.1.1", :dst_port=>80}]

下一个有点相关的问题是如何存储IP。您可能希望使用IPAddr对象而不仅仅是字符串,以便更轻松地对结果进行排序。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接