2013-02-28 88 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