Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I extract a sub-hash from a hash?

Tags:

ruby

hash

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} 
like image 671
sawa Avatar asked Jan 26 '12 21:01

sawa


People also ask

How do I get the hash value in Ruby?

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.

How do you merge hashes in Ruby?

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.

How do you make hash hash in Ruby?

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.


2 Answers

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.

like image 148
skalee Avatar answered Sep 28 '22 07:09

skalee


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}  
like image 38
Gazler Avatar answered Sep 28 '22 07:09

Gazler