这个问题在这里已经有一个答案:

如何获取列表(使用LINQ)并将其分解为每个第8个条目的原始列表的列表?

我想这样的事情将涉及跳过和/或取走,但我仍然是Linq的新手。

编辑:使用C# / .NET 3.5

EDIT2:这个问题的用语与其他“重复”问题不同。尽管问题是相似的,但这个问题的答案是优越的:“接受的”答案都非常扎实(与 yield 陈述)以及乔恩·斯基特(Jon Skeet)使用Morelinq的建议(在“其他”问题中不建议。)有时重复是很好的,因为它们迫使重新检查问题。

有帮助吗?

解决方案

使用以下扩展方法将输入分解为子集

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 作为 方法:

// As IEnumerable<IEnumerable<T>>
var items = list.Batch(8);

或者

// As IEnumerable<List<T>>
var items = list.Batch(8, seq => seq.ToList());

您最好使用库 Morelinq, ,但是,如果您确实必须使用“ Plain 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. 。这就是 Batch() Morelinq的方法do iirc。

根本不是原始的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, ,您将获得一些奇怪的副作用。

采取不会非常有效,因为它不会删除所需的条目。

为什么不使用简单的循环:

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

最简单的解决方案由MEL提供:

public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> items, 
                                                       int partitionSize)
{
    int i = 0;
    return items.GroupBy(x => i++ / partitionSize).ToArray();
}

简洁但较慢。上面的方法将一个可感染的材料分为所需的固定尺寸的块,总数不重要。要将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 手术 这里

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top