On Fri, Jul 15, 2011 at 5:38 PM, Stefano Mioli <stefano.mioli / gmail.com> wr=
ote:
> Hi list,
> =A0 =A0let's say we want to write a method that compares two hashes for e=
quality
> 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

It seems your spec is not very precise because your bullet list mixes
AND and OR.  I assume you meant

Two objects o1 and o2 are considered equal for this relation if and only if

( o1 is nil AND o2 is nil ) OR
( o1 is a Hash AND o2 is a Hash AND
  o1.keys =3D=3D o2.keys AND
  for each key k ( o1[k] is not a Hash OR
                          o2[k] is not a Hash OR
                          o1[k] equals o2[k] according to this relation ) )

> 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 tr=
ue:
>
> h1 =3D {
> =A0:one =3D> "one",
> =A0:two =3D> "two",
> =A0:three =3D> {
> =A0 =A0:alpha =3D> "alpha",
> =A0 =A0:bravo =3D> "bravo",
> =A0 =A0:charlie =3D> "charlie"
> =A0}
> }
>
> h2 =3D {
> =A0:one =3D> "whatever",
> =A0:two =3D> "hey",
> =A0:three =3D> {
> =A0 =A0:alpha =3D> "zulu",
> =A0 =A0:bravo =3D> "oscar",
> =A0 =A0:charlie =3D> "brown"
> =A0}
> }
>
> When fed these other two arrays, the method should return false:
>
> h3 =3D h1
>
> h4 =3D {
> =A0:one =3D> "one",
> =A0:two =3D> "two",
> =A0:three =3D> {
> =A0 =A0:alpha =3D> "alpha",
> =A0 =A0:bravo =3D> "bravo",
> =A0}
> }
>
> 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 co=
uld
> be any simpler/more idiomatic/more elegant.
>
> Corner cases that one might spot are also good to know about.
>
> def compare(hash1, hash2)

I would rename arguments because these need not be Hashes.

> =A0args =3D [hash1, hash2]
>
> =A0return true if args.all? {|h| h.nil?}
> =A0return false if args.one? {|h| h.nil?}
>
> =A0hash1.each_key do |k|
> =A0 =A0values =3D [hash1[k], hash2[k]]
>
> =A0 =A0if values.all? {|h| h.is_a?(Hash)}
> =A0 =A0 =A0return compare(*values)
> =A0 =A0else
> =A0 =A0 =A0return false if values.one? {|value| value.nil? }
> =A0 =A0end
> =A0end
>
> =A0true
> end

Hm, I'd probably remove #all?, #any? and the like and code conditions
directly - especially since you are always dealing with two items
because it's likely faster.  Your code creates a lot of temporary
Arrays along the way.

Btw, I believe your implementation misses the case where hash2's key
set is a superset of hash1's, i.e. contains additional keys.

Kind regards

robert

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/