C# CollectionAssert.Contains (int[][], int[]) does not pass - c#

The function I want to test returns an int[][] aaiCluster1 with data
aaiCluster1[0] = int[] { 1, 2 }
aaiCluster1[1] = int[] { 0 }
so I test using
CollectionAssert.AreEqual (aaiCluster1[0], new int[] { 1, 2 });
CollectionAssert.AreEqual (aaiCluster1[1], new int[] { 0 });
But the order of the int[] is not important and may change, so I rather want to test
CollectionAssert.Contains (aaiCluster1, new int[] { 1, 2 });
but this fails.
Is this command not able to evaluate the content of the array?
Any ideas for a suitable workaround, that does not require additional helper functions?
edit
I have to clarify:
it's about testing whether a jagged array contains another simple array nested in it.
What else must I explain??
edit #2
2 answers suggesting CollectionAssert.AreEquivalent were given already and have been deleted again, because AreEquivalent is not what I'm looking for... it does not work here.
1 answer suggesting Sequence.whatever was given, and it's even more wrong.
edit #3
I reverted Igor's changes to my question, because I feel they too much alter the meaning. However, I think his words are a good alternative describtion, so here is his text:
So I test using
CollectionAssert.AreEqual(new int[] { 1, 2 }, aaiCluster1[0]);
CollectionAssert.AreEqual(new int[] { 0 }, aaiCluster1[1]);
But the order in aaiCluster1 (actual) is not known so the above tests could fail as soon as the returned order in aaiCluster1 changes.
Question
How can I call something similar to this
CollectionAssert.AreEqual(new int[] { 1, 2 }, aaiCluster1);
// CollectionAssert.AreEqual(ICollection<T> expected, ICollection<ICollection<T>> actual);
where all arrays in aaiCluster1 are evaluated against the expected parameter new int[] { 1, 2 } and the assert passes if there is at least one contained array that is found to be Equal to the expected?

aaiCluster1.ContainsArray(new int[] { 1, 2 });
static class Extensions
{
public static bool ItemsEqual<TSource>(this TSource[] array1, TSource[] array2)
{
if (array1 == null && array2 == null)
return true;
if (array1 == null || array2 == null)
return false;
return array1.Count() == array2.Count() && !array1.Except(array2).Any();
}
public static bool ContainsArray<TSource>(this TSource[][] array1, TSource[] array2)
{
return array1.Any(x => x.ItemsEqual(array2));
}
}

If you want to make sure the on set fully contains another you can just use linq, Assuming there are no duplicates you can do this:
Assert.IsTrue(new int[] { 1, 2 }.Except(aaiCluster1[0]).Count() == 0);

Related

Return null if array element does not exist

