使用相同值但不同键设置几个哈希参数

4

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

{ 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]等则为nil。
当然,我可以在哈希表外面进行检查或构造来完成我所需要的操作,但是我想知道是否可能设置这样的哈希表,而不使用任何循环?如果不行,最优雅的方法是什么?谢谢!


1
你想要什么不清楚吗?你想要什么输出?你有什么输入? - Arup Rakshit
@iAmRubuuu,我认为很清楚,他们正在寻找一种可以轻松初始化哈希的方法,而不需要循环,他们可以指定具有相同值的键的范围。 - Hunter McMillen
可能是重复的问题:如何在哈希中引用键的值。参考链接 - Joe Frambach
哦,你是在寻找引用相同值的键,还是初始化为相同值?我可能在重复投票时太快了。 - Joe Frambach
6个回答

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

它与普通哈希表的工作方式相同,只是在使用范围键时,它会将给定值设置在范围内的每个点上。

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

谢谢,太棒了!我只需要在一个地方使用这个功能,所以我不会专门为此编写一个类^^ 不过如果我第二次需要它(我敢打赌我会需要),我会使用你的实现 :) 谢谢! - konnigun

4
这里有什么特别的问题吗?
myhash = { 1 => [6,2,2], 2 => [7,4,5] }
(3..7).each { |k| myhash[k] = [7,2,1] }

谢谢伙计!这是最性感的,我会用它 :) - konnigun

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只是用于上面的显示目的。)

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也是如此。而且,它使哈希尽可能小。
这种方法或任何解决方案的缺点是,范围键可能重叠,导致冲突。在大多数提出的解决方案中,键会互相覆盖,这可能会导致返回错误的值。这种方法不会这样做,因为需要直接冲突才能覆盖范围键。
要修复这个问题,需要决定是否允许重叠,如果允许,则第一个找到的是否可以返回,或者应该有逻辑来确定“最佳匹配”,即适合的最小范围,或完全不同的一些标准。或者,如果值相同,应将重叠的部分合并为更大的范围吗?这是一个麻烦的问题。

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}

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]

下投票者:为什么要进行负投票?请解释一下。在进行负投票之前,SO 明确提示您输入原因。您无权忽略这一点。 - Arup Rakshit
踩负面评价不仅会损害别人,也会损害你自己。很多人会随意踩负面评价,但却不敢说出为什么要这样做。当我知道自己的回答很好,但还是被踩负面评价时,我认为这是因为他们太无知了,所以不必担心。只要保持良好回答与差评的比例,长期来看,一切都会水落石出。 - the Tin Man
@theTinMan 你说得对!我已经测试了我给出的代码。但是不知道谁在没有询问我的困惑就投了反对票。这是非常糟糕的做法。我会给你点赞。 - Arup Rakshit
我为每个答案都设置了点赞,包括你的答案,所以肯定不是我 ^^ - konnigun

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