I have a hash:
h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
What is the best way to extract a sub-hash like this?
h1.extract_subhash(:b, :d, :e, :f) # => {:b => :B, :d => :D} h1 #=> {:a => :A, :c => :C}
hash.fetch(key) { | key | block } Returns a value from hash for the given key. If the key can't be found, and there are no other arguments, it raises an IndexError exception; if default is given, it is returned; if the optional block is specified, its result is returned.
We can merge two hashes using the merge() method. When using the merge() method: Each new entry is added to the end. Each duplicate-key entry's value overwrites the previous value.
Ruby hash creation. A hash can be created in two basic ways: with the new keyword or with the hash literal. The first script creates a hash and adds two key-value pairs into the hash object. A hash object is created.
ActiveSupport
, at least since 2.3.8, provides four convenient methods: #slice
, #except
and their destructive counterparts: #slice!
and #except!
. They were mentioned in other answers, but to sum them in one place:
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}
Note the return values of the bang methods. They will not only tailor existing hash but also return removed (not kept) entries. The Hash#except!
suits best the example given in the question:
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
does not require whole Rails, is pretty lightweight. In fact, a lot of non-rails gems depend on it, so most probably you already have it in Gemfile.lock. No need to extend Hash class on your own.
If you specifically want the method to return the extracted elements but h1 to remain the same:
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}
And if you want to patch that into the Hash class:
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
If you just want to remove the specified elements from the hash, that is much easier using 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}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With