4
\$\begingroup\$

I want to be able to compare 2 jagged arrays of doubles for value equality. The arrays can have any number of dimensions. Inside any dimension could also be another array etc.

It is known that the values of any array dimension is a double, null or another array, so it should be able to compare any of these with one function:

  • double[]
  • double[][]
  • double[][][]
  • double[][double[]][double[][]]

public static bool ArraysEqual(dynamic a, dynamic b)
{
    // Same objects or both null
    if (ReferenceEquals(a, b)) return true;

    // Just one object is null
    if (a == null ^ b == null) return false;

    // Different number of elements
    if (a.Length != b.Length) return false;

    // Array is empty
    if (a.Length == 0) return true;

    // Test each item in the arrays
    for (int i = 0; i < a.Length; i++)
    {
        // Same object or both null
        if (ReferenceEquals(a[i], b[i])) continue;

        // Just one object is null
        if (a[i] == null ^ b[i] == null) return false;

        var aIsArray = a[i].GetType().IsArray;
        var bIsArray = b[i].GetType().IsArray;

        // Just one object is an array
        if (aIsArray ^ bIsArray) return false;

        // Both are arrays (no need to check b, since it was exclusive or'd prior)
        // Stop evaluating if the recursive call returned false;
        if (aIsArray)
        {
            if (!ArraysEqual(a[i], b[i])) return false;
        }
        else if (a[i] != b[i]) 
        {
            return false;
        }
    }

    return true;
}
\$\endgroup\$
5
\$\begingroup\$

Nice question, and I am inspired to answer it, even though I don't use C#. I know enough about the algorithmic side to see a better way.

First though, the multitude of 1-liner if-conditions are bad practice. You should consider using braces even for these values.

The second thing, is the bitwise-XOR ^ operator you are doing in two places... this is conceptually broken, and unnecessary. A simple logical OR || comparison will be fine, since the previous check would return true if both values were null.

if (a == null || b == null)
{
     return false;
}

Now, about your algorithm.

The recursive system you have is a good option, but, if you change the 'recurse' point of the recursion, you will be able to do things in a more concise way. At the moment you are doing a number of the tests twice.

Have a look at the following:

public static bool ArraysEqual(dynamic a, dynamic b)
{
    // Same objects or both null
    if (ReferenceEquals(a, b))
    {
        return true;
    }

    // Just one object is null
    if (a == null || b == null)
    {
        return false;
    }

    if (a.GetType().IsArray && b.GetType().IsArray)
    {
        if (a.Length == b.Length)
        {
            for (int i = 0; i < a.Length; i++)
            {
                if (!ArraysEqual(a[i], b[i]))
                {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    // fall back to simple value compare
    return a == b;
}

Notice how, by doing the above, there is only one instance of each type of check.

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

I modified rolfl's code to work with value types.

    public static bool ArraysEqual(dynamic a, dynamic b)
    {
        // Same objects or both null
        if (ReferenceEquals(a, b))
        {
            return true;
        }

        // Just one object is null
        if (ReferenceEquals(a, null) || ReferenceEquals(b, null))
        {
            return false;
        }

        if (a.GetType().IsArray && b.GetType().IsArray)
        {
            if (a.Length == b.Length)
            {
                for (int i = 0; i < a.Length; i++)
                {
                    if (!ArraysEqual(a[i], b[i]))
                    {
                        return false;
                    }
                }
                return true;
            }
            return false;
        }

        // fall back to simple value compare
        return Equals(a, b);
    }
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.