2011-10-06 81 views
12

让我们这个例子:如何仅获得Ruby 1.9中有序哈希的子集?

d = {"a" => 1, "b" => 2, "c" => 3, "d" => 4} 

由于散列现在订购,我可能想从abcd数据。问题是我不能做d[0..1]d[2..3]

我可以做不过:

irb > d.to_a[0..1] 
=> [["a", 1], ["b", 2]] 

...但这种感觉凌乱,我不想投我哈希像这样的操作。

有没有更好的解决方案来处理这个问题?

# Holy Grail 
irb > d[0..1] 
=> {"a" => 1, "b" => 2} 

我可以看到如何编程自己这种方法,但可能有一些本机已经完成,我可以使用...?

+0

一个范围也可能是一个有效的散列键,所以你必须使用自定义的方法这一点。我不认为没有内置任何东西,顺便说一句。 – tokland

回答

7

那么你可以这样做:

> a = {"a" => 1, "b" => 2, "c" => 3, "d" => 4} 
> a.slice(*a.keys[0..1]) 
=> {"a" => 1, "b" => 1} 

至少散列不投,但它仍然不是在我看来很优雅。

+0

如果你只是用某种方法修补到Hash中,它会更优雅。 – tadman

+0

我喜欢这个解决方案^^ – marcgg

+0

这是一个很好的方式,但是你仍然必须得到所有的哈希键。但是如果实现不支持通过索引切片散列,那么很难找到更好的东西。 – tokland

4

如果您想对密钥进行比较以选择子集,可以使用Hash#select,它也适用于1.8.7,但返回一个数组数组(而不是您的示例)。

d.select {|k, v| k < 'c' } # => {"a"=>1, "b"=>2} 
d.select {|k, v| k < 'c' } # 1.8.7 => [["a", 1], ["b", 2]] 

您还可以使用的ActiveSupport的哈希扩展,增加了一个slice method to Hash也能正常工作,前提是你已经知道你想要的钥匙。

require 'active_support/core_ext/hash/slice' 
d.slice('a', 'b') # => {"a"=>1, "b"=>2} 
+0

在这种情况下,OP会希望Hash#slice_by_index具有Array#[]支持的所有选项。 – tokland

+0

是的,大多数时候,你知道你想要的键或者根据比较来选择它们,但是他希望它们基于索引,这不是我所回答的。最重要的是,我的答案很糟糕,因为它使用的是只有Hash#values_at的ActiveSupport,正如@DuoSRX所示。 –

+0

@BiHi在我的情况下,我想根据他们的索引得到钥匙,而不是他们的价值 – marcgg

2

也许有更好的方式来做到这一点,但是这是一个想法:

class Hash 
    def slice_by_index(a, b = nil) 
     k = if a.is_a?(Range) 
      keys[a] 
     elsif b.nil? 
      [keys[a]] 
     else 
      keys[a,b] 
     end 
     k.inject({}){|h, k| h[k] = self[k] ; h } 
    end 
end 

h = {"a" => 1, "b" => 2, "c" => 3, "d" => 4} 
p h.slice_by_index(1..3) #range 
p h.slice_by_index(2) #single element 
p h.slice_by_index(0,3) #start, lenght