Snippet: Hash#with_only, #merge_existing

Here are some potentiall useful one-liner methods for extracting and merging only parts of Hashes. I was surprised to find out that Hash didn’t already have built-in methods to do these things. Maybe they’re considered so obvious that they don’t need to be built-in, but I think the readability gain is worth it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Hash

  # Return a copy of the receiver with only the given keys.
  # 
  #   >> {:a => 1, :b => 2, :c => 3}.with_only(:a, :c)
  #   => {:a => 1, :c => 3}
  # 
  def with_only( *keys )
    reject{ |k,v| not keys.include?(k) }
  end

  # Return a copy of the receiver without the given keys.
  # 
  #   >> {:a => 1, :b => 2, :c => 3}.without(:a, :c)
  #   => {:b => 2}
  # 
  def without( *keys )
    reject{ |k,v| keys.include?(k) }
  end


  # Like #merge, but only accepts keys that the receiver already has.
  # 
  #   >> {:a => 1, :b => 2}.merge_existing({:a => 0, :c => 0})
  #   => {:a => 0, :b => 2}
  # 
  def merge_existing( other )
    merge( other.with_only(*self.keys) )
  end

  # Like #merge_existing, but modifies the receiver.
  # 
  def merge_existing!( other )
    merge!( other.with_only(*self.keys) )
  end

  alias :update_existing :merge_existing!

end

2 thoughts on “Snippet: Hash#with_only, #merge_existing”

  1. Hello Jacius!!!

    Really good one liners.
    But why don’t do with_only like this:

    def with_only( *keys )
    select{ |k,v| keys.include?(k) }
    end

    I guess reject + not is the same as select….

    See ya

  2. @Daniel: That was my first thought as well. But strangely, Hash#select doesn’t return a Hash, it returns nested arrays, like `[[:a, 1], [:b, 2]]`. So, using reject turned out to be the simplest way.

Comments are closed.