C# 如何将 IEnumerable<String> 拆分为 IEnumerable<string> 组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1349491/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 15:30:12  来源:igfitidea点击:

How can I split an IEnumerable<String> into groups of IEnumerable<string>

c#linq

提问by Kev Hunter

I have an IEnumerable<string> which I would like to split into groups of three so if my input had 6 items i would get a IEnumerable<IEnumerable<string>>returned with two items each of which would contain an IEnumerable<string>which my string contents in it.

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

I am looking for how to do this with Linq rather than a simple for loop

我正在寻找如何使用 Linq 而不是简单的 for 循环来做到这一点

Thanks

谢谢

采纳答案by Mehrdad Afshari

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

Note that this will return an IEnumerable<IGrouping<int,string>>which will be functionally similar to what you want. However, if you strictly need to type it as IEnumerable<IEnumerable<string>>(to pass to a method that expects it in C# 3.0 which doesn't support generics variance,) you should use Enumerable.Cast:

请注意,这将返回一个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>>();

回答by Alex Essilfie

I came up with a different approach. It uses a whileiterator alright but the results are cached in memory like a regular LINQ until needed.
Here's the code.

我想出了一个不同的方法。它使用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;
}

回答by diceguyd30

I know this has already been answered, but if you plan on taking slices of IEnumerables often, then I recommend making a generic extension method like this:

我知道这已经得到了回答,但是如果您打算经常使用 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));
}

Then you can use sequence.Split(3)to get what you want.

然后你可以用它sequence.Split(3)来得到你想要的。

(you can name it something else like 'slice', or 'chunk' if you don't like that 'split' has already been defined for strings. 'Split' is just what I happened to call mine.)

(如果您不喜欢已经为字符串定义了“拆分”,您可以将其命名为“切片”或“块”等其他名称。“拆分”正是我碰巧称之为我的。)

回答by Arne Claassen

Inspired By @dicegiuy30's implementation, I wanted to create a version that only iterates over the source once and doesn't build the whole result set in memory to compensate. Best I've come up with is this:

受到@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;
    }
}

This way I build each chunk on demand. I wish I should avoid the List<T>as well and just stream that that as well, but haven't figured that out yet.

通过这种方式,我可以按需构建每个块。我希望我也应该避免使用它List<T>,并且也只是流式传输它,但还没有弄清楚。

回答by BBeau

using Microsoft.Reactive you can do this pretty simply and you will iterate only one time through the source.

使用 Microsoft.Reactive 您可以非常简单地完成此操作,并且您将仅在源代码中迭代一次。

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

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

回答by Colonel Panic

We can improve @Afshari's solution to do true lazy evaluation. We use a GroupAdjacentBymethod that yields groups of consecutive elements with the same key:

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

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

Because the groups are yielded one-by-one, this solution works efficiently with long or infinite sequences.

由于这些组是一对一产生的,因此该解决方案可以有效地处理长序列或无限序列。

回答by Matthew Watson

This is a late reply to this thread, but here is a method that doesn't use any temporary storage:

这是对该线程的较晚回复,但这里有一种不使用任何临时存储的方法:

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

And some test code:

还有一些测试代码:

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

回答by Ronnie Overby

Mehrdad Afshari's answer is excellent. Here is the an extension method that encapsulates it:

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