Comparing Arrays (Sounds easy but I can't find it!)

M

Mike Bartels

Hi Everyone!

I have two Arrays A and B. Both arrays are byte arrays with 7 bytes each.
The contents of array A and B are the same

A = {1, 2, 3, 4, 5, 6, 7};
B = {1, 2, 3, 4, 5, 6, 7};

When I do

if (A == B)
{
dosomething();
}

the system does not think that they are the same...

Does this have something to do with heap and stack? (*shiver*)

How can I compare two arrays?

Thanks for your help!

Mike
 
M

Mike Bartels

OK. For predefined value types, the equality operator (==) returns true if
the values of its operands are equal, false otherwise. For _reference_ types
other than string, == returns true, if its two operands refer to the same
_object_. For the string type, == compares the values of the strings...

Still the question remains, how does one compare arrays?
 
B

Bob Grommes

A == B is testing whether object reference A is the same as object reference
B. Since they are different arrays they will be unequal.

A and B are both objects of type Array.

To my knowledge the only way to compare two arrays is to iterate through
them both until you find an element that's unqeual. If you get all the way
through without finding such, they are "equal" in the sense you mean.

Something like:

public bool ArraysEqual(Array a1,Array a2) {

if (a1.Length != a2.Length) {
return false;
}

foreach (int i1 in a1) {

foreach (int i2 in a2) {

if (i1 != i2) {
return false;
}

}

}

return true;
}

Notice that the quick and dirty (and untested) routine above assumes you're
dealing with two integer arrays. This probably gets to the root of why MSFT
hasn't overridden the equals operator for Array to something more generally
useful. Without generics (which will be available in the next release of
..NET) it's difficult to implement an efficient routine to compare arrays
regardless of the type of data they contain.

--Bob
 
J

Jon Skeet [C# MVP]

Mike Bartels said:
OK. For predefined value types, the equality operator (==) returns true if
the values of its operands are equal, false otherwise. For _reference_ types
other than string, == returns true, if its two operands refer to the same
_object_. For the string type, == compares the values of the strings...

Note that it's not just string - it's any type which overrides the ==
operator. This could include some of your own types, and there may well
be other types in the framework which override the equality operator.
Still the question remains, how does one compare arrays?

It's fairly easy to write a method which compares each element in
turn...
 
J

Jon Skeet [C# MVP]

Bob Grommes said:
A == B is testing whether object reference A is the same as object reference
B. Since they are different arrays they will be unequal.

A and B are both objects of type Array.

To my knowledge the only way to compare two arrays is to iterate through
them both until you find an element that's unqeual. If you get all the way
through without finding such, they are "equal" in the sense you mean.

Something like:

public bool ArraysEqual(Array a1,Array a2) {

if (a1.Length != a2.Length) {
return false;
}

foreach (int i1 in a1) {

foreach (int i2 in a2) {

if (i1 != i2) {
return false;
}

}

}

return true;
}

That won't work - that checks that each element in a2 is equal to *all*
the elements in a1. Instead of the foreach blocks, you want:

for (int i=0; i < a1.Length; i++)
{
if (a1 != a2)
{
return false;
}
}
 
H

Hans-Gerd Zigann

Jon Skeet said:
[...]
That won't work - that checks that each element in a2 is equal to *all*
the elements in a1. Instead of the foreach blocks, you want:

for (int i=0; i < a1.Length; i++)
{
if (a1 != a2)
{
return false;
}
}
[...]


Hi Jon,

that won't work.
'Cannot apply indexing with [] to an expression of type System.Array'
use enumerator instead.
 
M

Mabden

Jon Skeet said:
Bob Grommes said:
A == B is testing whether object reference A is the same as object reference
B. Since they are different arrays they will be unequal.

A and B are both objects of type Array.

To my knowledge the only way to compare two arrays is to iterate through
them both until you find an element that's unqeual. If you get all the way
through without finding such, they are "equal" in the sense you mean.

Something like:

public bool ArraysEqual(Array a1,Array a2) {

if (a1.Length != a2.Length) {
return false;
}

foreach (int i1 in a1) {

foreach (int i2 in a2) {

if (i1 != i2) {
return false;
}

}

}

return true;
}

That won't work - that checks that each element in a2 is equal to *all*
the elements in a1. Instead of the foreach blocks, you want:

for (int i=0; i < a1.Length; i++)
{
if (a1 != a2)
{
return false;
}
}


This won't find the case where a2.Length is bigger than a1, so you'll fall
out of the loop and not return false.
 
J

Jon Skeet [C# MVP]

Mabden said:
This won't find the case where a2.Length is bigger than a1, so you'll fall
out of the loop and not return false.

Yes it will - because the if statement at the start (which I wasn't
suggesting should be removed) will catch that case.
 
J

Jon Skeet [C# MVP]

Hans-Gerd Zigann said:
that won't work.
'Cannot apply indexing with [] to an expression of type System.Array'
use enumerator instead.

Ah - cast both to IList then, and it'll be fine. However, it's slightly
worse than I thought, as of course boxed values won't get unboxed to
identical references - we need to call Equals instead. We should also
handle the case where either argument is null...

Here's the full method, as it's getting confusing now:

static bool ArraysEqual (Array a1, Array a2)
{
if (a1==a2)
{
return true;
}

if (a1==null || a2==null)
{
return false;
}

if (a1.Length != a2.Length)
{
return false;
}

IList list1=a1, list2=a2;

for (int i=0; i < a1.Length; i++)
{
if (!Object.Equals(list1, list2))
{
return false;
}
}
return true;
}
 
M

Mabden

Jon Skeet said:
Hans-Gerd Zigann said:
that won't work.
'Cannot apply indexing with [] to an expression of type System.Array'
use enumerator instead.

This doesn't seem right, I can do the following:

class Test
{
public static bool test(int[]A, int[]B)
{
if (A.Length != B.Length)
return false;
for (int i=0; i < A.Length; i++)
{
if (A != B)
return false;
}
return true;
}

static void Main ()
{
bool result;
int[] x= {1,2,3,4,5,6};
int[] y= {1,2,3,4,5,6};
int[] z= {1,2,3,4,6,7};

result = test(x,y);
if (true == result)
System.Console.WriteLine ("Equal!");
else
System.Console.WriteLine ("Not equal!");

result = test(y,z);
if (true == result)
System.Console.WriteLine ("Equal!");
else
System.Console.WriteLine ("Not equal!");

return;
}
}

Ah - cast both to IList then, and it'll be fine. However, it's slightly
worse than I thought, as of course boxed values won't get unboxed to
identical references - we need to call Equals instead. We should also
handle the case where either argument is null...


Doesn't "a1.Length" cover null?
 
J

Jon Skeet [C# MVP]

Mabden said:
Jon Skeet said:
Hans-Gerd Zigann said:
that won't work.
'Cannot apply indexing with [] to an expression of type System.Array'
use enumerator instead.

This doesn't seem right, I can do the following:

class Test
{
public static bool test(int[]A, int[]B)
{
if (A.Length != B.Length)
return false;
for (int i=0; i < A.Length; i++)
{
if (A != B)
return false;
}
return true;
}


Yes, but that's with arguments of type int[], not System.Array.
Doesn't "a1.Length" cover null?

No, it throws a NullReferenceException, which isn't exactly desired
behaviour.
 
M

Mabden

Jon Skeet said:
Mabden said:
Jon Skeet said:
that won't work.
'Cannot apply indexing with [] to an expression of type System.Array'
use enumerator instead.

This doesn't seem right, I can do the following:

class Test
{
public static bool test(int[]A, int[]B)
{
if (A.Length != B.Length)
return false;
for (int i=0; i < A.Length; i++)
{
if (A != B)
return false;
}
return true;
}


Yes, but that's with arguments of type int[], not System.Array.


Huh? I thought this was the C# forum...

"Since the Array class is the parent class of all arrays defined in C#, an
array created using C/C++ syntax has access to the methods defined in the
Array class." -- C# Programmers Reference Chapter 6

Anyway, the OP didn't say they wanted anything fancy in their *home work
assignment* (I'm guessing...).

The original array was the one given in my code (as x and y):
A = {1,2,3,4,5,6};
B = {1,2,3,4,5,6};

That's an int array! So there! ;-)
 
J

Jon Skeet [C# MVP]

Mabden said:
Yes, but that's with arguments of type int[], not System.Array.

Huh? I thought this was the C# forum...

Yes, it is. What's your point?
"Since the Array class is the parent class of all arrays defined in C#, an
array created using C/C++ syntax has access to the methods defined in the
Array class." -- C# Programmers Reference Chapter 6

And indeed that's entirely correct. It says that System.Array is the
*parent* of int[], not the same type. You can apply indexing directly
to an int[], but not to a System.Array.
Anyway, the OP didn't say they wanted anything fancy in their *home work
assignment* (I'm guessing...).

I don't see any reason to think this would be a homework assignment,
actually (especially given other recent posts).
The original array was the one given in my code (as x and y):
A = {1,2,3,4,5,6};
B = {1,2,3,4,5,6};

That's an int array!

It might be. It might not be. It might be an array of objects:

object[] A = {1, 2, 3, 4, 5, 6};

- or of bytes, shorts, longs etc.

Using a method which takes a parameter of type System.Array would work
with either case - your method only works if it's declared as int[],
which we don't know.
 
M

Mabden

Jon Skeet said:
Mabden said:
Yes, but that's with arguments of type int[], not System.Array.

Huh? I thought this was the C# forum...

Yes, it is. What's your point?

Well, Impatient One, you would have had to read the next few lines to get
the point. You don't have to have a separate comment for every statement,
you can read the entire post and just comment on the gist of the message.
Making beligerent noises about the first sentence of a 5 paragraph
explanation really doesn't do much more than let everyone know you are
annoyed with me having a differing opionion.

You don't have to post the minute something annoys you. Take a breath, keep
reading, and then post something relevant to the entire post. This method
shortens everyones reading time, and leads to less confrontational posts.
This way we help each other. :)

I posted my code (a working program - not a snippet) to show that the OP's
problem could be solved using traditional index arrays.

That is the point.

"Since the Array class is the parent class of all arrays defined in C#, an
array created using C/C++ syntax has access to the methods defined in the
Array class." -- C# Programmers Reference Chapter 6

And indeed that's entirely correct. It says that System.Array is the
*parent* of int[], not the same type. You can apply indexing directly
to an int[], but not to a System.Array.

OK, so we agree that an int[] is derived from System.Array. But you say that
you cannot apply indexing to a System.Array.

This is new to me. This is where I would like you focus your explanation.
Please explain what you mean so I can understand the difference. I don't
understand why this is, and I think a lot of people trying to move to C#
would like some detail about how this works.

I don't see any reason to think this would be a homework assignment,
actually (especially given other recent posts).

I am allowed my opinion. I think it is homework.

The original array was the one given in my code (as x and y):
A = {1,2,3,4,5,6};
B = {1,2,3,4,5,6};

That's an int array!

It might be. It might not be. It might be an array of objects:

object[] A = {1, 2, 3, 4, 5, 6};

- or of bytes, shorts, longs etc.

Again, my opinion is colored by the fact that I believe it is a simple
homework assignment and the simplest case (Occam's Razor: "one should not
increase, beyond what is necessary, the number of entities required to
explain anything") would suggest that the numbers 1-6 are whole integers.

Using a method which takes a parameter of type System.Array would work
with either case - your method only works if it's declared as int[],
which we don't know.

You presented a very general discussion of stepping through an array. I even
went back and saved a copy, in case I have two complicated object arrays I
need to compare.

I still think the OP really meant an array of ints. ;-)
 
J

Jon Skeet [C# MVP]

I posted my code (a working program - not a snippet) to show that the OP's
problem could be solved using traditional index arrays.

That is the point.

I still don't see why that point would raise the quote: "Huh? I thought
this was the C# forum..."

What was the point of that particular paragraph, exactly? How is the
code you posted relevant to it?
"Since the Array class is the parent class of all arrays defined in C#, an
array created using C/C++ syntax has access to the methods defined in the
Array class." -- C# Programmers Reference Chapter 6

And indeed that's entirely correct. It says that System.Array is the
*parent* of int[], not the same type. You can apply indexing directly
to an int[], but not to a System.Array.

OK, so we agree that an int[] is derived from System.Array. But you say that
you cannot apply indexing to a System.Array.

Not directly, no - unless you cast to IList first. That's because it
implements IList with explicit interface implementation.
This is new to me. This is where I would like you focus your explanation.
Please explain what you mean so I can understand the difference. I don't
understand why this is, and I think a lot of people trying to move to C#
would like some detail about how this works.

Perhaps if you'd spent time looking at the code I posted instead of
saying "I thought this was the C# forum", suggesting that either I
didn't know that int[] derived from System.Array or that in some other
way my post was off-topic, you might have understood already. However,
here we go:

System.Array implements the IList.Item indexer explicitly - that's why
in MSDN it comes up as Array.System.Collections.IList.Item. (I'm not
sure why that includes the System.Collections bit but, say, Add just
comes up as Array.IList.Add.)

That means you can't the indexer on an expression of type System.Array
- you have to cast it to IList first. Note that it's the type of the
*expression* which is important here, not the type of the actual value
of the reference.

So:

Array foo = new int[5];
object x = foo[2]; // Error
object y = ((IList)foo)[2]; // Fine

Now, looking at the IL code generated for that, it really *is* calling
get_Item.

When the compiler sees an expression of type int[] (or similar) -
something it *knows* is an array - it uses different IL: ldelem and the
like.

The compiler only knows to use special indexing for what *it* thinks of
as arrays - namely int[], object[] etc. It doesn't know anything about
System.Array for indexing.
I am allowed my opinion. I think it is homework.

You're certainly allowed your opinion - but I still don't see any
*reason* to think it's a homework assignment. Lots of people want to
compare the contents of arrays, and many of them aren't doing homework.
The original array was the one given in my code (as x and y):
A = {1,2,3,4,5,6};
B = {1,2,3,4,5,6};

That's an int array!

It might be. It might not be. It might be an array of objects:

object[] A = {1, 2, 3, 4, 5, 6};

- or of bytes, shorts, longs etc.

Again, my opinion is colored by the fact that I believe it is a simple
homework assignment and the simplest case (Occam's Razor: "one should not
increase, beyond what is necessary, the number of entities required to
explain anything") would suggest that the numbers 1-6 are whole integers.

It strikes me that making the assumption that it's homework goes
against Occam's Razor to start with...

The numbers 1-6 are certainly whole integers. Whether the array that
contains them is an int[] or not is a different matter, however. In
particular, there is no guarantee that {1, 2, 3, 4, 5, 6} is an int
array. It depends on the context.
Using a method which takes a parameter of type System.Array would work
with either case - your method only works if it's declared as int[],
which we don't know.

You presented a very general discussion of stepping through an array. I even
went back and saved a copy, in case I have two complicated object arrays I
need to compare.

I still think the OP really meant an array of ints. ;-)

Maybe he did - but the answer I presented would also work as soon as he
wanted to do something slightly different. The answer you presented
would have required cutting and pasting, and then changing the type of
the array.

There is one advantage to your solution though - it doesn't require
unboxing.
 
M

Mabden

Jon Skeet said:
I still don't see why that point would raise the quote: "Huh? I thought
this was the C# forum..."

What was the point of that particular paragraph, exactly? How is the
code you posted relevant to it?


OK, Jon. Perhaps the statement shouldn't have stood alone. Here's the exact
same thing minus some whitespace.

=====================================================================
Yes, but that's with arguments of type int[], not System.Array.

Huh? I thought this was the C# forum... "Since the Array class is the parent
class of all arrays defined in C#, an array created using C/C++ syntax has
access to the methods defined in the Array class." -- C# Programmers
Reference Chapter 6

=====================================================================

See, I meant that int[] == System.Array (OK, "is derived from" - stop
nit-picking). That's a defined C# relationship.

i.e. the quote "Huh? I thought this was the C# forum..." sort of means "I
thought we were talking about reality".

I'm not sure exactly what your problem with it is, it was not meant to mean
anything about C#, just to preface the rest of the email; an opener to what
was to follow; an attention grabber; a humorous intro to the topic at hand.
No offence meant, and certainly not implying your post was in the wrong
newsgroup.

There is a lot of the remainder of your post that will require much more
study to understand. You have obviously delved much deeper into C# than I
have. You are providing a well-rounded method to a general problem, while I
just answered the simplest version of what it seemed (to me) the OP asked.

Call me simple.

Maybe he did - but the answer I presented would also work as soon as he
wanted to do something slightly different. The answer you presented
would have required cutting and pasting, and then changing the type of
the array.

There is one advantage to your solution though - it doesn't require
unboxing.

Yay, I win! ;-)
 
J

Jon Skeet [C# MVP]

Mabden said:
What was the point of that particular paragraph, exactly? How is the
code you posted relevant to it?

OK, Jon. Perhaps the statement shouldn't have stood alone. Here's the exact
same thing minus some whitespace.

=====================================================================
Yes, but that's with arguments of type int[], not System.Array.

Huh? I thought this was the C# forum... "Since the Array class is the parent
class of all arrays defined in C#, an array created using C/C++ syntax has
access to the methods defined in the Array class." -- C# Programmers
Reference Chapter 6

=====================================================================

See, I meant that int[] == System.Array (OK, "is derived from" - stop
nit-picking). That's a defined C# relationship.

No, derived from *isn't* nit-picking here. Let's take a parallel
example - trying to use the SelectedItem property of ComboBox. You can
use it when the type of the expression is ComboBox, but not when the
type of the expression is Control, even though ComboBox derives from
Control. You can use everything that the *parent* type declares from an
expression of the *child* type (subject to member hiding), but not vice
versa.

There is a fundamental difference between int[] and System.Array, as
far as the compiler is concerned.
i.e. the quote "Huh? I thought this was the C# forum..." sort of means "I
thought we were talking about reality".

That's what I thought it meant - the problem is that you thought that
the "derived from" was nit-picking, which is isn't. My post was very
much talking about reality, and your remark implied that it wasn't -
which was why I took umbrage.
 
J

Jon Skeet [C# MVP]

Jon Skeet said:
There is a fundamental difference between int[] and System.Array, as
far as the compiler is concerned.

I've now found the section of the spec which is most useful in this
discussion - section 19.1.1 from the ECMA spec:

<quote>
The type System.Array is the abstract base type of all array types. An
implicit reference conversion (§13.1.4) exists from any array type to
System.Array, and an explicit reference conversion (§13.2.3) exists
from System.Array to any array type. Note that System.Array is not
itself an array-type. Rather, it is a class-type from which all array-
types are derived.
</quote>

Mind you, it also has:

<quote>
At run-time, a value of type System.Array can be null or a reference to
an instance of any array type.
</quote>

which is misleading - you can create instances of System.Array which
are not array types in C#. (I believe single-dimensional arrays with
non-zero lower bounds can never be validly cast to array types in C#.)

I'll email Eric Gunnerson about it to check what's going on there...
 
M

Mabden

Jon Skeet said:
There is a fundamental difference between int[] and System.Array, as
far as the compiler is concerned.
OK, now you're quoting your own posts and double-teaming me! :(
I appreciate that you have the deeper knowledge here. I am keeping a copy of
(selected parts) of these posts for future reference. Thanks for the expert
input. I don't think I have anything more of value to add to this thread.
I'll email Eric Gunnerson about it to check what's going on there...
OK, now you really scare me. I am definately out of my league.

But my ego^H^H^Hhistory requires a re-post of my working code that solves
the problem:
=========================================================
class Test
{
public static bool test(int[]A, int[]B)
{
if (A.Length != B.Length)
return false;
for (int i=0; i < A.Length; i++)
{
if (A != B)
return false;
}
return true;
}

static void Main ()
{
bool result;
int[] x= {1,2,3,4,5,6};
int[] y= {1,2,3,4,5,6};
int[] z= {1,2,3,4,6,7};

result = test(x,y);
if (true == result)
System.Console.WriteLine ("Equal!");
else
System.Console.WriteLine ("Not equal!");

result = test(y,z);
if (true == result)
System.Console.WriteLine ("Equal!");
else
System.Console.WriteLine ("Not equal!");

return;
}
}
=========================================================
 

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

Similar Threads


Top