如何从哈希中提取子哈希?

我有一个哈希:

h1 = {:a => :A, :b => :B, :c => :C, :d => :D} 

提取这样的子哈希的最好方法是什么?

 h1.extract_subhash(:b, :d, :e, :f) # => {:b => :B, :d => :D} h1 #=> {:a => :A, :c => :C} 

如果您特别想要方法返回提取的元素,但h1保持不变:

 h1 = {:a => :A, :b => :B, :c => :C, :d => :D} h2 = h1.select {|key, value| [:b, :d, :e, :f].include?(key) } # => {:b=>:B, :d=>:D} h1 = Hash[h1.to_a - h2.to_a] # => {:a=>:A, :c=>:C} 

如果你想修补Hash类:

 class Hash def extract_subhash(*extract) h2 = self.select{|key, value| extract.include?(key) } self.delete_if {|key, value| extract.include?(key) } h2 end end 

如果你只是想从哈希中删除指定的元素,使用delete_if更容易。

 h1 = {:a => :A, :b => :B, :c => :C, :d => :D} h1.delete_if {|key, value| [:b, :d, :e, :f].include?(key) } # => {:a=>:A, :c=>:C} h1 # => {:a=>:A, :c=>:C} 

ActiveSupport至less从2.3.8开始,提供了四种方便的方法: #slice#except和它们的破坏性的对应物: #slice!#except! 。 他们在其他答案中被提到,但总结在一个地方:

 x = {a: 1, b: 2, c: 3, d: 4} # => {:a=>1, :b=>2, :c=>3, :d=>4} x.slice(:a, :b) # => {:a=>1, :b=>2} x # => {:a=>1, :b=>2, :c=>3, :d=>4} x.except(:a, :b) # => {:c=>3, :d=>4} x # => {:a=>1, :b=>2, :c=>3, :d=>4} 

请注意爆炸方法的返回值。 他们将不仅裁剪现有的散列,而且还将返回删除(不保留)条目。 Hash#except! 最适合问题中给出的例子:

 x = {a: 1, b: 2, c: 3, d: 4} # => {:a=>1, :b=>2, :c=>3, :d=>4} x.except!(:c, :d) # => {:a=>1, :b=>2} x # => {:a=>1, :b=>2} 

ActiveSupport不需要整个Rails,是相当轻量级的。 事实上,很多非轨道gem都依赖于它,所以很可能你已经在Gemfile.lock中拥有它了。 无需自行扩展哈希类。

如果你使用rails , 哈希#切片是要走的路。

 {:a => :A, :b => :B, :c => :C, :d => :D}.slice(:a, :c) # => {:a => :A, :c => :C} 

如果你不使用rails , Hash#values_at会按照你询问的顺序返回值,所以你可以这样做:

 def slice(hash, *keys) Hash[ [keys, hash.values_at(*keys)].transpose] end def except(hash, *keys) desired_keys = hash.keys - keys Hash[ [desired_keys, hash.values_at(*desired_keys)].transpose] end 

例如:

 slice({foo: 'bar', 'bar' => 'foo', 2 => 'two'}, 'bar', 2) # => {'bar' => 'foo', 2 => 'two'} except({foo: 'bar', 'bar' => 'foo', 2 => 'two'}, 'bar', 2) # => {:foo => 'bar'} 

说明:

{:a => 1, :b => 2, :c => 3}我们要{:a => 1, :b => 2}

 hash = {:a => 1, :b => 2, :c => 3} keys = [:a, :b] values = hash.values_at(*keys) #=> [1, 2] transposed_matrix =[keys, values].transpose #=> [[:a, 1], [:b, 2]] Hash[transposed_matrix] #=> {:a => 1, :b => 2} 

如果你觉得像猴子补丁是要走的路,以下是你想要的:

 module MyExtension module Hash def slice(*keys) ::Hash[[keys, self.values_at(*keys)].transpose] end def except(*keys) desired_keys = self.keys - keys ::Hash[[desired_keys, self.values_at(*desired_keys)].transpose] end end end Hash.include MyExtension::Hash 
 module HashExtensions def subhash(*keys) keys = keys.select { |k| key?(k) } Hash[keys.zip(values_at(*keys))] end end Hash.send(:include, HashExtensions) {:a => :A, :b => :B, :c => :C, :d => :D}.subhash(:a) # => {:a => :A} 

您可以使用在ActiveSupport的核心扩展中可用的slice!(*键)

 initial_hash = {:a => 1, :b => 2, :c => 3, :d => 4} extracted_slice = initial_hash.slice!(:a, :c) 

现在是initial_hash

 {:b => 2, :d =>4} 

extract_slide现在是

 {:a => 1, :c =>3} 

您可以slice.rb in ActiveSupport 3.1.3查看slice.rb in ActiveSupport 3.1.3

 class Hash def extract(*keys) key_index = Hash[keys.map{ |k| [k, true] }] # depends on the size of keys partition{ |k, v| key_index.has_key?(k) }.map{ |group| Hash[group] } end end h1 = {:a => :A, :b => :B, :c => :C, :d => :D} h2, h1 = h1.extract(:b, :d, :e, :f) 
 h1 = {:a => :A, :b => :B, :c => :C, :d => :D} keys = [:b, :d, :e, :f] h2 = (h1.keys & keys).each_with_object({}) { |k,h| h.update(k=>h1.delete(k)) } #=> {:b => :B, :d => :D} h1 #=> {:a => :A, :c => :C} 

这段代码将你要求的function注入到Hash类中:

 class Hash def extract_subhash! *keys to_keep = self.keys.to_a - keys to_delete = Hash[self.select{|k,v| !to_keep.include? k}] self.delete_if {|k,v| !to_keep.include? k} to_delete end end 

并产生你提供的结果:

 h1 = {:a => :A, :b => :B, :c => :C, :d => :D} p h1.extract_subhash!(:b, :d, :e, :f) # => {b => :B, :d => :D} p h1 #=> {:a => :A, :c => :C} 

注意:这个方法实际上返回提取的键/值。

如果你使用rails,使用Hash.except可能会很方便

 h = {a:1, b:2} h1 = h.except(:a) # {b:2} 

以下是build议方法的快速性能比较, #select似乎是最快的

 k = 1_000_000 Benchmark.bmbm do |x| x.report('select') { k.times { {a: 1, b: 2, c: 3}.select { |k, _v| [:a, :b].include?(k) } } } x.report('hash transpose') { k.times { Hash[ [[:a, :b], {a: 1, b: 2, c: 3}.fetch_values(:a, :b)].transpose ] } } x.report('slice') { k.times { {a: 1, b: 2, c: 3}.slice(:a, :b) } } end Rehearsal -------------------------------------------------- select 1.640000 0.010000 1.650000 ( 1.651426) hash transpose 1.720000 0.010000 1.730000 ( 1.729950) slice 1.740000 0.010000 1.750000 ( 1.748204) ----------------------------------------- total: 5.130000sec user system total real select 1.670000 0.010000 1.680000 ( 1.683415) hash transpose 1.680000 0.010000 1.690000 ( 1.688110) slice 1.800000 0.010000 1.810000 ( 1.816215) 

细化将如下所示:

 module CoreExtensions module Extractable refine Hash do def extract(*keys) select { |k, _v| keys.include?(k) } end end end end 

并使用它:

 using ::CoreExtensions::Extractable { a: 1, b: 2, c: 3 }.extract(:a, :b)