83

I find myself foreach-ing over IEnumerables a lot just so that I can return each result. Is there a way to compress something like this

foreach (var subSelector in subSelectors) { foreach (var node in FindSingle(context, subSelector)) yield return node; } 

To remove the inner foreach?

4
  • this has been asked many times and should be merged. search for "yield multiple enumerable" Commented Oct 3, 2010 at 23:35
  • @mafutrct: No results found for "yield multiple enumerable". Can you give an example? Commented Oct 3, 2010 at 23:40
  • 1
    This is what I found (granted, with a different search phrase): stackoverflow.com/questions/2055927/…, stackoverflow.com/questions/1824934/…, stackoverflow.com/questions/1270024/…. However, I did not find the question I was looking for that explained exactly what was asked for. I also recall having asked this myself some time ago... I'll try and look it up in my Q list. Commented Oct 4, 2010 at 9:56
  • 2
    It was probably stackoverflow.com/questions/1043050/…, which is not really related to this question. (I get the feeling I'm acting like a Wikipedian (even a German one). Sorry about that) Commented Oct 4, 2010 at 10:04

7 Answers 7

85

This is a somewhat frequently requested feature that C# does not support. See this Connect item for details:

http://connect.microsoft.com/VisualStudio/feedback/details/256934/yield-return-to-also-yield-collections

The proposed syntax is usually something like:

public static IEnumerable<T> PreorderTraversal<T>(this BinaryTree<T> root) { if (root == null) yield break; yield return root.Item; yield foreach root.Left.PreorderTraversal(); yield foreach root.Right.PreorderTraversal(); } 

If you are interested in playing with a C#-like language that supports this feature, take a look at Cω:

http://research.microsoft.com/en-us/um/cambridge/projects/comega/

You might also want to read this paper on the feature by the implementors of Cω:

http://research.microsoft.com/en-us/projects/specsharp/iterators.pdf

If you're interested in a non-C#-like language that supports this feature, take a look at the "yield!" feature of F#. (I just love that the name of the feature is "yield!")

Even if you are not interested in the theoretical stuff, it sounds like you face this situation as a practical problem. You should also read Wes Dyer's article on techniques for efficiently doing this sort of nested iteration without "yield foreach":

http://blogs.msdn.com/b/wesdyer/archive/2007/03/23/all-about-iterators.aspx

Sign up to request clarification or add additional context in comments.

10 Comments

@Joan: Some of the ideas from C-omega have been integrated in C#, some of them have not. Whether any research or development continues on it, I do not know.
I think just yield each would sound better. Thanks for the answer! This is interesting.
Or "YieldMany", which would fit well with SelectMany
@mpen You may currently use yield as a class name, and each as a variable name, so there could be code containing the words yield each already. Now c# can determine that yield in yield return is a keyword because it is followed by another keyword; so in a similar vein yield foreach might be better.
@StrategyThinker: indeed, that is the standard proposed syntax; see Kiril's article from 2007: kirillosenkov.blogspot.com/2007/10/yield-foreach.html
|
22

No, there isn't, unless you completely replace every yield return with a single return statement using LINQ.

For example:

return someSet .Concat(someOtherSet.SelectMany(s => FindSingle(context, s)); 

1 Comment

Even though, it is not exactly the same, because "return" exits from the function immediately, while "yield return" does not
15

With C# 7.0, local functions are allowed, which enables us to have a fairly neat approach

IEnumerable<T> FlatEnumerable(){ IEnumerable<IEnumerable<T>> NestedEnumerable(){ yield return myEnumerable1; yield return myEnumerable2; } return NestedEnumerable().SelectMany(e => e); } 

https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/local-functions

7 Comments

I wonder about the downvote reasons.. This is short and maintainable.
I think this is really clean. Good suggestion!
I combined it with a Flatten extension for a bit more clean reading. public static IEnumerable<T> Flatten<T>(this IEnumerable<IEnumerable<T>> source) => source.SelectMany(x => x);
The local function is completely unnecessary in this case when you could just simply create an array of those enumerables instead.
an array wouldn't be exactly the same because you're losing the deferred execution that was presumably the MO in the first place.
|
6

Use Enumerable.SelectMany:

return subSelectors.SelectMany(subselector => FindSingle(context, subSelector)); 

This only works if you don't have any other yield return statements in your method.

1 Comment

Yeah... but then you can't yield anything more after that point.
2

You can break your method into two. Given these extension methods:

public static class MultiEnumerableExtensions { public static IEnumerable<T> Pack<T>(this T item) { yield return item; } public static IEnumerable<T> Flatten<T>( this IEnumerable<IEnumerable<T>> multiList) { return multiList.SelectMany(x => x); } } 

And using Eric Lippert's example, it becomes this:

public static class BinaryTreeExtensions { public static IEnumerable<T> PreorderTraversal<T>(this BinaryTree<T> root) { return PreorderTraversalMulti(root).Flatten(); } private static IEnumerable<IEnumerable<T>> PreorderTraversalMulti<T>( this BinaryTree<T> root) { if (root == null) yield break; yield return root.Item.Pack(); // this packs an item into an enumerable yield return root.Left.PreorderTraversal(); yield return root.Right.PreorderTraversal(); } } 

The inner method yields enumerables of T instead of Ts, and the outer method just needs to flatten this result.

2 Comments

Looks like a lot of overhead for a small amount of syntactic sugar. I appreciate the suggestion though, it is interesting.
There's probably a threshold where it'd make sense to use it. Probably if you have more than a few "foreach yields" in your method. I think that Eric's example barely qualifies. Other people might think otherwise.
0

Use the power of Linq!

return subSelectors.SelectMany(s => FindSingle(context, s)); 

Comments

0

Combining @Jordao's answer and the fact that C#7 contains local functions, as @Oskar mentiond I think the following would be the "updated" answer:

 public static IEnumerable<T> PreorderTraversal<T>(this BinaryTree<T> root) { IEnumerable<IEnumerable<T>> PreorderTraversalMulti<T>( this BinaryTree<T> root) { if (root == null) yield break; yield return root.Item.Pack(); // this packs an item into an enumerable yield return root.Left.PreorderTraversal(); yield return root.Right.PreorderTraversal(); } return PreorderTraversalMulti.Concat( PreorderTraversalMulti(root).Flatten()); } 

I used this for a different reason - to get all files up to 3 stages inside and finalized with this function:

public static IEnumerable<FileInfo> EnumerateFiles(DirectoryInfo sourceFolder, string pattern, int steps2Enter, int currStep = 0, int maximumNumFiles = 800) { int total = 0; IEnumerable<FileInfo> NestedFunc() { if (currStep > steps2Enter) yield break; if (sourceFolder == null) yield break; foreach (var file in sourceFolder.GetFiles(pattern, SearchOption.TopDirectoryOnly)) { if (total++ > maximumNumFiles) yield break; yield return file; } } return NestedFunc().Concat(sourceFolder.EnumerateDirectories().SelectMany(s => EnumerateFiles(s, pattern, steps2Enter, currStep + 1, maximumNumFiles))); } 

So yes - I'm still waiting for some syntatic sugar which will enable writing

yield return foreach... 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.