Why cannot use iterator block with IOrderedEnumerable - c#

I wrote this:
using System;using System.Linq;
static class MyExtensions
{
public static IEnumerable<T> Inspect<T> (this IEnumerable<T> source)
{
Console.WriteLine ("In Inspect");
//return source; //Works, but does nothing
foreach(T item in source){
Console.WriteLine(item);
yield return item;
}
}
}
Then went to test it with this:
var collection = Enumerable.Range(-5, 11)
.Select(x => new { Original = x, Square = x * x })
.Inspect()
.OrderBy(x => x.Square)
//.Inspect()
.ThenBy(x => x.Original)
;
foreach (var element in collection)
{
Console.WriteLine(element);
}
The first use of Inspect() works fine. The second one, commented out, won't compile. The return of OrderBy is IOrderedEnumerable. I'd have thought IOrderedEnumerable is-a IEnumerable but, rolling with the punches, I tried:
public static IOrderedEnumerable<T> Inspect<T> (this IOrderedEnumerable<T> source)
{
Console.WriteLine ("In Inspect (ordered)");
foreach(T item in source){
Console.WriteLine(item);
yield return item;
}
}
But this won't compile either. I get told I cannot have an iterator block because System.Linq.IOrderedEnumberable is not an iterator interface type.
What am I missing? I cannot see why people wouldn't want to iterate over an ordered collection the same way they do with the raw collection.
(Using Mono 2.10.8.1, which is effectively C# 4.0, and MonoDevelop 2.8.6.3)
UPDATE:
As joshgo kindly pointed out, I can take an input parameter of IOrderedEnumerable, it does indeed act as-a IEnumerable. But to iterate I must return IEnumerable, and my original error was caused by ThenBy, which insists on being given IOrderedEnumerable. Very reasonable too. But is there a way to satisfy ThenBy here?
UPDATE2:
After playing with the code in both answers (both of which were very helpful), I finally understood why I can't use yield with an IOrderedEnumerable return: there is no point, because the values have to be fully available in order to do the sort. So instead of a loop with yield in it, I may as well use a loop to print out all items, then just return source once at the end.

I believe an explanation of the error can be found here: Some help understanding "yield"
Quoting Lasse V. Karlsen:
A method using yield return must be declared as returning one of the
following two interfaces: IEnumerable or IEnumerator
The issues seems to be with the yield operator and the return type of your second function, IOrderedEnumerable.
If you change the return type from IOrderedEnumerable to IEnumerable, then the 2nd Inspect() call will no longer be an error. However, the ThenBy() call will now throw an error. If you temporarily comment it out, it'll compile but you do lose access to the ThenBy() method.
var collection = Enumerable.Range(-5, 11)
.Select(x => new { Original = x, Square = x * x })
.Inspect()
.OrderBy(x => x.Square)
.Inspect()
//.ThenBy(x => x.Original)
;
foreach (var element in collection)
{
Console.WriteLine(element);
}
...
public static IEnumerable<T> Inspect<T> (this IOrderedEnumerable<T> source)
{
Console.WriteLine ("In Inspect (ordered)");
foreach(T item in source){
Console.WriteLine(item);
yield return item;
}
}

If you want to apply your extension method after operation, which returns IOrdereEnumerable and continue ordering, then you need to create second overloaded extension:
public static IOrderedEnumerable<T> Inspect<T>(this IOrderedEnumerable<T> source)
{
Console.WriteLine("In Ordered Inspect");
// inspected items will be unordered
Func<T, int> selector = item => {
Console.WriteLine(item);
return 0; };
return source.CreateOrderedEnumerable(selector, null, false);
}
What is interesting here:
You need to return IOrderedEnumerable in order to apply ThenBy or ThenByDescending
IOrderedEnumerable is not created via yield return. In your case it could be achieved by creating it from source
You should create dummy selector, which does not break ordering of items
Output will not contain ordered items, because selector is executed in same order as input sequence.
If you want to see ordered items, you need to execute your OrderedEnumerable. This will force executing of all operators, which present before Inspect:
public static IOrderedEnumerable<T> Inspect<T>(this IOrderedEnumerable<T> source)
{
Console.WriteLine("In Ordered Inspect");
var enumerable = source.CreateOrderedEnumerable(x => 0, null, false);
// each time you apply Inspect all query until this operator will be executed
foreach(var item in enumerable)
Console.WriteLine(item);
return enumerable;
}

Related

Invalid cast from IEnumerable to List

I'm working on a C# script within a Unity3D Project where I'm trying to take a list of strings and get a 2D list of the permutations. Using this answer's GetPermutations() in the following fashion:
List<string> ingredientList = new List<string>(new string[] { "ingredient1", "ingredient2", "ingredient3" });
List<List<string>> permutationLists = GetPermutations(ingredientList, ingredientList.Count);
But it throws an implicit conversion error:
IEnumerable<IEnumerable<string>> to List<List<string>> ... An explicit conversion exists (are you missing a cast)?
So I looked at a few places, such as here and came up with the following modification:
List<List<string>> permutationLists = GetPermutations(ingredientList, ingredientList.Count).Cast<List<string>>().ToList();
But it breaks at runtime, gets handled internally, and allows it to continue without indicating a failure – probably because it's running in Unity3D.
Here is what I see in Unity3D after I stop debugging the script:
InvalidCastException: Cannot cast from source type to destination type.
System.Linq.Enumerable+<CreateCastIterator>c__Iterator0`1[System.Collections.Generic.List`1[System.String]].MoveNext ()
System.Collections.Generic.List`1[System.Collections.Generic.List`1[System.String]].AddEnumerable (IEnumerable`1 enumerable) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/List.cs:128)
System.Collections.Generic.List`1[System.Collections.Generic.List`1[System.String]]..ctor (IEnumerable`1 collection) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/List.cs:65)
System.Linq.Enumerable.ToList[List`1] (IEnumerable`1 source)
Which I interpret as still casting incorrectly, so I also attempted the following approaches and more that I can't remember:
List<List<string>> permutationLists = GetPermutations(ingredientList, ingredientList.Count).Cast<List<List<string>>>();
List<List<string>> permutationLists = GetPermutations(ingredientList.AsEnumerable(), ingredientList.Count);
as well as explicitly casting with parenthesis before the method call like you would in C or Java, still to no avail.
So how should I be casting the results from the GetPermutations() function to get a List<List<string>>? Or alternatively, how could I modify the function to only return List<List<string>> since I don't need it to work for a generic type? I tried to modify the method myself to be the following:
List<List<string>> GetPermutations(List<string> items, int count)
{
int i = 0;
foreach(var item in items)
{
if(count == 1)
yield return new string[] { item };
else
{
foreach(var result in GetPermutations(items.Skip(i + 1), count - 1))
yield return new string[] { item }.Concat(result);
}
++i;
}
}
However, having removed the <T> from the function name it breaks stating that the body cannot be an iterator block. I have no prior experience with C# and I'm rusty with template functions in strongly typed languages, so any explanation/help is appreciated.
I wasn't sure how to look this issue up, so if this is a duplicate just post it here and I'll delete this post immediately.
So how should I be casting the results from the GetPermutations() function to get a List<List<string>>
Best solution: don't. Why do you need to turn the sequence into a list in the first place? Keep it as a sequence of sequences.
If you must though:
GetPermutations(...).Select(s => s.ToList()).ToList()
If you want to modify the original method, just do the same thing:
IEnumerable<List<string>> GetPermutations(List<string> items, int count)
{
int i = 0;
foreach(var item in items)
{
if(count == 1)
yield return new List<T>() { item };
else
{
foreach(var result in GetPermutations(items.Skip(i + 1), count - 1))
yield return (new string[] {item}.Concat(result)).ToList();
}
++i;
}
}
And then do GetPermutations(whatever).ToList() and you have a list of lists. But again, do not do this. Keep everything in sequences if you possibly can.
I want to turn the sequence into a list so that I can sort the elements alphabetically and re-join them as a sorted, single comma-delimited string.
OK, then do that. Let's rewrite your method as an extension method Permute(). And let's make some new one-liner methods:
static public string CommaSeparate(this IEnumerable<string> items) =>
string.Join(",", items);
static public string WithNewLines(this IEnumerable<string> items) =>
string.Join("\n", items);
static public IEnumerable<string> StringSort(this IEnumerable<string> items) =>
items.OrderBy(s => s);
Then we have the following -- I'll annotate the types as we go:
string result =
ingredients // List<string>
.Permute() // IEnumerable<IEnumerable<string>>
.Select(p => p.StringSort()) // IEnumerable<IEnumerable<string>>
.Select(p => p.CommaSeparate())// IEnumerable<string>
.WithNewLines(); // string
And we're done. Look at how clear and straightforward the code is when you make methods that do one thing and do it well. And look at how easy it is when you keep everything in sequences, as it should be.
Your question is related to several aspects of C# and .net types system. I will try to provide simple explanation and will provide links as more formal answers.
So, according to your description it looks like GetPermutations(ingredientList, ingredientList.Count); returns IEnumerable<IEnumerable<string>> but you are trying to assign this result to the variable of another type, in pseudo code:
List<List<string>> = IEnumerable<IEnumerable<string>>;
List<T> implements IEnumerable<T>, so in general it is possible to make this assignment:
IEnumerable<T> = List<T>;
but the problem is that in your case T on the left side differs from the T on the right side.
for IEnumerable<IEnumerable<string>> T is IEnumerable<string>.
for List<List<string>> T is List<string>
To fix your problem we should change the code to have the same T on the left and right sides i.e. convert T to either List<string> or IEnumerable<string>.
You can convert T to the List<string> this way:
IEnumerable<List<string> GetPermutationsList(List<string> items, int count)
{
return GetPermutations(items, count).Select(x=>x.ToList())
}
IEnumerable<List<string>> permutationLists = GetPermutations(ingredientList.AsEnumerable(), ingredientList.Count);
// or
List<List<string>> permutationLists = GetPermutations(ingredientList.AsEnumerable(), ingredientList.Count).ToList();
but in general it is not good idea to use List in all places. Use lists only where you really need it. Important points here:
IEnumerable<T> provides minimum functionality (enumeration only) that should be enougth for your goals.
IList <T> (List implements it ) provides maximum functionality (Add, Remove ,“random” access by index). Do you really need maximum functionality?
Also using ToList() can cause memory shortage problem for big data.
ToList() just forces immediate query evaluation and returns a List<T>
Some useful information: covariance-contr-variance, List, casting

