Search code examples
rubyhashcomplement

Find the (deep) complement of two hashes


The complement is the mathematical term for what I'm looking for, but for context and possibly more targeted solution: I have hash A, which can have nested hashes (i.e. they're N-dimensional), and I apply to it a process (over which I have no control) which returns hash B, which is hash A with some elements removed. From there on, I am trying to find the elements in A which have been removed in B.

For example: (note that I use symbols for simplicity. Keys will always be symbols, but values won't.)

a = {:a => :b,
     :c => {:d => :e, :f => :g},
     :h => :i,
     :j => {:k => :l, :m => :n},
     :o => {:p => :q, :r => :s},
     :t => :u}

b = {:h => :i,
     :j => {:k => :l, :m => :n},
     :o => {:r => :s},
     :t => :u}

complement(a,b)
#=> {:a => :b,
#    :c => {:d => :e, :f => :g},
#    :o => {:p => :q}}

What is the best (ruby-esque) way of doing this?


Solution

  • Came up with this

    a = {a: "thing", b: [1,2,3], c:2}
    b = {a: "thing", b: [1,2,3]}
    c= {}
    a.each do |k, v|
      c[k] = v unless b[k]
    end
    p c
    

    EDIT: Now checking nested hashes. But yes, there should be some better ruby way to do this.

    def check_deleted(a, b)
        c = Hash.new
        a.each do |k, v|
            if ! b.has_key? k
                c[k] = v
            elsif b[k].is_a? Hash
                c[k] = check_deleted(v, b[k])
            end
        end
        c
    end
    a = {a: "thing", b: [1,2,3], c:2, d: {e: 1, r:2}}
    b = {a: "thing", b: [1,2,3], d: {r:2}}
    
    p check_deleted(a,b) #=> {:c=>2, :d=>{:e=>1}}