c# Cast string as IEnumerable<T> - c#

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!

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);

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")));

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.

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

Related

How to check whether two lists items have value equality using EqualityComparer? [duplicate]

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.

Linq error: "string[] does not contain a definition for 'Except'."

Here's my code:
public static string[] SplitKeepSeparators(this string source, char[] keptSeparators, char[] disposableSeparators = null)
{
if (disposableSeparators == null)
{
disposableSeparators = new char[] { };
}
string separatorsString = string.Join("", keptSeparators.Concat(disposableSeparators));
string[] substrings = Regex.Split(source, #"(?<=[" + separatorsString + "])");
return substrings.Except(disposableSeparators); // error here
}
I get the compile time error string[] does not contain a definition for 'Except' and the best extension method overload ... has some invalid arguments.
I have included using System.Linq in the top of the source file.
What is wrong?
Your substrings variable is a string[], but disposableSeparators is a char[] - and Except works on two sequences of the same type.
Either change disposableSeparators to a string[], or use something like:
return substrings.Except(disposableSeparators.Select(x => x.ToString())
.ToArray();
Note the call to ToArray() - Except just returns an IEnumerable<T>, whereas your method is declared to return a string[].
Your problem is that you are using .Except<T>(this IEnumerable<T> source, IEnumerable<T> other) with two different types for T (string and char). Change your delimiters to a string array if you want to use Except.
https://msdn.microsoft.com/en-us/library/vstudio/bb300779%28v=vs.100%29.aspx

Issue with LINQ SequenceEquals extension in C#

I was trying out possibilities to check a string to be an palindrome with the following logic
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Anagram solver");
Console.WriteLine(IsPalindrome("HIMA", "AMHI").ToString());
Console.ReadKey();
}
static bool IsPalindrome(string s1, string s2)
{
return s1.OrderBy(c => c).SequenceEqual(s2.OrderBy(c => c));
}
}
My idea was to get character literals in a string, and compare with that of characters from another string to deduce for a possible palindrome. Is such a thing possible with LINQ SequenceEqual method ?
Looking from the sample above,
'H' shall be compared with 'A' (default equality comparison)
'I' shall be compared with 'M'
'M' shall be compared with 'H'
'A' shall be compared with 'I'
Can any one guide me here.
Thanks and Cheers
Srivatsa
If you want palindrome then you should not order them, just reverse and match -
static bool IsPalindrome(string s1, string s2)
{
return s1.SequenceEqual(s2.Reverse());
}
for case-insensitivity try -
static bool IsPalindrome(string s1, string s2)
{
return s1.ToLower().SequenceEqual(s2.ToLower().Reverse());
}
In your case, "HIMA" and "AMHI" are sorted by the OrderBy LINQ function, which results in two collections containing the characters "AHIM". If you call SequenceEqual this returns true.
For SequenceEqual to return true, both collections have to have the same amount of elements in exactly the same order. No elements are allowed to be duplicated or stored at another position.
If you want to determine if two words are anagrams, that is exactly the functionality you want.
For palindromes, you could use the following:
public bool CheckPalindrome(string first, string second)
{
if (first == null) throw new ArgumentNullException("first");
if (second == null) throw new ArgumentNullExcpetion("second");
return first.Reverse().SequenceEquals(second);
}
You could use this method:
public static bool IsPalindromWith(this string str1, string str2)
{
if(str1 == null || str2 == null) return false;
return str1.SequenceEqual(str2.Reverse());
}
Usage: bool isPalindrom = "HIMA".IsPalindromWith("AMIH");
However, it is a very simple approach which ignores many edge cases.
Here is a better version that takes at least the case into account:
public static bool IsPalindromWith(this string str1, string str2, StringComparison comparison = StringComparison.CurrentCultureIgnoreCase)
{
if(str1 == null || str2 == null) return false;
char[] str2Chars = str2.ToCharArray();
Array.Reverse(str2Chars);
return str1.Equals(new String(str2Chars), comparison);
}
To elaburate on the existing (and i my opinion corrent) answer by #feO2x
Try looking at it like this:
static bool IsAnagram(string s1, string s2)
{
var lst1 = s1.OrderBy(c => c); //will result in { 'A','H','I', 'M' }
var lst2 = s2.OrderBy(c => c); //will *also* result in { 'A','H','I', 'M' }
return lst1.SequenceEqual(lst2);
}
The OrderBy(...) destroys the original order which you are trying to test.
Simply removing them will solve your problem:
static bool IsAnagram(string s1, string s2)
{
var lst1 = s1.AsEnumerable();
var lst2 = s2.AsEnumerable();
return lst1.SequenceEqual(lst2);
}

how to handle double.NaN for Generic method accepting IComparable in C#

