簡體   English   中英

LINQ從列表中獲取x元素數量

[英]LINQ get x amount of elements from a list

我有一個查詢,我得到:

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

這很好。

但是,現在我想將此列表分解為x個列表,例如3個。因此,每個列表將包含查詢元素的1/3。

可以使用LINQ完成嗎?

我認為這樣可以將列表分成IGrouping

const int numberOfGroups = 3;

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

您可以使用PLINQ分區程序將結果分成單獨的枚舉。

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

您需要引用System.Collections.Concurrent命名空間。 partitions將是IEnumerable<Item>的列表,其中每個可枚舉都返回查詢的一部分。

您可以創建擴展方法:

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

NB

如果元素的數量不能被組的數量整除,則第一個組將更大。 例如[0,1,2,3,4] --> [0,1] - [2,3] - [4]

您可以使用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());

如果按照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 }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM