On Fri, Jul 15, 2011 at 10:38 AM, Stefano Mioli <stefano.mioli / gmail.com>wrote:

> Hi list,
>    let's say we want to write a method that compares two hashes for
> equality based on their keys.
>
> Each key is a symbol, and the corrisponding value can either be a string or
> another hash with this same properties.
>
> Two hashes, let's call them h1 and h2, are to be considered equal if:
>
> * they have the same keys
>
> * h1[:x] and h2[:x] are both hashes, and they are equal according to the
> very same rules you are reading ;)
>
> * they are both nil
>
> In short, we only care about the values when they are hashes.
> If they are strings, we don't care whether they are equal or not.
>
> To make an example, when fed these two hashes the method should return
> true:
>
> h1 = {
>  :one => "one",
>  :two => "two",
>  :three => {
>    :alpha => "alpha",
>    :bravo => "bravo",
>    :charlie => "charlie"
>  }
> }
>
> h2 = {
>  :one => "whatever",
>  :two => "hey",
>  :three => {
>    :alpha => "zulu",
>    :bravo => "oscar",
>    :charlie => "brown"
>  }
> }
>
> When fed these other two arrays, the method should return false:
>
> h3 = h1
>
> h4 = {
>  :one => "one",
>  :two => "two",
>  :three => {
>    :alpha => "alpha",
>    :bravo => "bravo",
>  }
> }
>
> The difference is that Ole :charlie is missing in h2.
> The values don't change to make it clear that we don't care about them.
>
> I came up with the following implementation that seems to work (at least
> according to the specs I wrote), but what I would like to ask is if it could
> be any simpler/more idiomatic/more elegant.
>
> Corner cases that one might spot are also good to know about.
>
> def compare(hash1, hash2)
>  args = [hash1, hash2]
>
>  return true if args.all? {|h| h.nil?}
>  return false if args.one? {|h| h.nil?}
>
>  hash1.each_key do |k|
>    values = [hash1[k], hash2[k]]
>
>    if values.all? {|h| h.is_a?(Hash)}
>      return compare(*values)
>    else
>      return false if values.one? {|value| value.nil? }
>    end
>  end
>
>  true
> end
>
>
> Should the code turn out to be unreadable, I posted it here[0] too.
>
> [0] = https://gist.github.com/**1084916 <https://gist.github.com/1084916>
>
>
> Thanks in advance.
>
> --
> Stefano
>
>
Looks like you've probably got a bug. You explicitly return in your code,
causing it to stop evaluating after the first hash:




def compare(hash1, hash2)
 args = [hash1, hash2]

 return true if args.all? {|h| h.nil?}
 return false if args.one? {|h| h.nil?}

 hash1.each_key do |k|
   values = [hash1[k], hash2[k]]

   if values.all? {|h| h.is_a?(Hash)}
     return compare(*values)
   else
     return false if values.one? {|value| value.nil? }
   end
 end

 true
end

h1 = {
 :one   => "one",
 :two   => { :alpha => "alpha" },
 :three => { :alpha => "alpha" },
}

h2 = h1.merge :three => { :bravo => 'bravo' }

h1 # => {:one=>"one", :two=>{:alpha=>"alpha"}, :three=>{:alpha=>"alpha"}}
h2 # => {:one=>"one", :two=>{:alpha=>"alpha"}, :three=>{:bravo=>"bravo"}}
compare h1, h2 # => true
RUBY_VERSION # => "1.9.2"