【问题标题】:Iterate starting from a preset position and continue iterating after end is reached从预设位置开始迭代,到达end后继续迭代
【发布时间】:2021-11-12 01:07:02
【问题描述】:

我见过一个类似的例子,在最后一个元素重置为第一个元素之后循环继续,这很棒。但是,我无法弄清楚如何添加 PLUS 从 IList 对象的指定索引开始迭代。

假设我们有一个计数为 6 的 IList 对象。(为简化起见,我只使用下面的索引而不是完整的对象。)

[1, 2, 3, 4, 5, 6]

现在假设我们需要迭代这个IList 10 次。

=> Iterate 10 times

基于上述的预期输出应该是:

=> 1, 2, 3, 4, 5, 6, 1, 2, 3, 4

但是,例如,我想从 #4 开始迭代,所以现在预期的输出应该是:

=> 4, 5, 6, 1, 2, 3, 4, 5, 6, 1

(以上所有数字都代表集合中元素的位置,而不是int的实际对象)

关于如何使用 LINQ 和 C# 实现这一点的任何想法?请记住,我们正在使用IList

【问题讨论】:

  • @Sweeper 正确。对不起
  • list.Skip(n).Concat(list.Take(n))?

标签: c# linq for-loop foreach


【解决方案1】:

我建议实现一个扩展方法来一次又一次地循环:

// Let it be general case with IEnumerable<T>
public static class EnumerableExtensions {
  public static IEnumerable<T> Loop<T>(this IEnumerable<T> source) {
    if (null == source)
      throw new ArgumentNullException(name(source));

    List<T> list = new List<T>();

    foreach (var item in source) {
      yield return item;

      list.Add(item);
    }

    // ist.Count > 0 - we can't loop (again and again) over empty list
    for (int i = 0; list.Count > 0; i = (i + 1) % list.Count)
      yield return list[i]; 
  }
}

那你就可以放

 List<int> myList = ...

 // we start looping infinitely - Loop()
 // but take first 10 items only - Take(10) 
 foreach (var item in myList.Loop().Take(10)) {
   ...
 }

 // we start looping infinitely - Loop()
 // skip first 4 items - Skip(4)
 // then take 11 items only - Take(11) 
 foreach (var item in myList.Loop().Skip(4).Take(11)) {
   ...
 }

【讨论】:

  • 一个非常优雅的解决方案,今天将尝试这个。谢谢
【解决方案2】:

你可以编写如下扩展方法:

public static class Extensions
{
    public static IEnumerable<T> Iterate<T>(this IList<T> input, int from, int length)
    {
        for(int i = from; i < from + length; i++)
        {
            yield return input[i % input.Count];
        }
    }
}

如果您的索引大于列表的大小,则模除法确保您再次从 0 开始。

在线演示:https://dotnetfiddle.net/Geqslv

【讨论】:

  • 您忘记了列表为空的边缘情况:)
  • 这是留给读者的练习(以及列表为空时的边缘情况,当 from 小于 0 时,当长度小于零时,当 from + length 更大时比 int.MaxValue, ...)
【解决方案3】:

您可以使用Skip 跳过所需数量的第一个元素。 例如:

public IEnumerable<T> GetRolledItems<T>(IList<T> source, int count, int startIndex)
{
  return Enumerable.Repeat(source, (count + startIndex) / source.Count() + 1)
    .SelectMany(a => a)
    .Skip(startIndex)
    .Take(count);
}

【讨论】:

  • 你好,你能解释一下这是如何工作的吗?
  • Enumerable.Repeat 多次给您source - 为了获得足够的数量,我将所需的计数添加 startIndex 作为移位并将其除以源中的项目数并加 1 以确保它被四舍五入。然后SelectMany 将把Enumerable&lt;IList&lt;T&gt;&gt; 变成Enumerable&lt;T&gt;,最后我们简单地将Skip 第一个StartIndex 元素和Take 需要count 元素。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-26
  • 2016-12-27
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多