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?
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;
}
}
}
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.
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.
Related
Ran into the following issue when writing a unit test for my code. Why does the Assert.Equal() fail when comparing an IEnumerable with itself?
private class ReferenceType { }
[Fact]
public void EnumerableEqualityTest()
{
IEnumerable<ReferenceType> GetEnumerable()
{
yield return new ReferenceType();
}
var enumerable = GetEnumerable();
Assert.Equal(enumerable, enumerable); // fails
}
To understand what is going on we need to understand what Assert.Equal() is actually doing. According to the documentation of Assert.Equal<T>(IEnumerable<T> expected, IEnumerable<T> actual) it "Verifies that two sequences are equivalent, using a default comparer".
The Assert.Equal() in this case iterates the enumerable to check if the individual values are equal. This means that the enumerable is iterated twice for the comparison and that a new instance of ReferenceType is created each time (through yield return). The test fails since the default comparer for a reference type only checks if the instances refer to the same object.
There are at least three ways to get the expected result:
Use the overload for Assert.Equal() that takes an argument of IEqualityComparer<T>.
Override the Equals() method of ReferenceType.
Skip yield return and use a collection that implements IEnumerable instead.
Arguably the first solution is the best since it does not change the implementation of GetEnumerable() or ReferenceType. In this case where GetEnumerable() is only used within the test I would opt for the third option as it is the easiest to do. It could look something like this:
IEnumerable<ReferenceType> GetData()
{
return new[] { new ReferenceType() };
}
or this:
IEnumerable<ReferenceType> GetData()
{
var referenceTypes = new List<ReferenceType>();
// ... add reference types
return referenceTypes;
}
This works since we are now iterating the collection that was created when we got the enumerable and not creating a new instance for each iteration.
I think, you can use to below function for problem solution.
public bool AreEquivalentEnumerator(IEnumerator<TSource> first, IEnumerator<TSource> second)
{
while (first.MoveNext())
{
if (!(second.MoveNext() && Equals(first.Current, second.Current))) return false;
}
if (second.MoveNext()) return false;
return true;
}
I have a complex type as:
class Row : IEquatable<Row>
{
public Type Type1 { get; }
public Type Type2 { get; }
public int dummy;
public override int GetHashCode()
{
var type1HashCode = Type1.GetHashCode();
//djb2 hash
unchecked
{
return ((type1HashCode << 5) + type1HashCode) ^ Type2.GetHashCode();
}
}
// Equals method also overrided
}
I have a HashSet<Row> and I want to merge it with another HashSet with two different strategies; first I want to merge and keep duplicates from main HashSet, I tried main.UnionWith(second) now I want to merge main with second (result being in main) and keep duplicates from second one; How can I do that? (it's a performance critical code)
My code:
var main = new HashSet<Row>()
{
new Row(typeof(int), typeof(long))
{
dummy = 10
}
};
var second = new HashSet<Row>()
{
new Row(typeof(int), typeof(long))
{
dummy = 20
}
};
// Merge here.
Trace.Write(main.First().dummy) //I want 20
I expect main.First().dummy to be 20.
The second strategy can be implemented by calling main.ExceptWith(second); first and then main.UnionWith(second) like the first strategy.
Since the UnionWith is basically a shortcut for
foreach (var element in second)
main.Add(element);
and ExceptWith - a shortcut for
foreach (var element in second)
main.Remove(element);
the second strategy can also be implemented with a single loop:
foreach (var element in second)
{
main.Remove(element);
main.Add(element);
}
But I think the performance gain would be negligible compared to ExceptWith + UnionWith approach.
If I'm reading this correctly, you want to keep duplicated values after merging. In this scenario, HashSet is the wrong data structure for your objective.
From the MSDN documentation for HashSet(T):
A HashSet collection is not sorted and cannot contain duplicate elements. If order or element duplication is more important than performance for your application, consider using the List class together with the Sort method.
I am presently sorting a C# list using the 'CompareTo' method in the object type contained in the list. I want to sort ascendingly all items by their WBS (Work Breakdown Structure) and I can manage this very well using the following code:
public int CompareTo(DisplayItemsEntity other)
{
string[] instanceWbsArray = this.WBS.Split('.');
string[] otherWbsArray = other.WBS.Split('.');
int result = 0;
for (int i = 0; i < maxLenght; i++)
{
if (instanceWbsArray[i].Equals(otherWbsArray[i]))
{
continue;
}
else
{
result = Int32.Parse(instanceWbsArray[i]).CompareTo(Int32.Parse(otherWbsArray[i]));
break;
}
}
return result;
}
Now, I would like to be able to sort considering more than one parameter, as in the project name alphabetically, before considering the second which would be the WBS. How can I do this?
I don't know the details of your class, so I'll provide an example using a list of strings and LINQ. OrderBy will order the strings alphabetically, ThenBy will order them by their lengths afterwards. You can adapt this sample to your needs easily enough.
var list = new List<string>
{
"Foo",
"Bar",
"Foobar"
};
var sortedList = list.OrderBy(i => i).
ThenBy(i => i.Length).
ToList();
What we generally do in cases like yours is this:
public int CompareTo( SomeClass other )
{
int result = this.SomeProperty.CompareTo( other.SomeProperty );
if( result != 0 )
return result;
result = this.AnotherProperty.CompareTo( other.AnotherProperty );
if( result != 0 )
return result;
[...]
return result;
}
P.S.
When posting code, please try to include only the code which is pertinent to your question. There is a load of stuff in the code that you posted that I did not need to read, and that in fact made my eyes hurt.
I like Eve's answer because of it's flexibility but I'm kinda surprised that no-one has mentioned creating a custom IComparer<T> instance
IComparer<T> is a generic interface that defines a method for comparing two instances of the type T. The advantage of using IComparer<T> is that you can create implementations for each sort order you commonly use and then use these as and when necessary. This allows you to create a default sort order in the types CompareTo() method and define alternative orders separately.
E.g.
public class MyComparer
: IComparer<YourType>
{
public int Compare(YourType x, YourType y)
{
//Add your comparison logic here
}
}
IComparer<T> is particularly useful for composition where you can do things like have a comparer which compares some properties of a given type using another comparer that operates on the type of the property.
It's also very useful if you ever need to define a sorting on a type you don't control. Another advantage it has is it doesn't require LINQ so can be used in older code (.Net 2.0 onwards)
First compare the project name alphabetically and if they are not equal return value, if not perform comparison based on second value
public int CompareTo(DisplayItemsEntity other)
{
if(other.ProjectName.CompareTo(this.ProjectName) != 0)
{
return other.ProjectName.CompareTo(this.ProjectName)
}
//else do the second comparison and return
return result;
}
I am trying to have a data structure with multiple string keys. To do this, I tried to create a Dictionary with string[] element. But the ContainsKey do no seem to work as I expect:
Dictionary<string[], int> aaa = new Dictionary<string[], int>();
int aaaCount = 0;
aaa.Add(new string[] { string1, string2 }, aaaCount++);
if (!aaa.ContainsKey(new string[] { string1, string2 }))
{
aaa.Add(new string[] { string1, string2 }, aaaCount++);
}
I see that there are two entries in aaa after the execution of the code above while I was expecting only one. Is this the expected behaviour? How can I ensure that there are no duplicate entries in the Dictionary?
Note: I tried the same with a list as well (List and the result is the same - the Contains method does not really work with string[])
If you want to use string[] as TKey, you should pass IEqualityComparer<string[]> to the constructor of Dictionary. Because Otherwise Dictionary uses standard comparison for TKey and in case of string[] it just compares references hence string[] is reference type. You have to implement IEqualityComparer yourself. It can be done in the following way:
(The implementation is quite naive, I provide it just as the starting point)
public class StringArrayComparer : IEqualityComparer<string[]>
{
public bool Equals(string[] left, string[] right)
{
if (ReferenceEquals(left, right))
{
return true;
}
if ((left == null) || (right == null))
{
return false;
}
return left.SequenceEqual(right);
}
public int GetHashCode(string[] obj)
{
return obj.Aggregate(17, (res, item) => unchecked(res * 23 + item.GetHashCode()));
}
}
You need to create an IEqualityComparer<string[]> and pass it to the dictionary's constructor.
This tells the dictionary how to compare keys.
By default, it compares them by reference.
Because an array is a reference type, i.e., you are checking reference (identity) equality, not equality based on the values within the array. When you create a new array with the same values the arrays themselves are still two distinct objects, so ContainsKey returns false.
Using an array as a Dictionary key is a bit... odd. What are you trying to map here? There is probably a better way to do it.
You may be better off, if your application supports it, to combine the string array into a single string.
We have numerous cases where two pieces of information uniquely identifies a record in a collection and in these cases, we join the two strings using a value that should never be in either string (i.e. Char(1)).
Since it is usually a class instance that is being added, we let the class specify the generation of the key so that the code adding to the collection only has to worry about checking a single property (i.e. CollectionKey).
If I want an empty enumeration, I can call Enumerable.Empty<T>(). But what if I want to convert a scalar type to an enumeration?
Normally I'd write new List<string> {myString} to pass myString to a function that accepts IEnumerable<string>. Is there a more LINQ-y way?
You can use Repeat:
var justOne = Enumerable.Repeat(value, 1);
Or just an array of course:
var singleElementArray = new[] { value };
The array version is mutable of course, whereas Enumerable.Repeat isn't.
Perhaps the shortest form is
var sequence = new[] { value };
There is, but it's less efficient than using a List or Array:
// an enumeration containing only the number 13.
var oneIntEnumeration = Enumerable.Repeat(13, 1);
You can also write your own extension method:
public static class Extensions
{
public static IEnumerable<T> AsEnumerable<T>(this T item)
{
yield return item;
}
}
Now I haven't done that, and now that I know about Enumerable.Repeat, I probably never will (learn something new every day). But I have done this:
public static IEnumerable<T> MakeEnumerable<T>(params T[] items)
{
return items;
}
And this, of course, works if you call it with a single argument. But maybe there's something like this in the framework already, that I haven't discovered yet.