【问题标题】:.NET Iterate through an IEnumerable several elements at a time.NET 一次遍历一个 IEnumerable 多个元素
【发布时间】:2013-11-17 21:15:38
【问题描述】:

给定一个集合,我需要一次遍历所有元素三个(或其他数量)。例如:

string[] exampleData = {"John", "Doe", "1.1.1990", "Jane", "Roe", "2.2.1980"}

for(int i = 0; i < exampleData.Length; i += 3) {
    CreateUser(foreName: exampleData[i], surName: exampleData[i+1], dateOfBirth: exampleData[i+2]);
} 

如果 exampleData 是 IEnumerable 而不是数组,我如何有效地重现这一点?

【问题讨论】:

  • 您可以在迭代之前调用 .ToList() 对 List 进行规范化
  • 虽然可以安排这样的迭代,但这将解决错误的问题。正确的做法是将数据从数组中取出并放入适当的 Person 类型的集合中。
  • @Jon 我同意。最好的方法是结构化数据以更好地满足要求。
  • IMO 你的代码比你可以用内置的 LINQ 函数构建的任何代码都要好。

标签: c# .net linq


【解决方案1】:

一种有效的方法是使用扩展方法:

public static IEnumerable<IList<T>> ChunksOf<T>(this IEnumerable<T> sequence, int size)
{
    List<T> chunk = new List<T>(size);

    foreach (T element in sequence)
    {
        chunk.Add(element);
        if (chunk.Count == size)
        {
            yield return chunk;
            chunk = new List<T>(size);
        }
    }
}

你可以这样使用它:

foreach (IList<string> chunk in exampleData.ChunksOf(3))
{
    CreateUser(foreName: chunk[0], surName: chunk[1], dateOfBirth: chunk[2]);
}

请注意,如果sequence.Count() 不是size 的整数倍,则ChunksOf 会丢弃最后一个部分块。如果您想返回部分块,则可以在末尾添加:if (chunk.Count &gt; 0) yield return chunk;

【讨论】:

  • 这似乎是一个非常优雅的答案,谢谢。与上面 cmets 中建议的仅使用 ToList() 相比,是否有任何有意义的开销?
  • @Dan:我不认为有任何重大开销。事实上,如果您从一个非常大的文件(例如,使用File.ReadLines)或其他数据源进行流式传输,那么ChunksOf 将使用比调用ToListGroupBy 更少的内存(如dcastro 的回答),因为ToListGroupBy 都会将整个文件读入内存,而 ChunksOf 一次只在内存中保留一小块。
【解决方案2】:

这个 sn-p 将您的数组分组为 3 个元素的集合,然后为每个集合创建一个用户。

    exampleData.Select((elem, index) => new {elem, index})
        .GroupBy(x => x.index/3)
        .Select(x => CreateUser(
                          x.ElementAt(0).elem,
                          x.ElementAt(1).elem,
                          x.ElementAt(2).elem));

当然,您需要确保数组的长度是 3 的倍数。

【讨论】:

    【解决方案3】:

    忘记 foreach、花哨的 linq 东西、临时子列表等,然后回到根源如何? :-)

    在一个迭代中调用 .GetEnumerator() 和调用 .MoveNext() 三次。这就是枚举的核心。

    之前的答案对我来说似乎过于复杂。这是简单而核心的:-)。

    【讨论】:

      【解决方案4】:

      虽然我更喜欢 Michael 的方法,但如果您需要特定字段的解析(例如 DOB 解析),您可能更喜欢更具体的方法

      public static IEnumerable<User> ExtractUsers(IEnumerable<string> input)
      {
          int i = 0;
          string forename;
          string surname;
          string dateofbirth;
          foreach (string current in input) 
          {
              switch (i) 
              {
              case 0:
                  one = forename;
                  break;
              case 1:
                  two = surname;
                  break;
              case 2:
                  three = dateofbirth;
                  i = -1; //uck, this will get incremented at the bottom
                  yield return CreateUser(forename, surname, dateofbirth);
                  break;
              }
              i++;
          }
      }
      

      【讨论】:

        【解决方案5】:

        Take()Skip() 会做你想做的事,但我建议你避免使用这样的数组。您最好创建一个具有FirstNameLastNameDateOfBirth 属性的Person 类并保留这些属性的数组。

        【讨论】:

          【解决方案6】:

          你可以使用 LINQ

                  string[] exampleData = {"John", "Doe", "1.1.1990", "Jane", "Roe", "2.2.1980"};
          
                  int chunkSize = 3;
                  var seqences = from seqIndex in Enumerable.Range(0, exampleData.Length)
                                 group exampleData[seqIndex] by seqIndex / chunkSize;
                  foreach (var sub in seqences)
                  {
                      CreateUser(sub.ToList()[0], sub.ToList()[1], sub.ToList()[2]);
                  }
          

          编辑:

               var seqences = from m in Enumerable.Range(0, exampleData.Count()) group 
                                     exampleData.ToList()[m] by m / chunkSize;
          

                  int chunkSize = 3;
                  var seqences = Enumerable.Range(0, exampleData.Count())
                                 .GroupBy(m => m/chunkSize, m => exampleData.ToList()[m]);
          

          【讨论】:

          • @Michael Liu Opps 对不起我的错
          猜你喜欢
          • 1970-01-01
          • 2021-10-26
          • 1970-01-01
          • 2013-04-05
          • 2020-04-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-06-13
          相关资源
          最近更新 更多