Sequence equality of object arrays containing two dimensional object arrays - c#

I wanted to code a function doing something with an input object[,] and returning an object[] whose elements are object[,]'s. I just coded something stupid for a start with no input to first set up tests, and then I will properly code the function :
public static class TestData
{
public static object[,] Island1()
{
object[,] res = new object[3, 2];
res[0, 0] = 1;
res[0, 1] = 0;
res[1, 0] = 1;
res[1, 1] = 1;
res[2, 0] = 1;
res[2, 1] = 0;
return res;
}
}
public class ComponentsFinder
{
public object[] GetIslands()
{
return new object[]{TestData.Island1()};
}
}
and the test :
[TestClass]
public class TestCompomentsFinder
{
[TestMethod]
public void FirstTest()
{
object[,] island1 = TestData.Island1();
object[] expected = new object[] {island1};
object[] actual = new ComponentsFinder().GetIslands();
bool res = actual.SequenceEqual(expected);
Assert.IsTrue(res);
}
}
This test fails and I know why : even if both object[]'s contain only one object representing an object[,] that represents the same "matrix", they do not point to the same array, hence the failure of the test.
Would I have a real class C instead of object[,] it wouldn't be a problem as I would make C implement IEquatable properly, and the Equals override would then be called by SequenceEquals and the test would be ok.
But here I don't have a class, so what should I do ? Should I really wrap everything in classes or is there another way to test the equality of my object[]'s (which amounts to know to test equality of object[,]'s in the sense of having same dimensions and same respective coefficients, the coefficients of my object[,]'s are type implementing IEquatable) ?

You're correct on why SequenceEqual() fails. Arrays in C# are reference types, which means when you compare them you get reference equality, which means the CLR checks to see if they are literally the same object in memory (the two object[,]s are different objects in memory.)
In addition, SequenceEqual() is iterating through your outermost object[]'s elements, but it will not reach into those arrays's contents to iterate over the inner object[,].
You need value equality, so that you can compare the values of the objects rather than their references.
However, since you're using objects for everything, you're not going to get value equality, even if the objects are actually ints. See this example from the C# interactive window:
> object object1 = 1;
> object object2 = 1;
> object1 == object2
false
> (int)object1 == (int)object2
true
You'll need to cast the individual values back into ints before you can do a proper comparison on them. I would recommend using int arrays anyway, just for better type safety (and probably a tiny bit better performance from not performing a bunch of boxing conversions.)
All that being said, the general approach would be to iterate over every individual item and compare them individually. If they're ints in object[,] arrays, make sure to cast them ((int)) first. As for how you implement this comparison, you have several options.
You can implement your own containing class, as you've already mentioned. In that case you could override/implement interfaces to provide the functionality you need. You can also implement a standalone helper method (something like public void ArrayCompare(object[,] arr1, object[,] arr2), for instance.)
I implemented an example as an extension method, which will allow you to use either ArrayExtension.ArrayCompare(actual, expected) or actual.ArrayCompare(expected) to use it.
public static class ArrayExtension
{
public static bool ArrayCompare(this object[,] arr1, object[,] arr2)
{
if (arr1.Rank != arr2.Rank) return false;
var numDims = arr1.Rank;
for(var i = 0; i < numDims; i++)
if (arr1.GetLength(i) != arr2.GetLength(i))
return false;
for(var j = 0; j < numDims; j++)
{
var dimLength = arr1.GetLength(j);
for(var k = 0; k < dimLength; k++)
if ((int)arr1[j, k] != (int)arr2[j, k])
return false;
}
return true;
}
}

Related

Struct type comparison

