LINQ从列表中获取x个元素

时间:2013-10-11 15:31:38

标签: c# linq list

我有一个查询,我得到:

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

这很好用。

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

可以使用LINQ完成吗?

5 个答案:

答案 0 :(得分:2)

我认为这样的事情可以奏效,将列表分成IGrouping s。

const int numberOfGroups = 3;

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

答案 1 :(得分:2)

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

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

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

答案 2 :(得分: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);

N.B。

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

答案 3 :(得分:0)

您可以在简单Skip中使用Takefor来完成您想要的内容

   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());

答案 4 :(得分:0)

如果使用Daniel 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 }