2013-10-11 51 views
0

元素的x量我有一个查询,我得到如下:LINQ得到一个列表

var query = Data.Items 
      .Where(x => criteria.IsMatch(x)) 
      .ToList<Item>(); 

这工作得很好。

但是现在我想把这个列表分解成x个列表,例如3.每个列表将包含1/3查询元素的数量。

可以使用LINQ来完成吗?

+2

看看在(和可能重复的)http://stackoverflow.com/questions/419019/split-list-into-sublists-with-linq – gunr2171

+0

MoreLinq有一个['Batch'](http://code.google.com/ p/morelinq/source/browse/MoreLinq/Batch.cs?r = f85495b139a19bce7df2be98ad88754ba8932a28)扩展方法des准备做到这一点。编辑:哎呀,不要介意。把它倒过来。它定义了每个列表的_size_,而不是列表的数量。抱歉。 –

+0

@ChrisSinclair不完全。 'Batch'将查询分成未知数量的组,所有大小都为n。他希望将查询分成m组,全部都是未知的大小。 – Servy

回答

2

您可以使用PLINQ分区程序将结果分解为单独的枚举类型。

var partitioner = Partitioner.Create<Item>(query); 
var partitions = partitioner.GetPartitions(3); 

您需要引用System.Collections.Concurrent命名空间。 partitions将是IEnumerable<Item>的列表,其中每个枚举返回查询的一部分。

2

我认为像这样的东西可以工作,将列表拆分为IGrouping s。

const int numberOfGroups = 3; 

var groups = query 
    .Select((item, i) => new { item, i }) 
    .GroupBy(e => e.i % numberOfGroups); 
+0

这不起作用。如果你有例如10个元素,你会得到4个组... – digEmAll

+0

啊,应该使用'%',所以group by将在范围'0 ..(numberOfGroups-1)' –

+1

'% '方法是生成非连续元素的子列表:例如,如果您有12个元素,则会获得“{0,3,6,9} - {1,4,7,10} - {2,5,8,11 }' – digEmAll

0

您可以创建一个扩展方法:

public static IList<List<T>> GetChunks<T>(this IList<T> items, int numOfChunks) 
{ 
    if (items.Count < numOfChunks) 
     throw new ArgumentException("The number of elements is lower than the number of chunks"); 
    int div = items.Count/numOfChunks; 
    int rem = items.Count % numOfChunks; 

    var listOfLists = new List<T>[numOfChunks]; 

    for (int i = 0; i < numOfChunks; i++) 
     listOfLists[i] = new List<T>(); 

    int currentGrp = 0; 
    int currRemainder = rem; 
    foreach (var el in items) 
    { 
     int currentElementsInGrp = listOfLists[currentGrp].Count; 
     if (currentElementsInGrp == div && currRemainder > 0) 
     { 
      currRemainder--; 
     } 
     else if (currentElementsInGrp >= div) 
     { 
      currentGrp++; 
     } 
     listOfLists[currentGrp].Add(el); 
    } 
    return listOfLists; 
} 

然后使用它是这样的:

var chunks = query.GetChunks(3); 

注:

如果元素的数量不能被整数的数量整除,则第一组将会变大。例如[0,1,2,3,4] --> [0,1] - [2,3] - [4]

0

您可以使用一个简单的forSkipTake来完成你想要什么

var groupSize = (int)Math.Ceiling(query.Count()/3d); 
    var result = new List<List<Item>>(); 
    for (var j = 0; j < 3; j++) 
     result.Add(query.Skip(j * groupSize).Take(groupSize).ToList()); 
0

如果丹尼尔IMMS的建议元素的顺序无关紧要使用IGrouping可能是最优雅的(加.Select(gr => gr.Select(e => e.item))得到IEnumerable<IEnumerable<T>>)。

如果您想保留订单,您需要知道元素的总数。否则,你不知道什么时候开始下一组。你可以用LINQ来做到这一点,但它需要两个枚举:一个用于计数,另一个用于返回数据(如Esteban Elverdin所建议的)。

如果枚举查询是昂贵的,你可以通过转动查询到列表避免第二枚举,然后使用GetRange方法:

public static IEnumerable<List<T>> SplitList<T>(List<T> list, int numberOfRanges) 
{ 
    int sizeOfRanges = list.Count/numberOfRanges; 
    int remainder = list.Count % numberOfRanges; 

    int startIndex = 0; 

    for (int i = 0; i < numberOfRanges; i++) 
    { 
     int size = sizeOfRanges + (remainder > 0 ? 1 : 0); 
     yield return list.GetRange(startIndex, size); 

     if (remainder > 0) 
     { 
      remainder--; 
     } 

     startIndex += size; 
    } 
} 

static void Main() 
{ 
    List<int> list = Enumerable.Range(0, 10).ToList(); 

    IEnumerable<List<int>> result = SplitList(list, 3); 

    foreach (List<int> values in result) 
    { 
     string s = string.Join(", ", values); 
     Console.WriteLine("{{ {0} }}", s); 
    } 
} 

输出是:

{ 0, 1, 2, 3 } 
{ 4, 5, 6 } 
{ 7, 8, 9 }