Let's take this example:
d = {"a" => 1, "b开发者_StackOverflow社区" => 2, "c" => 3, "d" => 4}
Since hashes are now ordered, I might want to get data from a
to b
or from c
to d
. The problem is that I can't do d[0..1]
or d[2..3]
.
I could however do:
irb > d.to_a[0..1]
=> [["a", 1], ["b", 2]]
... but this feels messy and I don't want to cast my hash for an operation like this.
Is there a cleaner solution to handle this?
# Holy Grail
irb > d[0..1]
=> {"a" => 1, "b" => 2}
I can see how to program myself such a method, but there might be something native already done that I could use...?
Well you could do :
> a = {"a" => 1, "b" => 2, "c" => 3, "d" => 4}
> a.slice(*a.keys[0..1])
=> {"a" => 1, "b" => 1}
At least the hash is not cast, but it's still not very elegant in my opinion.
If you want to make comparisons on the key to select the subset, you can use Hash#select
, it also works in 1.8.7 but returns an array of arrays instead (as per your example).
d.select {|k, v| k < 'c' } # => {"a"=>1, "b"=>2}
d.select {|k, v| k < 'c' } # 1.8.7 => [["a", 1], ["b", 2]]
You can also use ActiveSupport’s Hash extension that adds a slice
method to Hash that can also work, provided you already know the keys you want.
require 'active_support/core_ext/hash/slice'
d.slice('a', 'b') # => {"a"=>1, "b"=>2}
Probably there's a better way to do it but this is an idea:
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
精彩评论