Hash, ==, key-value comparison

W

walter

Ok,

ALthough the docs don't mention it, Hash comparison using == checks
not only the key value pairs, but also the default value / default
procs.

So how does one compare 2 hashes key-value pairs. I can see that it
might be useful to distinguish between 2 hashes whose key-value pairs
are the same but the default value/default proc are different, but I
would think that would be much less often than wanting to know if the
key-value pairs are the same.

Maybe I am thinking about this all wrong, but I always viewed a Hash
as a Hash, and yes some might have a default value and some might
have a default proc, but it is the data (the key-value pairs) that,
to me, make up the Hash. The default data/procs are convenience that
make it easier to work with a Hash, but I would think that {} and
Hash.new(3) would be == if their key-value pairs would be equal.

Assuming that I am in the minority here about ==, what is the proper
way to see if 2 Hashes key-value pairs are the same.



Walt
*****************************************************
Walter Szewelanczyk
IS Director
M.W. Sewall & CO. email : (e-mail address removed)
259 Front St. Phone : (207) 442-7994 x 128
Bath, ME 04530 Fax : (207) 443-6284
*****************************************************
 
E

Elias Athanasopoulos

Ok,

ALthough the docs don't mention it, Hash comparison using == checks
not only the key value pairs, but also the default value / default
procs.

So how does one compare 2 hashes key-value pairs. I can see that it

I can contribute a patch to:

* Change '==' method to compare only key-value pairs.
* Add Hash#=== method to act like current Hash#==

Comments?

Regards,
 
W

walter

On Fri, Apr 16, 2004 at 02:04:10AM +0900, (e-mail address removed) wrote: >
Ok, > > ALthough the docs don't mention it, Hash comparison using ==
checks > not only the key value pairs, but also the default value /
default > procs. > > So how does one compare 2 hashes key-value pairs.
I can see that it

I can contribute a patch to:

* Change '==' method to compare only key-value pairs.
* Add Hash#=== method to act like current Hash#==

Comments?

Personally, I like the idea. You get my vote!


Thanks,


Walt
*****************************************************
Walter Szewelanczyk
IS Director
M.W. Sewall & CO. email : (e-mail address removed)
259 Front St. Phone : (207) 442-7994 x 128
Bath, ME 04530 Fax : (207) 443-6284
*****************************************************
 
E

Elias Athanasopoulos

Personally, I like the idea. You get my vote!

I will add a ChangeLog entry if it is accepted.

elathan@velka:~/hacking/ruby> cat ../test.rb
h1 = { "a" => 1, "b" => 2}

h2 = Hash.new(2)
h2["a"] = 1
h2["b"] = 2

puts h1 == h2
puts h1 === h2
elathan@velka:~/hacking/ruby> ./ruby ../test.rb
true
false

Enjoy,
--
University of Athens I bet the human brain
Physics Department is a kludge --Marvin Minsky


--- /home/elathan/hacking/ruby/hash.c.orig 2004-04-15 04:46:29.000000000 +0300
+++ /home/elathan/hacking/ruby/hash.c 2004-04-15 04:52:39.000000000 +0300
@@ -1425,6 +1425,48 @@
}
if (RHASH(hash1)->tbl->num_entries != RHASH(hash2)->tbl->num_entries)
return Qfalse;
+
+ data.tbl = RHASH(hash2)->tbl;
+ data.result = Qtrue;
+ st_foreach(RHASH(hash1)->tbl, equal_i, (st_data_t)&data);
+
+ return data.result;
+}
+
+/*
+ * call-seq:
+ * hsh === other_hash => true or false
+ *
+ * Equality---Two hashes are strictly equal if they each contain the same number
+ * of keys and if each key-value pair is equal to (according to
+ * <code>Object#==</code>) the corresponding elements in the other
+ * hash. Both hashes must have also equal default values/procs.
+ *
+ * h1 = { "a" => 1, "c" => 2 }
+ * h2 = { 7 => 35, "c" => 2, "a" => 1 }
+ * h3 = { "a" => 1, "c" => 2, 7 => 35 }
+ * h4 = { "a" => 1, "d" => 2, "f" => 35 }
+ * h1 === h2 #=> false
+ * h2 === h3 #=> true
+ * h3 === h4 #=> false
+ *
+ */
+
+static VALUE
+rb_hash_strict_equal(hash1, hash2)
+ VALUE hash1, hash2;
+{
+ struct equal_data data;
+
+ if (hash1 == hash2) return Qtrue;
+ if (TYPE(hash2) != T_HASH) {
+ if (!rb_respond_to(hash2, rb_intern("to_hash"))) {
+ return Qfalse;
+ }
+ return rb_equal(hash2, hash1);
+ }
+ if (RHASH(hash1)->tbl->num_entries != RHASH(hash2)->tbl->num_entries)
+ return Qfalse;
if (!(rb_equal(RHASH(hash1)->ifnone, RHASH(hash2)->ifnone) &&
FL_TEST(hash1, HASH_PROC_DEFAULT) == FL_TEST(hash2, HASH_PROC_DEFAULT)))
return Qfalse;
@@ -1435,7 +1477,6 @@

return data.result;
}
-
static int
rb_hash_invert_i(key, value, hash)
VALUE key, value;
@@ -2365,6 +2406,7 @@
rb_define_method(rb_cHash,"inspect", rb_hash_inspect, 0);

