如何将IEnumerable <string>拆分为IEnumerable <string> </string> </string>组

时间:2009-08-28 21:34:08

标签: c# linq

我有IEnumerable<string&gt;我想分成三个一组,所以如果我的输入有6个项目,我会得到一个IEnumerable<IEnumerable<string>>返回两个项目,每个项目将包含IEnumerable<string>我的字符串内容。

我正在寻找如何使用Linq而不是简单的for循环

由于

8 个答案:

答案 0 :(得分:29)

var result = sequence.Select((s, i) => new { Value = s, Index = i })
                     .GroupBy(item => item.Index / 3, item => item.Value);

请注意,这将返回IEnumerable<IGrouping<int,string>>,其功能与您想要的相似。但是,如果您确实需要将其键入为IEnumerable<IEnumerable<string>>(要传递给C#3.0中不支持泛型差异的方法),则应使用Enumerable.Cast

var result = sequence.Select((s, i) => new { Value = s, Index = i })
                     .GroupBy(item => item.Index / 3, item => item.Value)
                     .Cast<IEnumerable<string>>();

答案 1 :(得分:28)

这是对此主题的延迟回复,但这是一个不使用任何临时存储的方法:

public static class EnumerableExt
{
    public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> input, int blockSize)
    {
        var enumerator = input.GetEnumerator();

        while (enumerator.MoveNext())
        {
            yield return nextPartition(enumerator, blockSize);
        }
    }

    private static IEnumerable<T> nextPartition<T>(IEnumerator<T> enumerator, int blockSize)
    {
        do
        {
            yield return enumerator.Current;
        }
        while (--blockSize > 0 && enumerator.MoveNext());
    }
}

还有一些测试代码:

class Program
{
    static void Main(string[] args)
    {
        var someNumbers = Enumerable.Range(0, 10000);

        foreach (var block in someNumbers.Partition(100))
        {
            Console.WriteLine("\nStart of block.");

            foreach (int number in block)
            {
                Console.Write(number);
                Console.Write(" ");
            }
        }

        Console.WriteLine("\nDone.");
        Console.ReadLine();
    }
}

答案 2 :(得分:20)

我知道这已经得到了解答,但是如果您打算经常使用IEnumerables片段,那么我建议制作这样的通用扩展方法:

public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, int chunkSize)
{
    return source.Where((x,i) => i % chunkSize == 0).Select((x,i) => source.Skip(i * chunkSize).Take(chunkSize));
}

然后您可以使用sequence.Split(3)来获得您想要的内容。

(你可以把它命名为'slice'或'chunk',如果你不喜欢'split'已经为字符串定义了。'Split'就是我碰巧称之为我的。)< / p>

答案 3 :(得分:15)

受到@ dicegiuy30的实现的启发,我想创建一个只迭代源一次的版本,并且不会在内存中构建整个结果集来补偿。我想出的最好的是:

public static IEnumerable<IEnumerable<T>> Split2<T>(this IEnumerable<T> source, int chunkSize) {
    var chunk = new List<T>(chunkSize);
    foreach(var x in source) {
        chunk.Add(x);
        if(chunk.Count <= chunkSize) {
            continue;
        }
        yield return chunk;
        chunk = new List<T>(chunkSize);
    }
    if(chunk.Any()) {
        yield return chunk;
    }
}

这样我就可以按需构建每个块。我希望我也应该避免使用List<T>,并且只是简单地说明这一点,但还没有想出来。

答案 4 :(得分:2)

使用Microsoft.Reactive,您可以非常简单地执行此操作,并且您将只通过源迭代一次。

IEnumerable<string> source = new List<string>{"1", "2", "3", "4", "5", "6"};

IEnumerable<IEnumerable<string>> splited = source.ToObservable().Buffer(3).ToEnumerable();

答案 5 :(得分:2)

我们可以改进@ Afshari的解决方案来进行真正的懒惰评估。我们使用GroupAdjacentBy方法生成具有相同键的连续元素组:

sequence
.Select((x, i) => new { Value = x, Index = i })
.GroupAdjacentBy(x=>x.Index/3)
.Select(g=>g.Select(x=>x.Value))

由于这些组是逐个产生的,因此该解决方案可以有效地使用长序列或无限序列。

答案 6 :(得分:0)

我想出了一个不同的方法。它使用while迭代器,但结果会像需要的常规LINQ一样缓存在内存中。
这是代码。

public IEnumerable<IEnumerable<T>> Paginate<T>(this IEnumerable<T> source, int pageSize)
{
    List<IEnumerable<T>> pages = new List<IEnumerable<T>>();
    int skipCount = 0;

    while (skipCount * pageSize < source.Count) {
        pages.Add(source.Skip(skipCount * pageSize).Take(pageSize));
        skipCount += 1;
    }

    return pages;
}

答案 7 :(得分:0)

Mehrdad Afshari的回答非常好。这是一个封装它的扩展方法:

using System.Collections.Generic;
using System.Linq;

public static class EnumerableExtensions
{
    public static IEnumerable<IEnumerable<T>> GroupsOf<T>(this IEnumerable<T> enumerable, int size)
    {
        return enumerable.Select((v, i) => new {v, i}).GroupBy(x => x.i/size, x => x.v);
    }
}