取n个元素。若到达末尾则从开头重新开始。

10

我该如何从一个包含m个元素的集合中取出n个元素,以便在用尽所有元素后重新从开头开始取?

List<int> list = new List<int>() {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
List<int> newList = list.Skip(9).Take(2).ToList();
List<int> expected = new List(){10,1};

CollectionAssert.AreEqual(expected, newList);

我该如何获取期望的列表? 我正在寻找CircularTake()函数或类似功能。


2
你需要为IEnumerable<T>编写一个循环扩展方法,然后就可以在其上使用Skip和Take。 - Sir Rufo
1
while (true) foreach (var item in enumerable) yield return item; 当 (true) 循环时,对于可枚举的每个项目,yield 返回该项。 - Sir Rufo
5个回答

10

由于我们可以使用% (它返回整数除法的余数)取模运算符来循环遍历索引范围,因此无需跟踪溢出情况。它将始终返回集合中的有效索引,并在到达末尾时回到0(对于多次超过列表末尾的情况也适用):

List<int> list = new List<int> {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
List<int> newList = new List<int>();

for (int skip = 9, take = 2; take > 0; skip++, take--)
{
    newList.Add(list[skip % list.Count]);
}

Result:

// newList == { 10, 1 }

enter image description here


This could be extracted into an extension method:

public static List<T> SkipTakeWrap<T>(this List<T> source, int skip, int take)
{
    var newList = new List<T>();

    while (take > 0)
    {
        newList.Add(source[skip % source.Count]);
        skip++;
        take--;
    }

    return newList;
}

然后它可以这样调用:

List<int> list = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
List<int> newList = list.SkipTakeWrap(9, 2);

10
使用扩展方法来循环重复可枚举项。
public static IEnumerable<T> Circular<T>( this IEnumerable<T> source )
{
    while (true)
        foreach (var item in source) 
            yield return item;
}

你可以使用你的代码

List<int> list = new List<int>() {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
List<int> newList = list.Circular().Skip(9).Take(2).ToList();

.NET Fiddle示例


旁注:我也曾经采用这种方法,但是后来我想,如果你只想完全枚举原始集合,然后停止——这实际上会更加复杂... https://dotnetfiddle.net/R68mFC - Stewart Ritchie

3

你可能需要像这样做些什么

        var start = 9;
        var amount = 2;
        List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        List<int> listOverflow = list.ToList();

        var overflow = (start + amount) - list.Count;
        if (overflow > 0)
            for (var i = 0; i < overflow; i++)
                listOverflow.AddRange(list.ToList());

        var newList = listOverflow.Skip(start).Take(amount).ToList();

2

我对 CircularTake 扩展的看法。

public static IEnumerable<T> CircularTake<T>(this IReadOnlyList<T> source, int count)
{
    return Enumerable.Range(0, count).Select(i => source[i % source.Count]);
}

1
int overflow = take - (elements.Count - skip);

if(overflow > 0)
{
  results.AddRange(elements.Skip(skip).Take(take - overflow));
  results.AddRange(elements.Take(overflow));
}

如果有可能存在多个循环迭代,例如从3个元素中取10个或更多个,则可以在递归函数中应用此逻辑。

2
如果有3个元素,你要求10个会怎样呢? - itsme86

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接