2013-02-28 95 views
3

说我有这样的方法:收益回报BRAINFREEZE

IEnumerable<record> GetSomeRecords() 
{ 
    while(...) 
    { 
    yield return aRecord 
    } 
} 

现在,可以说我有一个呼叫者也返回相同类型的枚举,这样的事情

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

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

我想要一个“扁平”IEnumerable,它可以展开一个嵌套的枚举循环。这让我发疯,因为我知道我以前做过这件事,但我似乎无法记住它是什么。有什么提示?

+0

我在这种情况下,我不确定“一个平面的IEnumerable”是什么意思 - 你能展示一个预期的返回类型/数据的例子吗? – 2013-02-28 21:55:09

+0

你**有**使用'yield return'吗? – mattytommo 2013-02-28 21:55:37

+0

如果您只想将数据作为物化列表返回(不进行任何数据转换),则可以使用GetSomeRecords()。ToList()。那是你要的吗? – 2013-02-28 21:56:07

回答

9

这是你所追求的?

IEnumerable<record> ParentGetSomeRecords() 
{ 
    foreach(var item in someItems) 
     foreach(var record in GetSomeRecords()) 
      yield return record; 
} 

如上所述,这只会工作,为孩子们的一个水平,但也是最相当于你的示例代码。

更新

有些人似乎认为要扁平化的层次结构的能力。这里是执行广度优先平坦化(让孩子之前的兄弟姐妹)扩展方法:

从单个项目编辑:

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

这可以用于像这样:

record rec = ...; 
IEnumerable<record> flattened = rec.BreadthFirstFlatten(r => r.ChildRecords); 

这将导致包含recIEnumerable<record>,所有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)); 
} 

这两个扩展方法可以用于像这样:

IEnumerable<records> records = ...; 
IEnumerable<record> flattened = records.BreadthFirstFlatten(r => r.ChildRecords); 

还是从相反的方向:

IEnumerable<record> records = ...; 
IEnumerable<record> flattened = records.BreadthFirstFlatten(r => r.ParentRecords); 

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

我有这些类型的方法,包括预购和后级深度优先遍历整个主机的,如果你想看到他们,我会做一个回购,并将其上传的地方:)

+0

可能是他在找什么。 +1 – 2013-02-28 21:58:46

+1

我认为他的“记录”类有一个“IEnumerable ”,所以它可以是'n'级深。这只适用于第一级 – mattytommo 2013-02-28 22:00:19

+0

@mattytommo我会发布一个更新到我的答案,以防万一他是谁。 – Lukazoid 2013-02-28 22:05:11

2

如何:

IEnumerable<record> ParentGetSomeRecords() 
{ 
    var nestedEnumerable = <whatever the heck gets your nested set>; 
    // SelectMany with an identity flattens 
    // IEnumerable<IEnumerable<T>> to just IEnumerable<T> 
    return nestedEnumerable.SelectMany(rec => rec); 
} 
+0

我认为他有一个永无止境的子项目链。这只适用于一级子项目。 – mattytommo 2013-02-28 21:57:00

+0

@mattytommo是啊,不得不重新阅读,几次...编辑解释评论 – JerKimball 2013-02-28 21:59:50

0

效率不高,但你可以这样做:

List<Record> rcrdList = new List<Record>(); 
foreach (var item in someItems) 
{ 
    rcrdList.AddRange(item.GetSomeRecords()); 
} 
return rcrdList; 
+0

@Downvoter,为什么? – 2013-03-01 07:50:56