I have a generic GetMinimum method. It accepts array of IComparable type (so it may be string[] or double[]). in the case of double[] how can I implement this method to ignore the double.NaN values? (I'm looking for good practices)
when I pass this array
double[] inputArray = { double.NaN, double.NegativeInfinity, -2.3, 3 };
it returns the double.NaN!
public T GetMinimum<T>(T[] array) where T : IComparable<T>
{
T result = array[0];
foreach (T item in array)
{
if (result.CompareTo(item) > 0)
{
result = item;
}
}
return result;
}
Since both NaN < x and NaN > x will always be false, asking for the minimum of a collection that can contain NaN is simply not defined. It is like dividing by zero: there is no valid answer.
So the logical approach would be to pre-filter the values. That will not be generic but that should be OK.
var results = inputArray.EliminateNaN().GetMinimum();
Separation of concerns: the filtering should not be the responsibility (and burden) of GetMinimum().
You can't from inside the method. The reason is you have no idea what T can be from inside the method. May be you can by some little casting, but ideally this should be your approach:
public T GetMinimum<T>(T[] array, params T[] ignorables) where T : IComparable<T>
{
T result = array[0]; //some issue with the logic here.. what if array is empty
foreach (T item in array)
{
if (ignorables.Contains(item)
continue;
if (result.CompareTo(item) > 0)
{
result = item;
}
}
return result;
}
Now call this:
double[] inputArray = { double.NaN, double.NegativeInfinity, -2.3, 3 };
GetMinimum(inputArray, double.NaN);
If you're sure there is only item to be ignored, then you can take just T as the second parameter (perhaps as an optional parameter).
Or otherwise in a shorter approach, just:
inputArray.Where(x => !x.Equals(double.NaN)).Min();
According to this Q&A, it's complicated: Sorting an array of Doubles with NaN in it
Fortunately, you can hack around it:
if( item is Single || item is Double ) {
Double fitem = (Double)item;
if( fitem == Double.NaN ) continue;
}

convert string[] to int?[]

how to convert a string array to nullable integer array.
Well, the two aspects are:
How do you convert an array from one type to another?
How do you convert a string to a nullable int?
The first is simple - you can use Array.ConvertAll, passing in an Converter delegate. There's the LINQ way as well (x.Select(...).ToArray()), but that's slightly less efficient if you know you've got an array to start with and you want an array out of the other end 1.
The string to int? is probably best done in a method. You can do it in an anonymous function, but in this particular case I'd use a separate method. I rarely like to have to declare variables in an anonymous function.
public static int? TryParseInt32(string text)
{
int value;
return int.TryParse(text, out value) ? value : (int?) null;
}
To answer the comment, yes you need the cast 2.
or without the conditional:
public static int? TryParseInt32(string text)
{
int value;
if (int.TryParse(text, out value))
{
return value;
}
else
{
return null;
}
}
Put them together like this:
string[] x = {"15", "10", "hello", "5" };
int?[] y = Array.ConvertAll(x, TryParseInt32);
1 Array.ConvertAll is more efficient than x.Select().ToArray() because it has more information. It can create the final array immediately - no resizing is ever required. Calling Enumerable.ToArray() (with the result of Select()) doesn't have the same information, so it effectively has to add the results to a List<T> (potentially resizing several times along the way) and then usually resize at the end as well. (It doesn't actually use List<T> but the concepts are the same.) It's definitely worth knowing about the Select/ToArray solution, but I don't think it's actually any more readable than ConvertAll in this case, so you might as well use the more efficient form.
2 The reason you need the cast in the conditional is because the compiler doesn't know what type of "null" you really mean - but it knows it can't convert from null to an int. The type of the conditional expression has to be either the type of the second operand or the type of the third operand, and the other one has to be implicitly convertibly. By forcing the third operand to be of type int? here everything is fine - because you can implicitly convert from int to int?. Note that the compiler doesn't take the way you're using the expression to try to work out the type. Another alternative is to use new int?() instead of null.
By parsing and using Linq:
string[] input = { "1", "3", "x" }
var result = input.Select(
s => { int i; return (int.TryParse(s, out i)) ? i : (int?) null; }).ToArray();
… but I grant that this is a bit cryptic. I wouldn’t use a lambda expression here. For clarity, this should belong to a proper function ParseNullableInt. Then you can call it like this:
var result = input.Select(ParseNullableInt).ToArray();
Edit: I removed the null/empty check because TryParse properly handles those cases by returning false.
string[] stringArray = ...;
int?[] nullableIntArray = Array.ConvertAll(stringArray,
s =>
{
int value;
if (!int.TryParse(s, out value))
return default(int?);
return (int?)value;
});
One option would be this (I'm presuming that you want nulls for the values that were not integer values):
public static class StringArrayExtension
{
public static int?[] ToNullableIntArray(this string[] array)
{
int?[] result = new int?[array.Length];
for (int index = 0; index < array.Length; index++)
{
string sourceValue = array[index];
int destinationValue;
if (int.TryParse(sourceValue, out destinationValue))
{
result[index] = destinationValue;
}
else
{
result[index] = null;
}
}
return result;
}
}
usage:
string[] source = { "hello", "1", "3.1415", "20", "foo" };
int?[] dest = source.ToNullableIntArray();
You need to use int.TryParse:
string[] s = { "123", "a", "45" };
int?[] result = new int?[s.Length];
for (int i = 0; i < s.Length; ++i)
{
int a;
if (int.TryParse(s[i], out a))
result[i] = a;
}
List<int?> ints = new List<int?>();
foreach (string str in strs)
{
int val;
if (int.TryParse(str, out val))
{
ints.Add(val);
}
else { ints.Add(null); }
}
return ints.ToArray();
An integer array can not be null. In fact every element in Integer Array will have "0" by default.
Regarding Conversion
Create an Integer array of the same length or an arraylist
int [] _arr=new int[strArray.length];
for(int i=0;i<strArray.length;i++]
{
if(String.IsNullOrEmpty(strArray[i]))
{
_arr[i]=0;
}
else
{
_arr[i]=int.Parse(strArray[i]);
}
}

Categories

Resources