产量回归脑风

JMa*_*sch 3 c# linq

说我有这样的方法:

IEnumerable<record> GetSomeRecords()
{
  while(...)
  {
    yield return aRecord
  }
}
Run Code Online (Sandbox Code Playgroud)

现在,假设我有一个调用者,它也返回一个相同类型的枚举,就像这样

IEnumerable<record> ParentGetSomeRecords()
{
  // I want to do this, but for some reason, my brain locks right here
  foreach(item in someItems)
    yield return GetSomeRecords();
}
Run Code Online (Sandbox Code Playgroud)

该代码获取语法错误错误,因为yield return想要一个类型记录,并且我返回一个IEnumerable记录

我想要一个"扁平"的IEnumerable,它可以使嵌套的枚举循环变平.它让我疯了,因为我知道我以前做过这个,但我似乎无法记住它是什么.得到任何提示?

Luk*_*oid 9

这就是你追求的吗?

IEnumerable<record> ParentGetSomeRecords()
{
    foreach(var item in someItems)
        foreach(var record in GetSomeRecords())
            yield return record;
}
Run Code Online (Sandbox Code Playgroud)

如上所述,这仅适用于单个级别的子级,但与您的示例代码最相同.

更新

有些人似乎相信你想要展平层次结构的能力.这是一个扩展方法,它执行广度优先扁平化(在孩子之前得到兄弟姐妹):

来自单个项目:

[Pure]
public static IEnumerable<T> BreadthFirstFlatten<T>(this T source, Func<T, IEnumerable<T>> selector)
{
    Contract.Requires(!ReferenceEquals(source, null));
    Contract.Requires(selector != null);
    Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);

    var pendingChildren = new List<T> {source};

    while (pendingChildren.Any())
    {
        var localPending = pendingChildren.ToList();
        pendingChildren.Clear();
        foreach (var child in localPending)
        {
            yield return child;
            var results = selector(child);
            if (results != null)
                pendingChildren.AddRange(results);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这可以这样使用:

record rec = ...;
IEnumerable<record> flattened = rec.BreadthFirstFlatten(r => r.ChildRecords);
Run Code Online (Sandbox Code Playgroud)

这将导致IEnumerable<record>包含rec,所有recs儿童,所有儿童的孩子等等.

如果您来自一个集合records,请使用以下代码:

[Pure]
private static IEnumerable<T> BreadthFirstFlatten<T, TResult>(IEnumerable<T> source, Func<T, TResult> selector, Action<ICollection<T>, TResult> addMethod)
{
    Contract.Requires(source != null);
    Contract.Requires(selector != null);
    Contract.Requires(addMethod != null);
    Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);

    var pendingChildren = new List<T>(source);

    while (pendingChildren.Any())
    {
        var localPending = pendingChildren.ToList();
        pendingChildren.Clear();
        foreach (var child in localPending)
        {
            yield return child;
            var results = selector(child);
            if (!ReferenceEquals(results, null))
                addMethod(pendingChildren, results);
        }
    }
}

[Pure]
public static IEnumerable<T> BreadthFirstFlatten<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> selector)
{
    Contract.Requires(source != null);
    Contract.Requires(selector != null);
    Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);

    return BreadthFirstFlatten(source, selector, (collection, arg2) => collection.AddRange(arg2));
}

[Pure]
public static IEnumerable<T> BreadthFirstFlatten<T>(this IEnumerable<T> source, Func<T, T> selector)
{
    Contract.Requires(source != null);
    Contract.Requires(selector != null);
    Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);

    return BreadthFirstFlatten(source, selector, (collection, arg2) => collection.Add(arg2));
}
Run Code Online (Sandbox Code Playgroud)

这两种扩展方法可以这样使用:

IEnumerable<records> records = ...;
IEnumerable<record> flattened = records.BreadthFirstFlatten(r => r.ChildRecords);
Run Code Online (Sandbox Code Playgroud)

或者从相反的方向:

IEnumerable<record> records = ...;
IEnumerable<record> flattened = records.BreadthFirstFlatten(r => r.ParentRecords);
Run Code Online (Sandbox Code Playgroud)

所有这些扩展方法都是迭代的,因此不受堆栈大小的限制.

我有很多这些类型的方法,包括预订和后期深度优先遍历,如果你想看到它们,我会制作一个回购并上传它们:)