Imagine I have an IEnumerable<T> where T is a struct type. For each element of the collection I want to check whether there is another element with the same value.
Firstly, I thought about something like this:
IEnumerable<T> collection = someInput;
foreach(var element in collection)
{
try
{
collection.First(x => x == element &&
x.GetHashCode() =! element.GetHashCode());
DoA(element);
}
catch
{
DoB(element);
}
}
But then I found out that hashes are actually equal for structures having same values. Apparently, Object.ReferenceEquals(x, element) is not a way as well.
So, there are 2 questions:
Is there an option to distinguish two different struct variables with the same values?
Is there any other other ways to solve my problem?
Thanks
Is there an option to distinguish two different struct variables with the same values?
No, structs are so called value types. They are only defined by their values and have no reference. If you want to distinguish two instances which have equal values you have to use a class instead of a struct. Classes are reference types and therefore are distinguishable even if they have equal values because they have different references.
In this case however you also have their position in the collection which could be used to distinguish them (it's bascially also some kind of reference).
Is there any other other ways to solve my problem?
As noted above you may use the position. Here's a simple basic implementation without LINQ. You can certainly make a shorter one with LINQ.
for (var i = 0; i < collection.Count; i++)
{
var foundEqual = false;
for (var j = 0; j < collection.Count; j++)
{
if (j != i && collection[i] == collection[j])
{
foundEqual = true;
break;
}
}
if (foundEqual)
{
DoA(collection[i]);
}
else
{
DoB(collection[i]);
}
}
If your struct doesn't implement IEquatable yet you have to implement it to make the equality comparison work. Look here for an explanation and an example:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/how-to-define-value-equality-for-a-type
You should never rely on GetHashCode() for equality comparison as an equal hash code does not guarantee that the compared objects are actually equal.
Structs will autmatically be compared to each other by a hashcode generated from their fields, so you should be able to use the Equals method to determine if there are any duplicates in your collection. One simple way to do this is to group your collection by the struct itself, and then compare the counts of the grouped items and the original collection. If there were any identical items, they would be grouped together, and the grouped item count would be less than the original collection count:
bool hasDuplicates = collection.GroupBy(i => i).Count() < collection.Count();
In general, you can compare items using the Equals method:
public bool HasDuplicateElement<T>(IEnumerable<T> items) where T : struct
{
if (items == null || items.Count() < 2) return false;
for(int i = 0; i < items.Count() - 1; i++)
{
for (int j = i + 1; j < items.Count(); j++)
{
if (items.ElementAt(i).Equals(items.ElementAt(j))) return true;
}
}
return false;
}
Note that if the struct has reference-type fields, and they don't have a meaningful equality implementation (i.e. they use the default reference equality), then you'll have to write your own IEqualityComparer<T> and pass that to the GroupBy method.

How do I use if/else in a generic function? C#

First I'd like to say that im pretty new in regards to coding, so don't kill me if the code looks horrible.
Alright, so the problem is that im trying to make a generic function which purpose is to take two sorted arrays and merge them into a new sorted array.
The problem im facing is trying to use if's in the function, and it doesn't let me use the < operand.
public object[] mergeTwoSorted<Tone, Ttwo>(Tone[] array, Ttwo[] array2)
{
object[] mergedArray = new object[array.Length + array2.Length];
for (int i = 0; i < mergedArray.Length; i++)
{
if (array is ValueType && array2 is ValueType)
{
if (array[i] > array2[i])
}
}
}
Any idea on how to tackle this problem?
First I'd like to say that im pretty new in regards to coding, so don't kill me if the code looks horrible.
It looks pretty reasonable so far. It looks like what you're trying to do is to take two sorted arrays of two different types, but there is an ordering relationship between the two types. You're then merging the two arrays into an array of objects, such that the objects from the two arrays are still in the same order, but they are interleaved with each other to match the inter-type ordering relation, yes?
That is an unusual thing to do, but it is possible.
If that is not what you are doing, then you need to stop and re-design the code. In particular, if your intention is to take two arrays of the same type, then you must have one type parameter, not two, and you must make an array of T as the output, not object.
The problem is that there is in general no way to express "I have an ordering relationship between these two types" in C#. You'll have to provide a function that does that. Traditionally we provide a function that takes the two types and returns an integer: -1 if the first is the smaller, 1 if the second is the smaller, and 0 if they are equal.
If we have that then your method becomes:
public static object[] MergeTwoSorted<TOne, TTwo> (
TOne[] items1,
TTwo[] items2,
Func<TOne, TTwo, int> comparer)
{
Note that we are using standard C# conventions here. Methods begin with a capital, type parameters are TSomething, and so on. A Func<A, B, C> is a function that takes an A, a B, and returns a C. Methods that do not manipulate an instance of their class are static.
Note that there is no need to re-state in the name of a thing what its type is. Say what the thing is logically, not how it is stored:
object[] merged = new object[items1.Length + items2.Length];
Now your loop needs some work:
for (int i = 0; i < mergedArray.Length; i++)
{
if (array is ValueType && array2 is ValueType)
{
if (array[i] > array2[i])
Arrays are never value types. I think you do not understand the difference between values and references, and that is really important to understand, so do some research on that.
Also, you have a counter i which counts the big merged array, but you use that as an index into the small arrays. That is very wrong; do you see why?
Think about it this way: you are going to count through both arrays at the same time filling in the merged array. So that would be:
int current1 = 0;
int current2 = 0;
while (current1 + current2 < merged.Length)
{
if (comparer(items1[current1], items2[current2]) < 0)
{
// items1[current1] is the smaller
merged[current1+current2] = items1[current1];
current1 += 1;
}
else
{
There is a bug in the code above; can you find it? Hint: indices for all array accesses must be >= 0 and < Length of the array. Is there a way in the code I've written so far that this gets violated?
Can you fix the bug and finish it off?
Can you now make a call site that takes an array of strings, an array of numbers, and an ordering relationship between strings and numbers, and merges the arrays?
public static object[] mergeTwoSorted<Tone, Ttwo>(Tone[] array, Ttwo[] array2)
where Tone : IComparable<Ttwo>
{
var mergedArray = new object[array.Length + array2.Length];
for (int i = 0; i < mergedArray.Length; i++)
{
if (array is ValueType && array2 is ValueType)
{
if (array[i].CompareTo( array2[i] )>0)
}
}
return mergedArray;
}
If you want to use specific operations on Generics, you have to limit them with a where clause accordingly. As when working with Fractural Numebrs, you need a common denominator.
Something like a "IMergable" interface that you have to write yourself.
public IMergeable[] mergeTwoSorted<Tone, Ttwo> (Tone[] array, Ttwo[] array2)
where Tone : IMergeable, Ttwo : IMergeable
{
IMergeable[] mergedArray = new IMergeable[array.Length + array2.Length];
for (int i = 0; i < mergedArray.Length; i++)
{
if (array is ValueType && array2 is ValueType)
{
if (array[i] > array2[i])
}
}
}
Alternatively, you could jsut use one type:
public Tone[] mergeTwoSorted<Tone> (Tone[] array, Tone[] array2)
{
IMergeable[] mergedArray = new IMergeable[array.Length + array2.Length];
for (int i = 0; i < mergedArray.Length; i++)
{
if (array is ValueType && array2 is ValueType)
{
if (array[i] > array2[i])
}
}
}

return multidimensional jagged array as an array in c#

I have a multidimensional jagged string array:
string[,][] MDJA =
{
{new string[]{"a", "b"}, new string[]{"c", "d"}, new string[]{"e", "f"}},
{new string[]{"g", "h"}, new string[]{"j", "i"}, new string[]{"k", "l"}},
{new string[]{"m", "n"}, new string[]{"o", "p"}, new string[]{"q", "r"}}
}
I'm using for-loops to compare the placement of the arrays inside the array to get the array I'm looking for, but the MDJA is inside a method and i would like it to return the specific array. As an example i might want to return
new string[]{"m", "n"}
Normally I would do this in a multidimensional-array:
for (byte i = 0; i < 3; i++)
{
if (var1[x] == var2[i])
{
return answers[y,i]
}
}
But i haven't used jagged arrays before and when using them multidimensionally it made it harder to get information.
P.S The 4 variables are arguments in the method, var1 and var2 are string arrays and x/y are integers.
Thank you for helping.
I am not quite sure what your method logic looks like but regarding element access it should be quite trivial:
for (int i = 0; i < MDJA.GetLength(0); i++)
{
for (int j = 0; j < MDJA.GetLength(1); j++)
{
// Your compare logics go here
//
bool found = i == 2 && j == 0;
if (found)
{
return MDJA[i, j];
}
}
}
This will return the string[]{"m", "n"}.
I've done this before, alas I do not have the code here with me.
Build a utility method that calls itself recursively, tests whether an array element is an array itself, if it is not (and so a value) add it to a List, otherwise pass the sub/child array to the recursive method.
Hint, use Array object as the parameter for this method, rather than a defined int[,][] array, thus any form of crazy int[,][][][,,,][][,] can be passed and will still work.
And for your problem, you would have to detect at what level you wish to stop transforming from jagged arrays to values, and then returning those jagged arrays, in a simplified array.
I will post my code later, it may help you.
public static int Count(Array pValues)
{
int count = 0;
foreach(object value in pValues)
{
if(value.GetType().IsArray)
{
count += Count((Array) value);
}
else
{
count ++;
}
}
return count;
}

Removing data from a generic string Array C#

I'm learning about generics, so I'm trying to make my own generic array class, which I can use to make an array of strings or an array of numeric data. This generic class has some different methods, forExample, I can add data and remove data, and search for data. All these methods works like a charm with numeric data, but I'm having some problems with making it work with strings, since it's a reference type and the default value is null, which makes my methods throw null exceptions.
My Class looks like this:
class MyArray<T> : IComparable<T>, IEnumerable<T>, IEnumerator<T>
{
T[] data = new T[10];
private int currentIndex = 0;
//This method works as intended, both with strings and ints
public void Add(T Value)
{
data[currentIndex] = Value;
currentIndex++;
if (currentIndex == data.Length)
{
T[] tmp = new T[data.Length + 10];
data.CopyTo(tmp, 0);
data = tmp;
}
}
//Can't figure out how to make this method work with strings
public void RemoveItem(T Value)
{
T[] tmp = new T[data.Length - 1];
int i = 0;
foreach (T ele in data)
{
if (!ele.Equals(Value))//This line gives me a nullRefException when I'm using strings
{
tmp[i] = ele;
i++;
}
}
data = tmp;
}
My Main form where I'm adding the data and trying to remove it looks like this:
static void Main(string[] args)
{
MyArray<string> StringArray = new MyArray<string>();
StringArray.Add("1");
StringArray.Add("2");
StringArray.RemoveItem("2");
}
I can't figure out a clever way to remove the data again, when it's a string, because of the default value of null.
Thanks in advance :)
Rather than ele.Equals(Value), use EqualityComparer<T>.Default.Equals(ele, Value); this will work correctly with null (for reference-types), Nullable<T> (for value-types), types that implement IEquatable<T>, and any other types using object.Equals.
You might want to cache it, though:
var comparer = EqualityComparer<T>.Default;
//loop
if(comparer.Equals(ele,Value)) {...}

How to compare arrays in C#? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Easiest way to compare arrays in C#
How can I compare two arrays in C#?
I use the following code, but its result is false. I was expecting it to be true.
Array.Equals(childe1,grandFatherNode);
You can use the Enumerable.SequenceEqual() in the System.Linq to compare the contents in the array
bool isEqual = Enumerable.SequenceEqual(target1, target2);
You're comparing the object references, and they are not the same. You need to compare the array contents.
.NET2 solution
An option is iterating through the array elements and call Equals() for each element. Remember that you need to override the Equals() method for the array elements, if they are not the same object reference.
An alternative is using this generic method to compare two generic arrays:
static bool ArraysEqual<T>(T[] a1, T[] a2)
{
if (ReferenceEquals(a1, a2))
return true;
if (a1 == null || a2 == null)
return false;
if (a1.Length != a2.Length)
return false;
var comparer = EqualityComparer<T>.Default;
for (int i = 0; i < a1.Length; i++)
{
if (!comparer.Equals(a1[i], a2[i])) return false;
}
return true;
}
.NET 3.5 or higher solution
Or use SequenceEqual if Linq is available for you (.NET Framework >= 3.5)
There is no static Equals method in the Array class, so what you are using is actually Object.Equals, which determines if the two object references point to the same object.
If you want to check if the arrays contains the same items in the same order, you can use the SequenceEquals extension method:
childe1.SequenceEqual(grandFatherNode)
Edit:
To use SequenceEquals with multidimensional arrays, you can use an extension to enumerate them. Here is an extension to enumerate a two dimensional array:
public static IEnumerable<T> Flatten<T>(this T[,] items) {
for (int i = 0; i < items.GetLength(0); i++)
for (int j = 0; j < items.GetLength(1); j++)
yield return items[i, j];
}
Usage:
childe1.Flatten().SequenceEqual(grandFatherNode.Flatten())
If your array has more dimensions than two, you would need an extension that supports that number of dimensions. If the number of dimensions varies, you would need a bit more complex code to loop a variable number of dimensions.
You would of course first make sure that the number of dimensions and the size of the dimensions of the arrays match, before comparing the contents of the arrays.
Edit 2:
Turns out that you can use the OfType<T> method to flatten an array, as RobertS pointed out. Naturally that only works if all the items can actually be cast to the same type, but that is usually the case if you can compare them anyway. Example:
childe1.OfType<Person>().SequenceEqual(grandFatherNode.OfType<Person>())
Array.Equals is comparing the references, not their contents:
Currently, when you compare two arrays with the = operator, we are really using the System.Object's = operator, which only compares the instances. (i.e. this uses reference equality, so it will only be true if both arrays points to the exact same instance)
Source
If you want to compare the contents of the arrays you need to loop though the arrays and compare the elements.
The same blog post has examples of how to do this. The basic implementation is:
public static bool ArrayEquals<T>(T[] a, T[] b)
{
if (a.Length != b.Length)
{
return false;
}
for (int i = 0; i < a.Length; i++)
{
if (!a[i].Equals(b[i]))
{
return false;
}
}
return true;
}
Though this will have performance issues. Adding a constraint:
public static bool ArrayEquals<T>(T[] a, T[] b) where T: IEquatable<T>
will improve things but will mean the code only works with types that implement IEquatable.
Using EqualityComparer.Default's Equal method instead of calling Equals on the types themselves will also improve performance without requiring the type to implement IEquatable. In this case the body of the method becomes:
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
for (int i = 0; i < a.Length; i++)
{
if (!comparer.Equals(a[i], b[i]))
{
return false;
}
}
The Equals method does a reference comparison - if the arrays are different objects, this will indeed return false.
To check if the arrays contain identical values (and in the same order), you will need to iterate over them and test equality on each.
Array.Equals() appears to only test for the same instance.
There doesn't appear to be a method that compares the values but it would be very easy to write.
Just compare the lengths, if not equal, return false. Otherwise, loop through each value in the array and determine if they match.

Categories