是否有任何方法可以将List<SomeObject>分离为SomeObject的几个单独的列表,使用项目索引作为每个分割的分隔符?

让我举个例子:

我有一个List<SomeObject>,我需要一个List<List<SomeObject>>或List<SomeObject>[],这样每个结果列表将包含一组原始列表的3个项目(依次)。

eg.:

原始列表:[a, g, e, w, p, s, q, f, x, y, i, m, c] 结果列表:[a、g e], [w、p, s], [q, f, x]、[y,我,m], [c]

我还需要结果列表的大小是这个函数的参数。


当前回答

我发现这个小片段做得很好。

public static IEnumerable<List<T>> Chunked<T>(this List<T> source, int chunkSize)
{
    var offset = 0;

    while (offset < source.Count)
    {
        yield return source.GetRange(offset, Math.Min(source.Count - offset, chunkSize));
        offset += chunkSize;
    }
}

其他回答

系统。Interactive为此提供了Buffer()。一些快速测试显示性能与Sam的解决方案类似。

如果源集合实现了IList < T >(按索引随机访问),我们可以使用下面的方法。它只在真正访问元素时获取元素,因此这对于延迟求值的集合特别有用。类似于unbounded IEnumerable< T >,但是对于IList< T >。

    public static IEnumerable<IEnumerable<T>> Chunkify<T>(this IList<T> src, int chunkSize)
    {
        if (src == null) throw new ArgumentNullException(nameof(src));
        if (chunkSize < 1) throw new ArgumentOutOfRangeException(nameof(chunkSize), $"must be > 0, got {chunkSize}");

        for(var ci = 0; ci <= src.Count/chunkSize; ci++){
            yield return Window(src, ci*chunkSize, Math.Min((ci+1)*chunkSize, src.Count)-1);
        }
    }

    private static IEnumerable<T> Window<T>(IList<T> src, int startIdx, int endIdx)
    {
        Console.WriteLine($"window {startIdx} - {endIdx}");
        while(startIdx <= endIdx){
            yield return src[startIdx++];
        }
    }

下面这个解是我能想到的最紧凑的解是O(n)

public static IEnumerable<T[]> Chunk<T>(IEnumerable<T> source, int chunksize)
{
    var list = source as IList<T> ?? source.ToList();
    for (int start = 0; start < list.Count; start += chunksize)
    {
        T[] chunk = new T[Math.Min(chunksize, list.Count - start)];
        for (int i = 0; i < chunk.Length; i++)
            chunk[i] = list[start + i];

        yield return chunk;
    }
}

下面是我几个月前写的一个列表拆分例程:

public static List<List<T>> Chunk<T>(
    List<T> theList,
    int chunkSize
)
{
    List<List<T>> result = theList
        .Select((x, i) => new {
            data = x,
            indexgroup = i / chunkSize
        })
        .GroupBy(x => x.indexgroup, x => x.data)
        .Select(g => new List<T>(g))
        .ToList();

    return result;
}

这是一个老问题,但这是我最后得出的结论;它只枚举可枚举对象一次,但是为每个分区创建列表。当调用ToArray()时,它不会像某些实现那样遭受意外行为:

    public static IEnumerable<IEnumerable<T>> Partition<T>(IEnumerable<T> source, int chunkSize)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }

        if (chunkSize < 1)
        {
            throw new ArgumentException("Invalid chunkSize: " + chunkSize);
        }

        using (IEnumerator<T> sourceEnumerator = source.GetEnumerator())
        {
            IList<T> currentChunk = new List<T>();
            while (sourceEnumerator.MoveNext())
            {
                currentChunk.Add(sourceEnumerator.Current);
                if (currentChunk.Count == chunkSize)
                {
                    yield return currentChunk;
                    currentChunk = new List<T>();
                }
            }

            if (currentChunk.Any())
            {
                yield return currentChunk;
            }
        }
    }