Repeatedly Iterating Through a List

I have a list like such:
List<Thing> foo = new List<Thing>();
foo.PopulateWithFourThings();
And I want to iterate through it over and over, such as 0123012301230123...
Foreach is not what I am looking for, because I do not want everything all at once. I have been messing with Queues, and am wondering if Queues are appropriate here? Or if there is something better than Queues for this.
I am looking for the best code practice in this situation (repeatedly iterate through a list).
So is there a better option than:
if (nextElementIsNeeded)
{
Thing thing = foo[0];
foo.RemoveAt(0);
foo.Add(thing);
return thing;
}
Or the following code using a Queue:
Queue<Thing> foo = new Queue<Thing>();
foo.PopulateWithForThings();
//////////////////////////////////////
if (nextElementIsNeeded)
{
Thing thing = foo.Dequeue();
foo.Enqueue(thing);
return thing;
}
Instead of constantly adding and removing the same Items to/from a collection (no matter which type), simply use a circular index to access the list you have.
int currentElementIndex = 0;
//..
if (nextElementIsNeeded)
{
currentElementIndex = (currentElementIndex + 1) % foo.Count;
thing = foo[currentElementIndex];
}
Not sure of the practicality, but here's an extension method that would do it:
public static IEnumerable<T> SelectForever<T>(this IEnumerable<T> source)
{
while(true)
foreach(T item in source)
yield return item;
}
or to add a projection:
public static IEnumerable<TResult> SelectForever<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
while(true)
foreach(TSource item in source)
yield return selector(item);
}
usage would be:
foreach(Thing item in foo.SelectForever())
{
...hopefully break at some point
}
or
foreach(string s in foo.SelectForever(item => item.ToString()))
{
...hopefully break at some point
}
Note that this is only possible because of deferred execution - calling foo.SelectForever().ToList() would run until it ran out of memory.

