MakePeaceGreatAgain
MakePeaceGreatAgain

Reputation: 37050

Compare if two sequences are equal

Before marking this as duplicate because of its title please consider the following short program:

static void Main()
{
    var expected = new List<long[]> { new[] { Convert.ToInt64(1), Convert.ToInt64(999999) } };
    var actual = DoSomething();
    if (!actual.SequenceEqual(expected)) throw new Exception();
}

static IEnumerable<long[]> DoSomething()
{
    yield return new[] { Convert.ToInt64(1), Convert.ToInt64(999999) };
}

I have a method which returns a sequence of arrays of type long. To test it I wrote some test-code similar to that one within Main.

However I get the exception, but I don´t know why. Shouldn´t the expected sequence be comparable to the actually returned one or did I miss anything?

To me it looks as both the method and the epxected contain exactly one single element containing an array of type long, doesn´t it?

EDIT: So how do I achieve to not get the exception meaning to compare the elements within the enumeration to return equality?

Upvotes: 2

Views: 3995

Answers (3)

MakePeaceGreatAgain
MakePeaceGreatAgain

Reputation: 37050

SequenceEquals tests for the elements within the sequences to be identical. The elements within the enumerations are of type long[], so we actually compare two different arrays (containing the same elements however) against each other which is obsiously done by comparing their references instead of their actual value .

So what we actually check here is this expected[0] == actual[0] instead of expected[0].SequqnceEquals(actual[0])

This is obiosuly returns false as both arrays share different references.

If we flatten the hierarchy using SelectMany we get what we want:

if (!actual.SelectMany(x => x).SequenceEqual(expected.SelectMany(x => x))) throw new Exception();

EDIT:

Based on this approach I found another elegant way to check if all the elements from expected are contained in actual also:

if (!expected.All(x => actual.Any(y => y.SequenceEqual(x)))) throw new Exception();

This will search if for ever sub-list within expected there is a list within actual that is sequentially identical to the current one. This seems much smarter to be as we do not need any custom EqualityComparer and no weird hashcode-implementation.

Upvotes: 4

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149588

The actual problem is the fact that you're comparing two long[], and Enumerable.SequenceEquals will use an ObjectEqualityComparer<Int64[]> (you can see that by examining EqualityComparer<long[]>.Default which is what is being internally used by Enumerable.SequenceEquals), which will compare references of those two arrays, and not the actual values stored inside the array, which obviously aren't the same.

To get around this, you could write a custom EqualityComparer<long[]>:

static void Main()
{
    var expected = new List<long[]> 
                       { new[] { Convert.ToInt64(1), Convert.ToInt64(999999) } };
    var actual = DoSomething();

    if (!actual.SequenceEqual(expected, new LongArrayComparer()))
        throw new Exception();
}

public class LongArrayComparer : EqualityComparer<long[]>
{
    public override bool Equals(long[] first, long[] second)
    {
        return first.SequenceEqual(second);
    }

    // GetHashCode implementation in the courtesy of @JonSkeet
    // from http://stackoverflow.com/questions/7244699/gethashcode-on-byte-array
    public override int GetHashCode(long[] arr)
    {
        unchecked
        {
            if (array == null)
            {
                return 0;
            }

            int hash = 17;
            foreach (long element in arr)
            {
                hash = hash * 31 + element.GetHashCode();
            }

            return hash;
        }
    }
}

Upvotes: 6

Jamiec
Jamiec

Reputation: 136154

No, your sequences are not equal!

Lets remove the sequence bit, and just take what is in the first element of each item

var firstExpected = new[] { Convert.ToInt64(1), Convert.ToInt64(999999) };
var firstActual = new[] { Convert.ToInt64(1), Convert.ToInt64(999999) };
Console.WriteLine(firstExpected == firstActual); // writes "false"

The code above is comparing two separate arrays for equality. Equality does not check the contents of arrays it checks the references for equality.

Your code using SequenceEquals is, essentially, doing the same thing. It checks the references in each case of each element in an enumerable.

Upvotes: 4

Related Questions