rb_define_method(rb_cHash,"==", rb_hash_equal, 1);
+ rb_define_method(rb_cHash,"===", rb_hash_strict_equal, 1);
rb_define_method(rb_cHash,"[]", rb_hash_aref, 1);
rb_define_method(rb_cHash,"fetch", rb_hash_fetch, -1);
rb_define_method(rb_cHash,"[]=", rb_hash_aset, 2);
 
A

Ara.T.Howard

Ok,

ALthough the docs don't mention it, Hash comparison using == checks
not only the key value pairs, but also the default value / default
procs.

So how does one compare 2 hashes key-value pairs. I can see that it
might be useful to distinguish between 2 hashes whose key-value pairs
are the same but the default value/default proc are different, but I
would think that would be much less often than wanting to know if the
key-value pairs are the same.

Maybe I am thinking about this all wrong, but I always viewed a Hash
as a Hash, and yes some might have a default value and some might
have a default proc, but it is the data (the key-value pairs) that,
to me, make up the Hash. The default data/procs are convenience that
make it easier to work with a Hash, but I would think that {} and
Hash.new(3) would be == if their key-value pairs would be equal.

Assuming that I am in the minority here about ==, what is the proper
way to see if 2 Hashes key-value pairs are the same.

how about

(a.keys - b.keys).empty? and (a.values - b.values).empty?

-a
--
===============================================================================
| EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
| PHONE :: 303.497.6469
| ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
| URL :: http://www.ngdc.noaa.gov/stp/
| TRY :: for l in ruby perl;do $l -e "print \"\x3a\x2d\x29\x0a\"";done
===============================================================================
 
W

walter

how about

(a.keys - b.keys).empty? and (a.values - b.values).empty?

Thanks, but that doesn't work

a = {'A'=>1, 'B'=>2, 'C'=>3}
b = {'A'=>3, 'B'=>2, 'C'=>1}
puts (a.keys - b.keys).empty? and (a.values - b.values).empty?

here the keys and values are the same but belong to different
elements.


I know ways to do it, but it seems ugly and overcomplicated. Also
since it is pure ruby it is much
c = Hash.new{|h,k| h[k]=0}
c['A'] = 1
c['B'] = 2
c['C'] = 3

puts (a.size == b.size) && !(a.keys.collect{|k| a[k] ==
b[k]}.include?(false))
puts (a.size == c.size) && !(a.keys.collect{|k| a[k] ==
c[k]}.include?(false))


I am sure I can get a much faster ruby version, but not as nice as a
== b.



Walt
*****************************************************
Walter Szewelanczyk
IS Director
M.W. Sewall & CO. email : (e-mail address removed)
259 Front St. Phone : (207) 442-7994 x 128
Bath, ME 04530 Fax : (207) 443-6284
*****************************************************
 
M

Mark Hubbart

Ok,

ALthough the docs don't mention it, Hash comparison using == checks
not only the key value pairs, but also the default value / default
procs.

So how does one compare 2 hashes key-value pairs. I can see that it
might be useful to distinguish between 2 hashes whose key-value pairs
are the same but the default value/default proc are different, but I
would think that would be much less often than wanting to know if the
key-value pairs are the same.

