2012-12-12 64 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); 
    } 
}