1

我有一个查询,我得到:

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

这工作正常。

但是现在我想将此列表分解为 x 个列表,例如 3。因此每个列表将包含查询中元素数量的 1/3。

可以使用 LINQ 完成吗?

4

5 回答 5

2

我认为这样的事情可以工作,将列表拆分为IGroupings.

const int numberOfGroups = 3;

var groups = query
    .Select((item, i) => new { item, i })
    .GroupBy(e => e.i % numberOfGroups);
于 2013-10-11T15:35:48.773 回答
2

您可以使用 PLINQ 分区器将结果分解为单独的可枚举项。

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

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

于 2013-10-11T16:20:58.363 回答
1

您可以使用SkipTake以简单for的方式完成您想要的

   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());
于 2013-10-12T01:46:06.847 回答
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]

于 2013-10-11T16:28:11.467 回答
0

如果元素的顺序无关紧要,则使用IGroupingDaniel Imms 建议的 a 可能是最优雅的方式(添加.Select(gr => gr.Select(e => e.item))以获取 a 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 }
于 2013-10-12T02:06:46.173 回答