LINQ分区列表为8个成员列表[重复]

    这个问题在这里已有答案:                           使用LINQ将列表拆分为子列表                                      27个答案                                    
已邀请:
使用以下扩展方法将输入分解为子集
public static class IEnumerableExtensions
{
    public static IEnumerable<List<T>> InSetsOf<T>(this IEnumerable<T> source, int max)
    {
        List<T> toReturn = new List<T>(max);
        foreach(var item in source)
        {
                toReturn.Add(item);
                if (toReturn.Count == max)
                {
                        yield return toReturn;
                        toReturn = new List<T>(max);
                }
        }
        if (toReturn.Any())
        {
                yield return toReturn;
        }
    }
}
    
我们在MoreLINQ中使用这样一种方法作为Batch方法:
// As IEnumerable<IEnumerable<T>>
var items = list.Batch(8);
要么
// As IEnumerable<List<T>>
var items = list.Batch(8, seq => seq.ToList());
    
你最好使用像MoreLinq这样的库,但如果你真的不得不使用“普通LINQ”这样做,你可以使用
GroupBy
var sequence = new[] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};

var result = sequence.Select((x, i) => new {Group = i/8, Value = x})
                     .GroupBy(item => item.Group, g => g.Value)
                     .Select(g => g.Where(x => true));

// result is: { {1,2,3,4,5,6,7,8}, {9,10,11,12,13,14,15,16} }
基本上,我们使用为消耗的值提供索引的
Select()
版本,我们将索引除以8以确定每个值所属的组。然后我们通过这个分组键对序列进行分组。最后的
Select
IGrouping<>
降低到
IEnumerable<IEnumerable<T>>
(并且由于
IGrouping
IEnumerable
,因此不是必需的)。 通过在示例中分解常量
8
并将其替换为指定参数,可以很容易地将其转换为可重用的方法。 它不一定是最优雅的解决方案,它不再是一个懒惰的流媒体解决方案......但它确实有效。 您还可以使用迭代器块(
yield return
)编写自己的扩展方法,这可以提供更好的性能并使用比
GroupBy
更少的内存。这就是MoreLinq对IIRC的
Batch()
方法。     
这根本不是Linq设计师最初想到的,但请查看GroupBy的误用情况:
public static IEnumerable<IEnumerable<T>> BatchBy<T>(this IEnumerable<T> items, int batchSize)
{
    var count = 0;
    return items.GroupBy(x => (count++ / batchSize)).ToList();
}

[TestMethod]
public void BatchBy_breaks_a_list_into_chunks()
{
    var values = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    var batches = values.BatchBy(3);
    batches.Count().ShouldEqual(4);
    batches.First().Count().ShouldEqual(3);
    batches.Last().Count().ShouldEqual(1);
}
我认为它赢得了这个问题的“高尔夫”奖。
ToList
非常重要,因为您要确保在尝试对输出执行任何操作之前实际执行了分组。如果你删除
ToList
,你会得到一些奇怪的副作用。     
Take将不会非常有效,因为它不会删除所采用的条目。 为什么不使用简单的循环:
public IEnumerable<IList<T>> Partition<T>(this/* <-- see extension methods*/ IEnumerable<T> src,int num)  
{  
    IEnumerator<T> enu=src.getEnumerator();  
    while(true)  
    {  
        List<T> result=new List<T>(num);  
        for(int i=0;i<num;i++)  
        {  
            if(!enu.MoveNext())  
            {  
                if(i>0)yield return result;  
                yield break;  
            }  
            result.Add(enu.Current);  
        }  
        yield return result;  
    }  
}
    
from b in Enumerable.Range(0,8) select items.Where((x,i) => (i % 8) == b);
    
梅尔给出了最简单的解决方案:
public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> items, 
                                                       int partitionSize)
{
    int i = 0;
    return items.GroupBy(x => i++ / partitionSize).ToArray();
}
简洁但速度慢。上述方法将IEnumerable分成所需固定大小的块,其中块的总数不重要。要将IEnumerable拆分为N个相同大小或接近相等大小的块,您可以:
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> items, 
                                                   int numOfParts)
{
    int i = 0;
    return items.GroupBy(x => i++ % numOfParts);
}
为了加快速度,一种简单的方法可以做到:
public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> items, 
                                                       int partitionSize)
{
    if (partitionSize <= 0)
        throw new ArgumentOutOfRangeException("partitionSize");

    int innerListCounter = 0;
    int numberOfPackets = 0;
    foreach (var item in items)
    {
        innerListCounter++;
        if (innerListCounter == partitionSize)
        {
            yield return items.Skip(numberOfPackets * partitionSize).Take(partitionSize);
            innerListCounter = 0;
            numberOfPackets++;
        }
    }

    if (innerListCounter > 0)
        yield return items.Skip(numberOfPackets * partitionSize);
}
这比现在行星上的任何东西都要快:)这里有一个
Split
操作的等效方法     

要回复问题请先登录注册