What is the best way to trim a list?

I have a List of strings. Its being generated elsewhere but i will generate it below to help describe this simplified example
var list = new List<string>();
list.Add("Joe");
list.Add("");
list.Add("Bill");
list.Add("Bill");
list.Add("");
list.Add("Scott");
list.Add("Joe");
list.Add("");
list.Add("");
list = TrimList(list);
I would like a function that "trims" this list and by trim I want to remove all items at the end of the array that are blank strings (the final two in this case).
NOTE: I still want to keep the blank one that is the second item in the array (or any other one that is just not at the end) so I can't do a .Where(r=> String.isNullOrEmpty(r))
I would just write it without any LINQ, to be honest- after all, you're modifying a collection rather than just querying it:
void TrimList(List<string> list)
{
int lastNonEmpty = list.FindLastIndex(x => !string.IsNullOrEmpty(x));
int firstToRemove = lastNonEmpty + 1;
list.RemoveRange(firstToRemove, list.Count - firstToRemove);
}
If you actually want to create a new list, then the LINQ-based solutions are okay... although potentially somewhat inefficient (as Reverse has to buffer everything).
Take advantage of Reverse and SkipWhile.
list = list.Reverse().SkipWhile(s => String.IsNullOrEmpty(s)).Reverse().ToList();
List<T> (not the interface) has a FindLastIndex method. Therefore you can wrap that in a method:
static IList<string> TrimList(List<string> input) {
return input.Take(input.FindLastIndex(x => !string.IsNullOrEmpty(x)) + 1)
.ToList();
}
This produces a copy, whereas Jon's modifies the list.
The only solution I can think of is to code a loop that starts at the end of the list and searches for an element that is not an empty string. Don't know of any library functions that would help. Once you know the last good element, you know which ones to remove.
Be careful not to modify the collection while you are iterating over it. Tends to break the iterator.
I always like to come up with the most generic solution possible. Why restrict yourself with lists and strings? Let's make an algorithm for generic enumerable!
public static class EnumerableExtensions
{
public static IEnumerable<T> TrimEnd<T>(this IEnumerable<T> enumerable, Predicate<T> predicate)
{
if (predicate == null)
{
throw new ArgumentNullException("predicate");
}
var accumulator = new LinkedList<T>();
foreach (var item in enumerable)
{
if (predicate(item))
{
accumulator.AddLast(item);
}
else
{
foreach (var accumulated in accumulator)
{
yield return accumulated;
}
accumulator.Clear();
yield return item;
}
}
}
}
Use it like this:
var list = new[]
{
"Joe",
"",
"Bill",
"Bill",
"",
"Scott",
"Joe",
"",
""
};
foreach (var item in list.TrimEnd(string.IsNullOrEmpty))
{
Console.WriteLine(item);
}

