是否有任何方法可以将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 List<List<T>> GetSplitItemsList<T>(List<T> originalItemsList, short number)
    {
        var listGroup = new List<List<T>>();
        int j = number;
        for (int i = 0; i < originalItemsList.Count; i += number)
        {
            var cList = originalItemsList.Take(j).Skip(i).ToList();
            j += number;
            listGroup.Add(cList);
        }
        return listGroup;
    }

其他回答

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

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;
    }
}

这个怎么样?

var input = new List<string> { "a", "g", "e", "w", "p", "s", "q", "f", "x", "y", "i", "m", "c" };
var k = 3

var res = Enumerable.Range(0, (input.Count - 1) / k + 1)
                    .Select(i => input.GetRange(i * k, Math.Min(k, input.Count - i * k)))
                    .ToList();

据我所知,GetRange()与所获取的项目数量是线性的。所以这应该表现得很好。

完全懒惰的,没有计数或复制的:

public static class EnumerableExtensions
{

  public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, int len)
  {
     if (len == 0)
        throw new ArgumentNullException();

     var enumer = source.GetEnumerator();
     while (enumer.MoveNext())
     {
        yield return Take(enumer.Current, enumer, len);
     }
  }

  private static IEnumerable<T> Take<T>(T head, IEnumerator<T> tail, int len)
  {
     while (true)
     {
        yield return head;
        if (--len == 0)
           break;
        if (tail.MoveNext())
           head = tail.Current;
        else
           break;
     }
  }
}

没有办法在一个解决方案中结合所有理想的特性,如完全懒惰、无复制、完全通用性和安全性。最根本的原因是不能保证在访问块之前输入不发生变化。 假设我们有一个如下签名的函数:

public static IEnumerable<IEnumerable<T>> Chunk<T>(this IEnumerable<T> source, int chunkSize)
{
    // Some implementation
}

那么下面的使用方式就有问题了:

var myList = new List<int>()
{
    1,2,3,4
};
var myChunks = myList.Chunk(2);
myList.RemoveAt(0);
var firstChunk = myChunks.First();    
Console.WriteLine("First chunk:" + String.Join(',', firstChunk));
myList.RemoveAt(0);
var secondChunk = myChunks.Skip(1).First();
Console.WriteLine("Second chunk:" + String.Join(',', secondChunk));
// What outputs do we see for first and second chunk? Probably not what you would expect...

根据具体的实现,代码将失败并产生运行时错误或产生不直观的结果。

所以,至少有一个属性需要减弱。如果你想要一个无懈无击的惰性解决方案,你需要将输入类型限制为不可变类型,即使这样也不能直接覆盖所有用例。但是,如果您可以控制使用,您仍然可以选择最通用的解决方案,只要您确保以一种有效的方式使用它。否则,你可能会放弃懒惰,接受一定数量的复制。

最后,这完全取决于您的用例和需求,哪种解决方案最适合您。

如果列表的类型为system.collections.generic,则可以使用“CopyTo”方法将数组中的元素复制到其他子数组中。您可以指定开始元素和要复制的元素数量。

你也可以对你的原始列表做3个克隆,并在每个列表上使用“RemoveRange”将列表缩小到你想要的大小。

或者只是创建一个helper方法来为您做这件事。