为什么这个可枚举的列表会分开?

时间:2015-10-15 12:32:13

标签: c# linq ienumerable

我尝试从字符串列表中一次取3行,但它没有按预期工作。考虑一下这段代码......

var listOfStrings = new List<string>
{
    "String 1",
    "String 2",
    "String 3",
    "String 4",
    "String 5",
    "String 6"
};

foreach (var x in listOfStrings.Take(3).ToList())
{
    var currRows = x.ToList();

    // currRows should have 3 items
    foreach (var itm in currRows)
    {

    }
}

第一次运行我希望currRows有3个项目(String 1,2和3),第二次我希望有这3个项目(String 4,5和6)。但是当我运行这个currRows时,只包含例如“String 1”,这是逐字符分割的?!

我在这里缺少什么?

3 个答案:

答案 0 :(得分:2)

  1. x.ToList();正在string上调用该方法。 stringIEnumerable<char>,因此currRows的结果是字符列表。您可以完全删除该行,并在第二个循环中使用x
  2. 如果您在ToList()循环中使用的集合上执行此操作,则使用foreach通常是[1]无意义的操作。它将迭代您的集合并构建一个新列表,然后进行迭代。您可以从第一个循环中删除ToList,但效果会更好,但结果会相同。
  3. Take只需n个项目,不会按n个项目进行分组。
  4. 试试这个:

    var listOfStrings = new List<string>
    {
        "String 1",
        "String 2",
        "String 3",
        "String 4",
        "String 5",
        "String 6"
    };
    
    int step = 0;
    const int numTake = 3;
    for(var i = 0; i < listOfStrings.Count / numTake; i++)
    {
        Console.WriteLine("Starting a group!");
        foreach (var x in listOfStrings.Skip(3 * step++).Take(3))
        {
            Console.WriteLine(x);
        }
    }
    

    需要大量改进的空间,但它应该让您了解应该如何处理这项任务。

    1. 你需要这样做的情况 - 但你很可能在开始使用LINQ时不会遇到它们。

答案 1 :(得分:2)

  

但是当我运行这个currRows只包含例如“String 1”和   这是按字符划分的?!

这是因为Enumerable.Take将从IEnumerable<T>获取所请求数量的项目。这会使您的x变量属于string类型,您稍后会调用ToList(),有效地创建List<char>,这不是您想要的。

您可以使用MoreLINQ具有Batch扩展名的方法,该方法可以完全符合您的要求。它返回IEnumerable<IEnumerable<T>>

foreach (var batch in listOfStrings.Batch(3))
{
    // batch is an IEnumerable<T>, and will have 3 items.
    foreach (var item in batch)
    {

    }
}

另一种可能性是自己创建扩展方法。这取自this answer

public static class EnumerableExtensions
{
    public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> items,
                                                       int maxItems)
    {
        return items.Select((item, inx) => new { item, inx })
                    .GroupBy(x => x.inx / maxItems)
                    .Select(g => g.Select(x => x.item));
    }
}

答案 2 :(得分:0)

从您的代码开始:

var listOfStrings = new List<string>
{
    "String 1",
    "String 2",
    "String 3",
    "String 4",
    "String 5",
    "String 6"
};

foreach (var x in listOfStrings.Take(3).ToList())
{
    var currRows = x.ToList();

    // currRows should have 3 items
    foreach (var itm in currRows)
    {

    }
}

通过为循环内容创建一个额外的变量,让我们稍微简化你的代码:

var listOfStrings = new List<string>
{
    "String 1",
    "String 2",
    "String 3",
    "String 4",
    "String 5",
    "String 6"
};

var thingsYouAreLoopingOver = listOfStrings.Take(3).ToList();

foreach (var x in thingsYouAreLoopingOver)
{
    var currRows = x.ToList();

    // currRows should have 3 items
    foreach (var itm in currRows)
    {

    }
}

但肯定这可以再次简化为thingYouAreLoopingOver只是listOfStrings的前三个东西,所以我们有:

var thingsYouAreLoopingOver = new List<string>
{
    "String 1",
    "String 2",
    "String 3"
};

foreach (var x in thingsYouAreLoopingOver)
{
    var currRows = x.ToList();

    // currRows should have 3 items
    foreach (var itm in currRows)
    {

    }
}

现在应该清楚为什么你会得到你所看到的行为。