Reputation: 11
I have this function:
public static IEnumerable<T> UniqueInOrder<T>(IEnumerable<T> iterable)
{
return iterable.Distinct();
}
This works with any IEnumerable except with a string, I must test it with this call:
Assert.AreEqual("ABCDAB", UniqueInOrder("AAAABBBCCDAABBB"));
The assert fail:
Expected is <System.String>, actual is <System.Linq.Enumerable+<DistinctIterator>c__Iterator10`1[System.Char]>
Values differ at index [4]
I also tried something like:
public static IEnumerable<T> UniqueInOrder<T>(IEnumerable<T> iterable)
{
return "abc";
}
But I have a compiler error:
Cannot implicitly convert type 'string' to 'System.Collections.Generic.IEnumerable<T>'
How is possible that I can call the function with a string but I can not return a string? The type is still the same IEnumerable< T >
Any idea? Thank you!
EDITED: Distinct() was wrong, I changed to this:
public static IEnumerable<T> UniqueInOrder<T>(IEnumerable<T> iterable)
{
List<T> result = new List<T>();
foreach (var c in iterable.ToList())
if (result.LastOrDefault() == null || !result.LastOrDefault().Equals(c))
result.Add(c);
return result;
}
Now all the tests are passing! Thanks!
Upvotes: 1
Views: 4566
Reputation: 37000
Actually a string is a collection of characters, thus when you use UniqueInOrder("AAAABBBCCDAABBB")
you actuall call UniqueInOrder<char>
, instead of UniqueInOrder<string>
. Thus the return-value of the method will also be IEnumerble<char>
, not just string.
So you should compare the methods return-value with a collection of characters, e.g.:
CollectionAssert.AreEqual(new[] { 'A', 'B', 'C', 'D', 'A' 'B', 'Ä' }, UniqueInOrder(...));
or easier:
CollectionAssert.AreEqual(expected.ToCharArray(), UniqueInOrder(...));
But even then your test will fail as Distinct
will filter out all duplicates.
When you want to check if two sequences are completely identical you may use SequenceEqual
instead:
var equal = firstColl.SequenceEqual(second);
EDIT: Obviously you´re trying to remove all just the doubled characters in a sequence. You may use this:
public static IEnumerable<T> UniqueInOrder<T>(IEnumerable<T> iterable) where T : IEquatable<T>
{
T previous = default(T);
foreach (var t in iterable)
{
if (!t.Equals(previous))
yield return t;
previous = t;
}
}
Now you can call it and compare it with your expected output, e.g.:
var actual = new String(UniqueInOrder("AABBCCDDAABB").ToArray());
var expected "ABCDAB";
Assert.AreEqual(expected, actual);
Upvotes: 7
Reputation: 460158
Himbrombeere has explained why the assertion fails because of the type issue. But it will also fail because Distinct
does not what you expect. It will remove all duplicate letters, not only those which are consecutive. So it will return ABCD
instead of ABCDAB
.
This will do what you want and the assertion will pass:
public static IEnumerable<T> UniqueInOrder<T>(IEnumerable<T> iterable, EqualityComparer<T> comparer = null)
{
if (comparer == null) comparer = EqualityComparer<T>.Default;
bool first = true;
T lastItem = default(T);
foreach(T thisItem in iterable)
{
if (first || !comparer.Equals(thisItem, lastItem))
{
first = false;
yield return thisItem;
}
lastItem = thisItem;
}
}
Assert.AreEqual("ABCDAB", String.Conat(UniqueInOrder("AAAABBBCCDAABBB")));
Upvotes: 2
Reputation: 750
UniqueInOrder("AAAABBBCCDAABBB")
returns "ABCD"
, as these are the distinct characters in the string. This is not equal to "ABCDAB"
, and the difference is in the 5th character (or character 4 in code terms, since am IEnumerable
is 0-indexed).
This is exactly what the assert fail is telling you. The characters differ at the element in the enumerable with index 4. Specifically, in the second argument that index doesn't exist. Assert.AreEqual("ABCD", UniqueInOrder("AAAABBBCCDAABBB"))
will work fine
You get a compiler error in the return "abc"
case because "abc"
, a string, is not a generic IEnumerable
. If you passed in an IEnumerable<int>
the types would be incompatible so it has to be a compiler error
Upvotes: 0
Reputation: 1753
As to the first part of your question, the reason your assertion is failing is because of the .Distinct()
When calling
UniqueInOrder("AAAABBBCCDAABBB");
This method:
private IEnumerable<T> UniqueInOrder<T>(IEnumerable<T> iterable)
{
return iterable.Distinct();
}
will return {'A', 'B', 'C', 'D'}
However this method:
private IEnumerable<T> RemovesDupesInOrder<T>(IEnumerable<T> iterable)
{
List<T> result = new List<T>();
T last = default(T);
iterable.ToList().ForEach(t =>
{
if (t.Equals(last) == false)
{
last = t;
result.Add(t);
}
});
return result;
}
would return {'A', 'B', 'C', 'D', 'A', 'B'}
BEWARE: t.Equals(last)
may or may not do what you expect with reference types.
Upvotes: 1