I posted this in another thread, but I figured I should dup it here:

for two hashes a and b:

a.sort = b.sort

It converts them to arrays, and sorts them. This should do it.
Maybe I am thinking about this all wrong, but I always viewed a Hash
as a Hash, and yes some might have a default value and some might
have a default proc, but it is the data (the key-value pairs) that,
to me, make up the Hash. The default data/procs are convenience that
make it easier to work with a Hash, but I would think that {} and
Hash.new(3) would be == if their key-value pairs would be equal.

I agree that the way == works with hashes seems strange; I would expect
it to just compare keys/values. OTOH, I can see how some might feel
default values are an important part of determining equality.

cheers,
--Mark
 
F

Florian Gross

(e-mail address removed) wrote:

This will work:

irb(main):001:0> class Hash
irb(main):002:1> def equal_content?(other)
irb(main):003:2> {}.update(self) == {}.update(other)
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> hash_a = Hash.new { |h, k| h[k] = rand }
=> {}
irb(main):007:0> hash_a[:foo] = "bar"
=> "bar"
irb(main):008:0> hash_a[:qux] = "quz"
=> "quz"
irb(main):009:0> hash_b = {:foo => "bar", :qux => "quz"}
=> {:foo=>"bar", :qux=>"quz"}
irb(main):010:0> hash_a.equal_content?(hash_b)
=> true
irb(main):011:0> {}.equal_content?:)x => 1)
=> false

Regards,
Florian Gross
 
N

nobu.nokada

Hi,

At Fri, 16 Apr 2004 02:21:58 +0900,
Elias Athanasopoulos wrote in [ruby-talk:97279]:
* Change '==' method to compare only key-value pairs.
* Add Hash#=== method to act like current Hash#==

I feel Hash#=== would test membership, i.e., alias for
Hash#key?.
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: Hash, ==, key-value comparison"

|* Change '==' method to compare only key-value pairs.
|* Add Hash#=== method to act like current Hash#==

They are possible options, along with

* add new method (e.g. content_equal?) for membership equality.

I'm not sure which one is the best way to go.

matz.
 
A

Ara.T.Howard

Hi,

In message "Re: Hash, ==, key-value comparison"

|* Change '==' method to compare only key-value pairs.
|* Add Hash#=== method to act like current Hash#==

They are possible options, along with

* add new method (e.g. content_equal?) for membership equality.

I'm not sure which one is the best way to go.

matz.

s/content_equal/equiv/ ?

-a
--
===============================================================================
| EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
| PHONE :: 303.497.6469
| ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
| URL :: http://www.ngdc.noaa.gov/stp/
| TRY :: for l in ruby perl;do $l -e "print \"\x3a\x2d\x29\x0a\"";done
===============================================================================
 
R

Robert Klemme

Ara.T.Howard said:
how about

(a.keys - b.keys).empty? and (a.values - b.values).empty?

Does not ensure that key value mappings are identical:

irb(main):006:0> a={1=>1,2=>2}
=> {1=>1, 2=>2}
irb(main):007:0> b={1=>2,2=>1}
=> {1=>2, 2=>1}
irb(main):008:0> (a.keys - b.keys).empty? and (a.values - b.values).empty?
=> true
irb(main):009:0> a == b
=> false

In this case == is more correct than you suggested comparison.

robert
 
R

Robert Klemme

Florian Gross said:
(e-mail address removed) wrote:

This will work:

.... and burn a lot of mem if both hashes are huge.

Regards

robert

irb(main):001:0> class Hash
irb(main):002:1> def equal_content?(other)
irb(main):003:2> {}.update(self) == {}.update(other)
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> hash_a = Hash.new { |h, k| h[k] = rand }
=> {}
irb(main):007:0> hash_a[:foo] = "bar"
=> "bar"
irb(main):008:0> hash_a[:qux] = "quz"
=> "quz"
irb(main):009:0> hash_b = {:foo => "bar", :qux => "quz"}
=> {:foo=>"bar", :qux=>"quz"}
irb(main):010:0> hash_a.equal_content?(hash_b)
=> true
irb(main):011:0> {}.equal_content?:)x => 1)
=> false

