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.
Related
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;
}
}
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])
}
}
}
I have code that needs to know that a collection should not be empty or contain only one item.
In general, I want an extension of the form:
bool collectionHasAtLeast2Items = collection.AtLeast(2);
I can write an extension easily, enumerating over the collection and incrementing an indexer until I hit the requested size, or run out of elements, but is there something already in the LINQ framework that would do this? My thoughts (in order of what came to me) are::
bool collectionHasAtLeast2Items = collection.Take(2).Count() == 2; or
bool collectionHasAtLeast2Items = collection.Take(2).ToList().Count == 2;
Which would seem to work, though the behaviour of taking more elements than the collection contains is not defined (in the documentation) Enumerable.Take Method , however, it seems to do what one would expect.
It's not the most efficient solution, either enumerating once to take the elements, then enumerating again to count them, which is unnecessary, or enumerating once to take the elements, then constructing a list in order to get the count property which isn't enumerator-y, as I don't actually want the list.
It's not pretty as I always have to make two assertions, first taking 'x', then checking that I actually received 'x', and it depends upon undocumented behaviour.
Or perhaps I could use:
bool collectionHasAtLeast2Items = collection.ElementAtOrDefault(2) != null;
However, that's not semantically-clear. Maybe the best is to wrap that with a method-name that means what I want. I'm assuming that this will be efficient, I haven't reflected on the code.
Some other thoughts are using Last(), but I explicitly don't want to enumerate through the whole collection.
Or maybe Skip(2).Any(), again not semantically completely obvious, but better than ElementAtOrDefault(2) != null, though I would think they produce the same result?
Any thoughts?
public static bool AtLeast<T>(this IEnumerable<T> source, int count)
{
// Optimization for ICollection<T>
var genericCollection = source as ICollection<T>;
if (genericCollection != null)
return genericCollection.Count >= count;
// Optimization for ICollection
var collection = source as ICollection;
if (collection != null)
return collection.Count >= count;
// General case
using (var en = source.GetEnumerator())
{
int n = 0;
while (n < count && en.MoveNext()) n++;
return n == count;
}
}
You can use Count() >= 2, if you sequence implements ICollection?
Behind the scene, Enumerable.Count() extension method checks does the sequence under loop implements ICollection. If it does indeed, Count property returned, so target performance should be O(1).
Thus ((IEnumerable<T>)((ICollection)sequence)).Count() >= x also should have O(1).
You could use Count, but if performance is an issue, you will be better off with Take.
bool atLeastX = collection.Take(x).Count() == x;
Since Take (I believe) uses deferred execution, it will only go through the collection once.
abatishchev mentioned that Count is O(1) with ICollection, so you could do something like this and get the best of both worlds.
IEnumerable<int> col;
// set col
int x;
// set x
bool atLeastX;
if (col is ICollection<int>)
{
atLeastX = col.Count() >= x;
}
else
{
atLeastX = col.Take(x).Count() == x;
}
You could also use Skip/Any, in fact I bet it would be even faster than Take/Count.
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.
I have two enumerables with the exact same reference elements, and wondering why Equals wouldn't be true.
As a side question, the code below to compare each element works, but there must be a more elegant way
var other = (ActivityService) obj;
if (!AllAccounts.Count().Equals(other.AllAccounts.Count())) return false;
for (int i = 0; i < AllAccounts.Count(); i++) {
if (!AllAccounts.ElementAt(i).Equals(other.AllAccounts.ElementAt(i))) {
return false;
}
}
return true;
Have a look at the Enumerable.SequenceEqual method.
bool result = AllAccounts.SequenceEqual(other.AllAccounts);
Depending on the data type you may also need to use the overloaded method that accepts an IEqualityComparer to define a custom comparison method.
.Equals is comparing the references of the enumerables, not the elements they contain.