Check if IEnumerable has ANY rows without enumerating over the entire list

I have the following method which returns an IEnumerable of type T. The implementation of the method is not important, apart from the yield return to lazy load the IEnumerable. This is necessary as the result could have millions of items.
public IEnumerable<T> Parse()
{
foreach(...)
{
yield return parsedObject;
}
}
Problem:
I have the following property which can be used to determine if the IEnumerable will have any items:
public bool HasItems
{
get
{
return Parse().Take(1).SingleOrDefault() != null;
}
}
Is there perhaps a better way to do this?
IEnumerable.Any() will return true if there are any elements in the sequence and false if there are no elements in the sequence. This method will not iterate the entire sequence (only maximum one element) since it will return true if it makes it past the first element and false if it does not.
Similar to Howto: Count the items from a IEnumerable<T> without iterating? an Enumerable is meant to be a lazy, read-forward "list", and like quantum mechanics the act of investigating it alters its state.
See confirmation: https://dotnetfiddle.net/GPMVXH
var sideeffect = 0;
var enumerable = Enumerable.Range(1, 10).Select(i => {
// show how many times it happens
sideeffect++;
return i;
});
// will 'enumerate' one item!
if(enumerable.Any()) Console.WriteLine("There are items in the list; sideeffect={0}", sideeffect);
enumerable.Any() is the cleanest way to check if there are any items in the list. You could try casting to something not lazy, like if(null != (list = enumerable as ICollection<T>) && list.Any()) return true.
Or, your scenario may permit using an Enumerator and making a preliminary check before enumerating:
var e = enumerable.GetEnumerator();
// check first
if(!e.MoveNext()) return;
// do some stuff, then enumerate the list
do {
actOn(e.Current); // do stuff with the current item
} while(e.MoveNext()); // stop when we don't have anything else
The best way to answer this question, and to clear all doubts, is to see what the 'Any' function does.
public static bool Any<TSource>(this IEnumerable<TSource> source) {
if (source == null) throw Error.ArgumentNull("source");
using (IEnumerator<TSource> e = source.GetEnumerator()) {
if (e.MoveNext()) return true;
}
return false;
}
https://github.com/microsoft/referencesource/blob/master/System.Core/System/Linq/Enumerable.cs

