Compare Array Values?

Hi –

On Thu, 23 Nov 2006, Gabriele M. wrote:

Morton smartly pointed out that elements can be arrays themselves. So, should
we treat them as any other kind of object and compare them with ==?
I guess we should want to recursively apply our equality concept given by the
OP.

We still don’t have a ruling :slight_smile: I guess there are three
possibilities:

  1. shallow – just use == on nested arrays
  2. deep – flatten nested arrays
  3. recursive – do the comparison on nested arrays

I’ve been assuming #1, but any of them could be interesting to
implement.

David

Hi –

On Thu, 23 Nov 2006, Darshan.Ishaya wrote:

The Coolest Way:

def same_elems?(array1, array2)
return false unless array1.length == array2.length

compare = Proc.new do |x,y|
if (x.class == y.class)

You’d probably want to throw in a “and if x.respond_to?(’<=>’)”.

 x <=> y

else
x.class.to_s <=> y.class.to_s

The problem there is that it can’t sort within a class that doesn’t
have <=> (and if it did, it would have sorted it that way :slight_smile: So, for
example:

 p [:a,:b].sort(&compare)  # => [:a,:b]
 p [:b,:a].sort(&compare)  # => [:b,:a]

David

On 11/23/06, [email protected] [email protected] wrote:

OP.

We still don’t have a ruling :slight_smile: I guess there are three
possibilities:

  1. shallow – just use == on nested arrays
  2. deep – flatten nested arrays
  3. recursive – do the comparison on nested arrays

I’ve been assuming #1, but any of them could be interesting to
implement.

For my purposes at the moment flatten is fine. But seeing your posts on
it
recently and considering the fact that I said it could be arbitrary
elements, I guess #1 would probably be the go.

Quoting Jason M. [email protected]:

Eek. There goes my street cred.

Don’t feel too bad. That’s a very common mistake. Heck, even hackers try
to slip
it past experienced programmers occasionally… remember the uid=0 hack
discovered in the linux kernel a few years back?

On Thu, 23 Nov 2006, Daniel N wrote:

[1,2,3,nil] and [nil, nil, nil, 1,2,1,3,2]

At first I thought of
(( array1 | array2 ) - array1).size == 0

but this does not take into account duplicate values and the should not
equals do equal :(. In fact I have not been able work out how to use any of
the standard array, or Enumerable methods in such a way I can check if two
arrays contain the same values as well as taking into account duplicates.

Any pointers?

maybe i mis-undestand, but why wouldn’t you use a hashlist?

harp:~ > cat a.rb
def content_eq a, b
ah = Hash.new{|h,k| h[k] = []} and a.each{|x| ah[x] << x}
bh = Hash.new{|h,k| h[k] = []} and b.each{|x| bh[x] << x}
ah == bh
end

pairs = [
[ [1,2,3,4,“abc”], [2,3,1,“abc”,4] ],
[ [1,2,3], [1,1,2,3] ],
[ [1,2,3,nil], [nil, nil, nil, 1,2,1,3,2] ],
]

pairs.each{|a,b| puts “content_eq(#{ a.inspect }, #{ b.inspect }) #=>
#{ content_eq a, b }”}

harp:~ > ruby a.rb
content_eq([1, 2, 3, 4, “abc”], [2, 3, 1, “abc”, 4]) #=> true
content_eq([1, 2, 3], [1, 1, 2, 3]) #=> false
content_eq([1, 2, 3, nil], [nil, nil, nil, 1, 2, 1, 3, 2]) #=> false

??

-a

Of course this would be much simpler if we were redefining Array#==
with this algorythm (there would be no need to check #kind_of?) but I
really DON’T want to know what could happen by redefining Array#==,
since every library could rely on it :stuck_out_tongue:
(Actually, it might be funny to find out)

So… err… can the Array contain Hashes or other Objects as well???
This could get pretty tough, eh?

[1,4,nil,{:foo => 1, :bar =>2},customKlass,2]

On 11/23/06, El Gato [email protected] wrote:

So… err… can the Array contain Hashes or other Objects as well???
This could get pretty tough, eh?

[1,4,nil,{:foo => 1, :bar =>2},customKlass,2]

I guess it could, but for my purposes it will contain either strings or
active_records or possibly a mix of either.

Ah, thanks. I knew it would only work for classes with <=> defined, but
I was naively assuming all important classes did. (I’ve only been
playing with Ruby for a couple of weeks.) I didn’t realize a class as
important as Symbol didn’t have it.

I could try a slightly better, but probably similarly flawed:

def same_elems?(array1, array2)
return false unless array1.length == array2.length

compare = Proc.new do |x,y|
if (x.class == y.class)
if x.respond_to?(’<=>’)
x <=> y
else
x.to_s <=> y.to_s
end
else
x.class.to_s <=> y.class.to_s
end
end

return array1.sort(&compare) == array2.sort(&compare)
end

I’m now naively assuming all important classes implement to_s ; )

At least judging by this:

darshan@shadow ~ $ ri -T “<=>” | awk ‘{gsub(/<=>,/,"<=>\n");print}’ |
grep “<=>” | wc -l
25
darshan@shadow ~ $ ri -T “to_s” | awk ‘{gsub(/to_s,/,“to_s\n”);print}’ |
grep “to_s$” | wc -l
116

it’ll have a much better chance (almost 5 x) of working. (Sorry, I
haven’t switched to Ruby one-liners yet; I’m sure I will soon.)

I guess it all depends on the application, and what classes are going to
be in the arrays. I sure had fun playing with it though. I’m so stoked
about Ruby.

Darshan

On 23/11/06, Darshan.Ishaya [email protected] wrote:

Well, I’m new to Ruby, but it was an interesting question so I gave it
a stab. My first idea was

a.sort == b.sort

which works if all the elements of an array of of the same class. But
if you mix objects of different class (like you example with Fixnums
and a String), sort() craps out on it. You can pass a block to sort,
but I couldn’t think of a good way to use that to our advantage. So I
resorted to more of a brute force method:

Would this work?
a.sort_by{|n| n.hash} == b.sort_by {|n|n.hash}

Farrel

I think it does! I think hash() is the magic method that I was looking
for originally. I considered object_id(), but objects that are eql? can
have different object_id’s, so it was a no-go. hash() seems to be the
magic one.

I think you get the award.

Darshan

I modified a previous poster’s program that used hashes (good idea!).
Now there’s only one hash, which counts the number of identical keys
instead of actually storing the values. I then decrement the hash values
for each key in b. If a and b are equal, all values in the hash should
be 0. I also immediately reject as non-equal two arrays of different
sizes. Seems to work for his test cases (and some I added), and I guess
is probably smaller and faster. Are there any holes in this solution?

def content_eq a, b
return false if a.length != b.length
ah = Hash.new {|h,k| h[k] = 0 }
a.each {|x| ah[x] += 1 }
b.each {|x| ah[x] -= 1 }

Not equal if any non-zero values

not ah.values.detect {|v| v != 0 }
end

pairs = [
[ [1,2,3,4,“abc”], [2,3,1,“abc”,4] ],
[ [1,2,3], [1,1,2,3] ],
[ [1,2,3,nil], [nil, nil, nil, 1,2,1,3,2] ],
[ [], [] ],
[ [], [nil] ],
[ [/^$/], [/^$/] ],
[ [/^$/], [/^/] ],
[ [nil, nil, nil, nil], [nil, nil, nil, nil] ],
]

pairs.each{|a,b| puts “content_eq(#{ a.inspect }, #{ b.inspect }) #=> #{
content_eq a, b }”}


content_eq([1, 2, 3, 4, “abc”], [2, 3, 1, “abc”, 4]) #=> true
content_eq([1, 2, 3], [1, 1, 2, 3]) #=> false
content_eq([1, 2, 3, nil], [nil, nil, nil, 1, 2, 1, 3, 2]) #=> false
content_eq([], []) #=> true
content_eq([], [nil]) #=> false
content_eq([/^$/], [/^$/]) #=> true
content_eq([/^$/], [/^/]) #=> false
content_eq([nil, nil, nil, nil], [nil, nil, nil, nil]) #=> true