Is there a simple^ way of getting the value 'null' if an array element does not exist?
For example, in the code below sArray has 3 elements and the first 3 calls to SomeMethod work (prints true), however the 4th call SomeMethod(sArray[3]); gives me an IndexOutOfRangeException. Is there a way to make the 4th call to SomeMethod print false?
static void Main(string[] args)
{
int[] sArray = new int[]{1,2,3};
SomeMethod(sArray[0]);
SomeMethod(sArray[1]);
SomeMethod(sArray[2]);
SomeMethod(sArray[3]);
}
static void SomeMethod(int? s) => Console.WriteLine(s.HasValue);
^Would prefer single line expression
There is a Linq method ElementAtOrDefault
To use it the way you want to (returning null) you will need ti change the underlying type of your array to nullable int:
int?[] sArray = new int?[]{1,2,3};
SomeMethod(sArray.ElementAtOrDefault(1000));
How about an extension method?
public static T? TryGet<T>(this T[] source, int index) where T: struct
{
if (0 <= index && index < source.Length)
{
return source[index];
}
else
{
return null;
}
}
Then you could write:
static void Main(string[] args)
{
int[] sArray = new int[]{1,2,3};
SomeMethod(sArray.TryGet(0));
SomeMethod(sArray.TryGet(1));
SomeMethod(sArray.TryGet(2));
SomeMethod(sArray.TryGet(3));
}
SomeMethod(sArray.Skip(3).Select(z => (int?)z).FirstOrDefault());
is a working replacement of:
SomeMethod(sArray[3]);
The former will call SomeMethod with null (while the latter will throw an exception if the array doesn't have at least 4 entries).
In Skip(3) the 3 can be changed to whatever index you want to retrieve from the array. The Select is needed to project the int into a int? so that FirstOrDefault returns either the 4th element or null.
If you don't want to use LINQ then you could use:
SomeMethod(sArray.Length > 3 ? sArray[3] : (int?)null);
instead.
Or consider using:
foreach (var entry in sArray.Take(4))
{
SomeMethod(entry);
}
to loop through up to 4 elements of the array (it will work fine if there are fewer than 4 - it will just make fewer calls to SomeMethod).
Arrays in C# have a .Length property which you can check before trying to pass an item from one to SomeMethod, and the typical approach is to loop through each element of the array rather than guessing whether or not an index is valid:
for (int i = 0; i < sArray.Length; i++)
{
SomeMethod(sArray[i]);
}
You will not be able to avoid an IndexOutOfRangeException if you reference an index in an array that doesn't exist.
However, if you really want a method with this type of functionality, you could simply modify your existing code to check whether or not the index specified is greater than the length of the array.
Since your array is an int[] (and not an int?[]), all valid indexes will have a value. Also, we can use the ?. to handle cases where the array itself may be null:
private static void SomeMethod(int[] array, int index) =>
Console.WriteLine(index >= 0 && index < array?.Length);
Then in use, instead of passing an array item with an invalid index (which will always throw an IndexOutOfRangeException), you would pass the array itself and the index separately:
static void Main()
{
int[] sArray = new int[] { 1, 2, 3 };
SomeMethod(sArray, 0);
SomeMethod(sArray, 1);
SomeMethod(sArray, 2);
SomeMethod(sArray, 3);
SomeMethod(null, 0);
GetKeyFromUser("\nPress any key to exit...");
}
Output
in this case I'll suggest you to create a extension somewhere in your code like this
static class ArrExt
{
public static int? Get(this int[] arr, int i)
{
return (i >= 0 && i < arr.Length) ? arr[i] : default(int?);
}
}
then you can do this
int[] sArray = new int[] { 1, 2, 3 };
SomeMethod(sArray.Get(0));
SomeMethod(sArray.Get(1));
SomeMethod(sArray.Get(2));
SomeMethod(sArray.Get(3));
okay this is not a single line solution I know, but it's easier for both programmer and computer.

Checking for an array in a list, with "List<int[]>.Contains(new int[] { .. })", always returns false

I am trying to check if list that consists of int[2] arrays contains certain element.
In short, why this produces false?
And how can I check this properly?
List < int[] > ngonPairs = new List<int[]> {new int[2] { 0, 1 }};
bool flag = ngonPairs.Contains(new int[2] { 0, 1 });
Flag is always false.
This is because
new[]{1, 2} != new[]{1, 2}
They are different arrays, and changes to one won't be reflected in the other.
However using LINQ's SequenceEqual, you can compare the contents of two sequences:
new[]{1, 2}.SequenceEqual(new[]{1, 2}) // == true
Now, using LINQ's Any you could:
bool flag = ngonPairs.Any(p => p.SequenceEqual(new int[] {0, 1}));
.Any operates over a sequence and returns true if any of the items in the sequence satisfy the predicate.
In this case, the predicate compares a single items from ngonPairs (i.e. arrays), and we can then use SequenceEqual described above to compare each of those arrays to our known array.
List contains an array object, but you are trying to search for another newly created object. Both array objects are different, so it always return false. If your intention is to compare the values of the array, you can use EqualityComparer to check.
Write a comparer to compare two arrays. Following code snippet is the example and it will just compare int array of size two.
class ArrayComparer : EqualityComparer<int[]>
{
public override bool Equals(int[] x, int[] y)
{
if (x[0] == y[0] && x[1] == y[1])
return true;
else
return false;
}
public override int GetHashCode(int[] obj)
{
throw new NotImplementedException();
}
}
Then use this EqualityComparer instance in the Contains function to compare the array values;
bool flag = ngonPairs.Contains(new int[2] { 0, 1 }, new ArrayComparer());

List.Contains doesn't return true when element exists

I have got a little problem.
I have a list of int arrays that contains of ID (any number) and Count (which can be anything from 0 to 5) which is named playerWordList and second list with IDs only with name words. What I'm trying to do is a function that checks if every ID from second list is present in first (Count doesn't matter) and if it is not present, add element with that missing ID and Count equal to 0.
At the moment, I have a workaround, which is double loop that for every element in second list iterates through every element in first list looking for a match, but I see that there is a method List.Contains that would perfectly match in my problem but it is just not working properly.
I read a few topic on SO about it, but they mainly focus on comparing two elements of custom class, which require overriding Equals and GetHashCode, but I don't know if it applies to my problem in any way, and if it does, then I have no idea how can I override Equals and GetHashCode for int arrays.
Here is my code:
foreach (var word in unit.words)
{
int[] ex1 = new int[2] { word.id, 0 };
int[] ex2 = new int[2] { word.id, 1 };
int[] ex3 = new int[2] { word.id, 2 };
int[] ex4 = new int[2] { word.id, 3 };
int[] ex5 = new int[2] { word.id, 4 };
int[] ex6 = new int[2] { word.id, 5 };
if (playerWordList.Contains(ex1) ||
playerWordList.Contains(ex2) ||
playerWordList.Contains(ex3) ||
playerWordList.Contains(ex4) ||
playerWordList.Contains(ex5) ||
playerWordList.Contains(ex6)) break;
else
{
int[] newWord = new int[2] { word.id, 0 };
playerWordList.Add(newWord);
}
}
You can't override GetHashCode and Equals methods for a built-in type but you can implement an IEqualityComparer for int[] and pass it to Contains method.This is usually the way to go, but if you want a quick way, you can simply use Any method with SequenceEqual:
if(playerWordList.Any(x => x.SequenceEqual(ex1) ||
x.SequenceEqual(ex2) ||
x.SequenceEqual(ex3) ||
x.SequenceEqual(ex4) ||
x.SequenceEqual(ex5) ||
x.SequenceEqual(ex6))
Contains checks on reference equality. So these object instances should be the same. It doesn't check on value equality. I suspect that you insert other instances of ex1(2,3,4,5) somewhere else and that contains doesn't return true because of the references not being equal.
Hope this clarifies some things.
This will work:
int[] ex1 = new int[2] { word.id, 0 };
playerWordList.Insert(ex1)
if(playerWordList.Contains(ex1))
// returns true
But if you create and insert ex1 somewhere else and put it in the playerWordList and then in your foreach create "new" instances of ex objects than these are actually different references to different objects (although with the same values, but Contains doesn't compare values but references ;)

When using Contains on a Queue of Arrays the Console returns False. Why?

I created a program, in which I have a queue of int arrays:
Queue< int[] > Test = new Queue< int[] >();
Given an array, I want to check whether the array is inside the Queue.
The namespaces I'm currently using are: System, System.Collections, and System.Collections.Generic.
I tried something like this:
Queue<int[]> Test = new Queue<int[]>();
Test.Enqueue(new int[] { 20, 20 });
Console.WriteLine( Test.Contains(new int[] { 20, 20 }) ); // Is the array {20,20} inside the queue?
In this code, it seems to me that the Console will output "true", because I added the array {20, 20} to the queue, and now I'm checking using the Contains method.
But when I run the code -- the Console outputs false.
I have two questions:
Why is this happening? And how can I fix this?
Because arrays are reference types the comparison will be based on references. Even if the contents are the same your arrays have different references.That's why you get false.
To fix that you can implement a custom comparer or you can use LINQ methods for example
bool arrayExists = Test.Any(x => x.SequenceEqual(new[] { 20, 20 }));
Contains() uses the type's default comparer. For arrays, it compares the array reference, not the contents. You will need to create a custom equality comparer.
Something like this:
class ArrayComparer : IEqualityComparer<int[]>
{
public bool Equals(int[] x, int[] y)
{
return x.SequenceEqual(y);
}
public int GetHashCode(int[] obj)
{
int h = 0;
foreach (int item in obj)
{
h = (h << 5) + 3 + h ^ item.GetHashCode();
}
return h;
}
}
Test.Contains(new int[] { 20, 20 }, new ArrayComparer())
Arrays are compared by reference by default, not contents. One way of fixing this is to store lists instead of arrays, which will compare contents. You can convert your arrays into lists before adding to the queue, and back again after taking them out if that's important.

Pass array to function from specific index in C# .Net

I am new to C# (previously working on C++), I used to pass array to function with specific index. Here is the code in C++,
void MyFunc(int* arr) { /*Do something*/ }
//In other function
int myArray[10];
MyFunc(&myArray[2]);
Can I do something like this in C# .Net* ?*
As Array is Enumerable, you can make use of LINQ function Skip.
void MyFunc( int[] array ){ /*Do something*/ }
int[] myArray = { 0, 1, 2, 3, 4, 5, 6 }
MyFunc( myArray.Skip(2) );
The linq version is my preferred one. However it will prove to be very very inefficient.
You could do
int myArray[10];
int mySlice[8];
Array.Copy(myArray, 2, mySlice, 0);
and pass mySlice to the function
.NET has the System.ArraySegment<T> structure which addresses this exact use-case.
But I’ve never actually seen this structure used in code, with good reasons: it doesn’t work. Notably, it doesn’t implement any interfaces such as IEnumerable<T>. The Linq solution (= using Skip) is consequently the best alternative.
Probably the most simple way to do this is:
public void MyFunction(ref int[] data,int index)
{
data[index]=10;
}
And call it by this way:
int[] array= { 1, 2, 3, 4, 5 };
Myfunction(ref array,2);
foreach(int num in array)
Console.WriteLine(num);
This will print 1,2,10,4,5
public void MyFunc(ref int[] arr)
{
// Do something
}
int[] myArray = .... ;
MyFunc(ref myArray);
See here for more information on ref!

Categories