Check if one IEnumerable contains all elements of another IEnumerable

What is the fastest way to determine if one IEnumerable contains all the elements of another IEnumerable when comparing a field/property of each element in both collections?
public class Item
{
public string Value;
public Item(string value)
{
Value = value;
}
}
//example usage
Item[] List1 = {new Item("1"),new Item("a")};
Item[] List2 = {new Item("a"),new Item("b"),new Item("c"),new Item("1")};
bool Contains(IEnumerable<Item> list1, IEnumerable<Item>, list2)
{
var list1Values = list1.Select(item => item.Value);
var list2Values = list2.Select(item => item.Value);
return //are ALL of list1Values in list2Values?
}
Contains(List1,List2) // should return true
Contains(List2,List1) // should return false
There is no "fast way" to do this unless you track and maintain some state that determines whether all values in one collection are contained in another. If you only have IEnumerable<T> to work against, I would use Intersect.
var allOfList1IsInList2 = list1.Intersect(list2).Count() == list1.Count();
The performance of this should be very reasonable, since Intersect() will enumerate over each list just once. Also, the second call to Count() will be optimal if the underlying type is an ICollection<T> rather than just an IEnumerable<T>.
You could also use Except to remove from the first list all values that exist in the second list, and then check if all values have been removed:
var allOfList1IsInList2 = !list1.Except(list2).Any();
This method had the advantage of not requiring two calls to Count().
C# 3.5+
Using Enumerable.All<TSource> to determine if all List2 items are contained in List1:
bool hasAll = list2Uris.All(itm2 => list1Uris.Contains(itm2));
This will also work when list1 contains even more than all the items of list2.
Kent's answer is fine and short, but the solution that he provides always requires iteration over the whole first collection. Here is the source code:
public static IEnumerable<TSource> Intersect<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
{
if (first == null)
throw Error.ArgumentNull("first");
if (second == null)
throw Error.ArgumentNull("second");
return Enumerable.IntersectIterator<TSource>(first, second, comparer);
}
private static IEnumerable<TSource> IntersectIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
{
Set<TSource> set = new Set<TSource>(comparer);
foreach (TSource source in second)
set.Add(source);
foreach (TSource source in first)
{
if (set.Remove(source))
yield return source;
}
}
That is not always required. So, here is my solution:
public static bool Contains<T>(this IEnumerable<T> source, IEnumerable<T> subset, IEqualityComparer<T> comparer)
{
var hashSet = new HashSet<T>(subset, comparer);
if (hashSet.Count == 0)
{
return true;
}
foreach (var item in source)
{
hashSet.Remove(item);
if (hashSet.Count == 0)
{
break;
}
}
return hashSet.Count == 0;
}
Actually, you should think about using ISet<T> (HashSet<T>). It contains all required set methods. IsSubsetOf in your case.
The solution marked as the answer would fail in the case of repetitions. If your IEnumerable only contains distinct values then it would pass.
The below answer is for 2 lists with repetitions:
int aCount = a.Distinct().Count();
int bCount = b.Distinct().Count();
return aCount == bCount &&
a.Intersect(b).Count() == aCount;
You should use HashSet instead of Array.
Example:
List1.SetEquals(List2); //returns true if the collections contains exactly same elements no matter the order they appear in the collection
Reference
The only HasSet limitation is that we can't get item by index like List nor get item by Key like Dictionaries. All you can do is enumerate them(for each, while, etc)
the Linq operator SequenceEqual would work also (but is sensitive to the enumerable's items being in the same order)
return list1Uris.SequenceEqual(list2Uris);
Another way is to convert your superset list to a HashSet and use the IsSuperSet method of HashSet.
bool Contains(IEnumerable<Item> list1, IEnumerable<Item>, list2)
{
var list1Values = list1.Select(item => item.Value);
var list2Values = list2.Select(item => item.Value).ToHashSet();
return list2Values.IsSupersetOf(list1Values);
}

Categories

Resources