2011-10-06 79 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