设置几个具有相同值但不同键的哈希参数

时间:2013-04-01 20:53:08

标签: ruby

我的应用程序中有一个构造,我需要这样的哈希:

{ 1 => [6,2,2], 2 => [7,4,5], (3..7) => [7,2,1] }

所以我希望键3,4,5,6和7具有相同的值。
当然上面的示例不起作用,因为Ruby是智能的并且将哈希键设置为给定:它将范围设置为键:)所以我只能访问我的值my_hash[(3..7)]my_hash[3]my_hash[4]等等。是的 当然我可以在哈希之外进行检查或构造来做我需要的东西,但是我很好奇是否可以在不使用哈希声明之外的任何循环的情况下设置这样的哈希?如果没有,最优雅的是什么?谢谢!

6 个答案:

答案 0 :(得分:5)

您可以继承Hash以便更容易构建此类哈希:

class RangedHash < Hash
  def []=(key, val)
    if key.is_a? Range
      key.each do |k|
        super k, val
      end
    else
      super key, val
    end
  end
end

它的工作方式与普通哈希相同,除非使用Range键,它会在Range中的每个点设置给定值。

irb(main):014:0> h = RangedHash.new
=> {}
irb(main):015:0> h[(1..5)] = 42
=> 42
irb(main):016:0> h[1]
=> 42
irb(main):017:0> h[5]
=> 42
irb(main):018:0> h['hello'] = 24
=> 24
irb(main):019:0> h['hello']
=> 24

答案 1 :(得分:4)

这有什么特别的错吗?

myhash = { 1 => [6,2,2], 2 => [7,4,5] }
(3..7).each { |k| myhash[k] = [7,2,1] }

答案 2 :(得分:3)

我认为没有办法使用文字哈希语法设置多个密​​钥,或者没有一些迭代,但这里有一个简单的方法来迭代:

irb(main):007:0> h = { 1 => [6,2,2], 2 => [7,4,5] }; (3..7).each {|n| h[n] = [7,2,1]}; h
=> {1=>[6, 2, 2], 2=>[7, 4, 5], 3=>[7, 2, 1], 4=>[7, 2, 1], 5=>[7, 2, 1], 6=>[7, 2, 1], 7=>[7, 2, 1]}

(请注意,尾随; h仅用于上述显示目的。)

答案 3 :(得分:2)

我不喜欢为范围中的每个可能条目创建单独的键/值对的想法。它根本不可扩展,特别是对于宽范围。考虑这个小范围:

'a' .. 'zz'

这会导致702个额外的密钥。试试('a'..'zz').to_a吧。前进。我等一下。

不是创建密钥,而是拦截查找。重用RangedHash班级名称:

class RangedHash < Hash
  def [](key)
    return self.fetch(key) if self.key? key

    self.keys.select{ |k| k.is_a? Range }.each do |r_k|
      return self.fetch(r_k) if r_k === key
    end

    nil
  end
end

foo = RangedHash.new
foo[1]    = [6,2,2]
foo[2]    = [7,4,5]
foo[3..7] = [7,2,1]

此时foo看起来像是:

{1=>[6, 2, 2], 2=>[7, 4, 5], 3..7=>[7, 2, 1]}

测试方法:

require 'pp'
3.upto(7) do |i|
  pp foo[i]
end

哪个输出:

[7, 2, 1]
[7, 2, 1]
[7, 2, 1]
[7, 2, 1]
[7, 2, 1]

对于范围中的任何值,此输出与该范围关联的值。超出范围但仍在散列中定义的值正常工作,对于散列中不存在的键返回nil也是如此。并且,它使散列尽可能小。

此问题的缺点或问题的任何解决方案是范围可能重叠的键,从而导致冲突。在大多数提议的解决方案中,密钥会相互踩踏,这将最终导致返回错误的值。此方法不会这样做,因为它会直接冲突来覆盖范围键。

要解决此问题,需要决定是否允许重叠,如果是,则可以返回找到的第一个,或者是否存在确定“最佳拟合”的逻辑,即适合的最小范围,或完全一些其他标准。或者,如果值相同,是否应将重叠连接起来以形成更大的范围?这是一堆蠕虫。

答案 4 :(得分:1)

直接修补哈希,但与Luke's...

的想法相同
class Hash
  alias_method :orig_assign, '[]='
  def []= k, v
    if k.is_a? Range
      k.each { |i| orig_assign i, v }
      v
    else
      orig_assign k, v
    end
  end
end

t = {}
t[:what] = :ever
t[3..7] = 123
p t # => {5=>123, 6=>123, 7=>123, 3=>123, 4=>123, :what=>:ever}

答案 5 :(得分:0)

以下是更多方法:

h = { 1 => [6,2,2], 2 => [7,4,5], (3..7) => [7,2,1] } 

def my_hash(h,y)
  h.keys.each do |x|
    if (x.instance_of? Range) and (x.include? y) then
      return p h[x]
    end
  end
p h[y]
end

my_hash(h,2)
my_hash(h,3)
my_hash(h,1)
my_hash(h,10)
my_hash(h,5)
my_hash(h,(3..7))

输出:

[7, 4, 5]
[7, 2, 1]
[6, 2, 2]
nil
[7, 2, 1]
[7, 2, 1]