Regards,
Florian Gross
 
J

Jean-Hugues ROBERT

I tend to think about == as "has the same value as".
So == is rather strong.
I like it this way, so I would rather stick with the exiting scheme.
OTOH I agree that the default value/proc is easy to miss. Yet,
maybe some other operator could do the job, something like ~= if available ?
Jean-Hugues
 
G

Gavin Sinclair

s/content_equal/equiv/ ?

My turn:

* Hash#== compares only the data
* Hash#equiv? compares the data and the proc
* Hash#=== is alias for Hash#key?

The first two I think are matters of importance; the third one is just
a nice idea. There are probably other, equally good, ideas for
Hash#===.

Cheers,
Gavin
 
Z

Zsban Ambrus

Hi,
...

I feel Hash#=== would test membership, i.e., alias for
Hash#key?.

I'd agree with that. This way you could easily test for many possible
values in a case statement.

Note that if there are many vaes to match with, then a hash may be
more efficent then a "when" with a comma-delimited list, as long as the
hash is calculated only once. (Of course, if there are multiple "when"
branches like that, a single hash that does all the matching or probably a
hash of proces or lambdas (I don't quite know the fine point of
distinguishing these) is better.)

Let me mention how this will work in perl6. In perl6, the =~ operator will
do something similar to ruby's ===, that is, it is used in the when clauses,
it matches numeric or string data, regexps, and classes just like === in
ruby (and thus does not automatically coerce a string to a regexp as =~ in
per5 or ruby). When it meets a hash or an array on either side, it mathces
each key (element of array) with the other side until it finds a match.
To do this with an array is quite unneccesarry I think, but it is good for a
haash.
 
R

Robert Klemme

Zsban Ambrus said:
I'd agree with that. This way you could easily test for many possible
values in a case statement.

Does this work? I mean

h = {}
case h
when val1, val2
...
end

Does something else: it tests val1 === h and val2 === h and not h === val1.
Currently I don't see how Hash#=== can be used in a case to test for
multiple values. In order to use Hash#=== in a case statement, the hash has
to appear in the when clauses. Do I overlook something?

Regards

robert
 
Z

Zsban Ambrus

Does this work? I mean

h = {}
case h
when val1, val2
...
end

Does something else: it tests val1 === h and val2 === h and not h === val1.
Currently I don't see how Hash#=== can be used in a case to test for
multiple values. In order to use Hash#=== in a case statement, the hash has
to appear in the when clauses. Do I overlook something?

Regards

robert

No, I mean something like this:

bad= {}; # bad magic words that the player might try
%w[abracadabra xyzzy hocuspocus bringmehome].each do |x| bad[x]=1 end;
# this list of (bad) magic words might get very long

....

(in a loop)
....
gets
~/^\s*(\w+)/ and word= $1
case word
when bad
puts "That won't work in this game. Nice try, thuogh."
when "aardvark" # this is the real magic word
player.move(Home)
when "a", "ahead"
player.move_ahead
when "fight", "shoot"
(.... check if there's a monster here, kill it with 1/2 probability,
otherwise make it angry ...)
....
end
 
R

Robert Klemme

Zsban Ambrus said:
Zsban Ambrus said:
Does this work? I mean

h = {}
case h
when val1, val2
...
end

Does something else: it tests val1 === h and val2 === h and not h === val1.
Currently I don't see how Hash#=== can be used in a case to test for
multiple values. In order to use Hash#=== in a case statement, the hash has
to appear in the when clauses. Do I overlook something?

Regards

robert

No, I mean something like this:

bad= {}; # bad magic words that the player might try
%w[abracadabra xyzzy hocuspocus bringmehome].each do |x| bad[x]=1 end;
# this list of (bad) magic words might get very long

...

(in a loop)
....
gets
~/^\s*(\w+)/ and word= $1
case word
when bad
puts "That won't work in this game. Nice try, thuogh."
when "aardvark" # this is the real magic word
player.move(Home)
when "a", "ahead"
player.move_ahead
when "fight", "shoot"
(.... check if there's a monster here, kill it with 1/2 probability,
otherwise make it angry ...)
....
end

Ah, ok I see. Thx! I like especially the "when bad". :)

robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top