2012-12-12 108 views
4

我有值的列表:使用LINQ到轉換列表,以列表的列表

IList<V> values = { V1, V2, V3, V4, V5, V6, V7 }; 

我想列表轉換成列表,列表,其中每個子列表是一個指定的大小。每個子列表的大小可能會有所不同。例如:

IList<IList<V>> values_size2 = { { V1, V2 }, { V3, V4 }, { V5, V6 }, { V7 } }; 
IList<IList<V>> values_size3 = { { V1, V2, V3 }, { V4, V5, V6 }, { V7 } }; 
IList<IList<V>> values_size4 = { { V1, V2, V3, V4 }, { V5, V6, V7 } }; 

我大概可以做到這一點很容易地使用嵌套循環,但不知道是否有一個聰明的辦法做到這一點使用LINQ?

我最初的想法是以某種方式使用Aggregate方法,但沒有想到馬上就會想到。

謝謝。

回答

6

這是一個基於IEnumerable的通用Batch函數。你可以將返回類型從IEnumerable<IEnumerable<T>>更改爲IEnumerable<IList<T>>而沒有其他更改(因爲在我的實現中它已經是一個列表)。要將整個事件更改爲返回列表列表,您需要對結果調用ToList,或者做一個更復雜的重構。在技術上,這是不使用LINQ

注意,它只是創建一個使用相同的樣式和圖案通過LINQ常用的一種新方法。

public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source 
    , int batchSize) 
{ 
    //TODO validate parameters 

    List<T> buffer = new List<T>(); 

    foreach (T item in source) 
    { 
     buffer.Add(item); 

     if (buffer.Count >= batchSize) 
     { 
      yield return buffer; 
      buffer = new List<T>(); 
     } 
    } 
    if (buffer.Count >= 0) 
    { 
     yield return buffer; 
    } 
} 
+1

解決方案是好的,有一件事我會改進的 - 當你創建緩衝區設置緩衝區容量批量大小。 –

5

您可以使用MoreLINQ批延伸(可從Nuget獲得):

IList<IList<V>> values_size2 = values.Batch(2); 
IList<IList<V>> values_size3 = values.Batch(3); 
IList<IList<V>> values_size4 = values.Batch(4); 

你也可以查看來源here

0

給出的答案被別人,我想出了自己的解決方案採用純LINQ如下:

IList<IList<T>> Batch<T>(IList<T> values, int batchSize) 
{ 
    return values.Aggregate(
     new List<IList<T>>(), 
     (state, next) => { 
      IList<T> batch = (state.Count > 0) ? state[state.Count - 1] : null; 
      if ((batch == null) || (batch.Count == batchSize)) 
      { 
       batch = new List<T>(batchSize); 
       state.Add(batch); 
      } 
      batch.Add(next); 
      return state; 
     }); 
} 

這可能不是爲使用一個IEnumerable作爲有效的,但在我的情況我處理的小設置所以它應該沒關係。

我打算選擇Servy的答案作爲「正確」答案,因爲它不依賴於外部庫,並將我帶入我的解決方案。

感謝大家對你的幫助:)

0
public static IEnumerable<IEnumerable<T>> Chunks<T>(this IEnumerable<T> source, int chunkSize) 
{ 
    while (source.Any()) 
    { 
     yield return source.Take(chunkSize); 
     source = source.Skip(chunkSize); 
    } 
}