77

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?

mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • this has been asked many times and should be merged. search for "yield multiple enumerable" – mafu Oct 03 '10 at 23:35
  • @mafutrct: *No results found for "yield multiple enumerable".* Can you give an example? – Mark Byers Oct 03 '10 at 23:40
  • 1
    This is what I found (granted, with a different search phrase): http://stackoverflow.com/questions/2055927/ienumerable-and-recursion-using-yield-return, http://stackoverflow.com/questions/1824934/rewrite-this-foreach-yield-to-a-linq-yield, http://stackoverflow.com/questions/1270024/nested-yield-return-with-ienumerable. 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. – mafu Oct 04 '10 at 09:56
  • 2
    It was probably http://stackoverflow.com/questions/1043050/c-performance-of-nested-yield-in-a-tree, 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) – mafu Oct 04 '10 at 10:04

7 Answers7

81

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

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • I thought Comega is already integrated into C#, so they stopped developing it. Is it still continuing to bring new ideas for future C# versions? – Joan Venge Oct 04 '10 at 17:03
  • 1
    @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. – Eric Lippert Oct 04 '10 at 18:48
  • 21
    I think just `yield each` would sound better. Thanks for the answer! This is interesting. – mpen Oct 06 '10 at 09:08
  • 2
    Or "YieldMany", which would fit well with SelectMany – Tormod Oct 25 '13 at 06:14
  • 5
    @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. – Strategy Thinker Jun 14 '16 at 22:05
  • 2
    @StrategyThinker: indeed, that is the standard proposed syntax; see Kiril's article from 2007: http://kirillosenkov.blogspot.com/2007/10/yield-foreach.html – Eric Lippert Jun 14 '16 at 22:16
  • Wes Dyer's article "All About Iterators" has been moved to https://learn.microsoft.com/en-us/archive/blogs/wesdyer/all-about-iterators – vvnurmi Feb 22 '20 at 11:30
  • @StrategyThinker, but in `yield each x` will be one more word. If `yield` is a class and one of `each` or `x` is a variable, what is the other one? I don't remember any nonkeyword constructions from 3 or more words. – Qwertiy Oct 09 '20 at 20:39
  • Do we need a keyword at all, or could C# infer this based on types? That is, in cases where the method returns IEnumerable(T) we would expect to yield on a T. But if we instead yield an IEnumerable(T) could that be enough to trigger a "yield foreach" without actually needing the "foreach" keyword? I suspect that answer is, "no", because a type might at the same time be both a T and an IEnumerable(T), but I don't want to dismiss this w/0 investigating how it might work. IE, we could always yield the plain T here; if someone wants IEnumerable(T) they could still manually loop through the items. – Joel Coehoorn Apr 18 '22 at 15:29
  • [continued] ... The caveat to that idea is it leaves the programmer open to bugs where they forget to, say, mention a specific property of an item, and instead of a compile-time error get code that does something they don't expect. – Joel Coehoorn Apr 18 '22 at 15:34
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));
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • Even though, it is not exactly the same, because "return" exits from the function immediately, while "yield return" does not – infografnet Dec 02 '20 at 12:11
13

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

5

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.

Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
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.

Community
  • 1
  • 1
Jordão
  • 55,340
  • 13
  • 112
  • 144
  • Looks like a lot of overhead for a small amount of syntactic sugar. I appreciate the suggestion though, it *is* interesting. – mpen Oct 06 '10 at 09:06
  • 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. – Jordão Oct 06 '10 at 13:11
0

Use the power of Linq!

return subSelectors.SelectMany(s => FindSingle(context, s));
Demur Rumed
  • 421
  • 6
  • 17
Jerther
  • 5,558
  • 8
  • 40
  • 59
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...
ephraim
  • 379
  • 1
  • 3
  • 15