2012-07-13 151 views
96

我试图将列表拆分为一系列较小的列表。将列表拆分成N个大小的小列表

我的问题:我的功能拆分列表不会将它们拆分成正确大小的列表。它应该将它们分成大小为30的列表,但是它会将它们分成大小为114的列表?

如何让我的功能将列表分成X个列表大小30或更少

public static List<List<float[]>> splitList(List <float[]> locations, int nSize=30) 
{  
    List<List<float[]>> list = new List<List<float[]>>(); 

    for (int i=(int)(Math.Ceiling((decimal)(locations.Count/nSize))); i>=0; i--) { 
     List <float[]> subLocat = new List <float[]>(locations); 

     if (subLocat.Count >= ((i*nSize)+nSize)) 
      subLocat.RemoveRange(i*nSize, nSize); 
     else subLocat.RemoveRange(i*nSize, subLocat.Count-(i*nSize)); 

     Debug.Log ("Index: "+i.ToString()+", Size: "+subLocat.Count.ToString()); 
     list.Add (subLocat); 
    } 

    return list; 
} 

如果我使用函数尺寸144的列表上,则输出为:

指数:4,尺寸:120
指数:3,尺寸:114
指数:2 ,尺寸:114
指数:1,尺寸:114
指数:0,大小:114

+1

如果LINQ的解决方案是可以接受的,[这个问题可能有一定的帮助(http://stackoverflow.com/questions/419019/split -list - 进入 - 子列表与 - LINQ)。 – 2012-07-13 03:28:53

+0

具体Sam Saffron对上一个问题的回答。除非这是一个学校作业,否则我只会使用他的代码并停止。 – jcolebrand 2012-07-13 03:35:24

回答

110
public static List<List<float[]>> splitList(List<float[]> locations, int nSize=30) 
{   
    var list = new List<List<float[]>>(); 

    for (int i=0; i < locations.Count; i+= nSize) 
    { 
     list.Add(locations.GetRange(i, Math.Min(nSize, locations.Count - i))); 
    } 

    return list; 
} 

通用版本:

public static IEnumerable<List<T>> splitList<T>(List<T> locations, int nSize=30) 
{   
    for (int i=0; i < locations.Count; i+= nSize) 
    { 
     yield return locations.GetRange(i, Math.Min(nSize, locations.Count - i)); 
    } 
} 
+1

Loveee'yield return' – lostmylogin 2017-11-27 09:04:26

+0

因此,如果我有一个List length zillion,并且我想将它分成更小的列表Length 30,并且从每个小列表中我只想采取(1),那么我仍然创建30个项目列表我扔掉了29件物品。这可以做得更聪明! – 2018-03-05 15:54:24

24

怎么样:

while(locations.Any()) 
{  
    list.Add(locations.Take(nSize).ToList()); 
    locations= locations.Skip(nSize).ToList(); 
} 
+0

这会消耗大量内存吗?每次定位.Skip.ToList发生时,我想知道是否分配了更多的内存,未加载的项目是由新列表引用的。 – Zasz 2014-02-12 07:40:06

+0

是在每个循环中创建新列表。是的,它消耗内存。但是,如果你有内存问题,这不是优化的地方,因为该列表的实例已准备好在下一个循环中收集。你可以通过跳过“ToList”来交换内存的性能,但我不打算优化它 - 它是如此微不足道,不太可能是瓶颈。从这个实现中获得的主要收益是它很容易理解的微不足道。如果你愿意,你可以使用接受的答案,它不会创建这些列表,但有点复杂。 – Rafal 2014-02-12 10:59:13

+0

'.Skip(n)'每次调用''n''都会遍历'n''元素,虽然这可能没有问题,但考虑到性能关键代码是很重要的。 http://stackoverflow.com/questions/20002975/performance-of-skip-and-similar-functions-like-take – Chakrava 2016-08-23 16:44:40

5

我会采取任何类型包括浮法一个通用的方法,它已经单元测试,希望它有助于:

/// <summary> 
    /// Breaks the list into groups with each group containing no more than the specified group size 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="values">The values.</param> 
    /// <param name="groupSize">Size of the group.</param> 
    /// <returns></returns> 
    public static List<List<T>> SplitList<T>(IEnumerable<T> values, int groupSize, int? maxCount = null) 
    { 
     List<List<T>> result = new List<List<T>>(); 
     // Quick and special scenario 
     if (values.Count() <= groupSize) 
     { 
      result.Add(values.ToList()); 
     } 
     else 
     { 
      List<T> valueList = values.ToList(); 
      int startIndex = 0; 
      int count = valueList.Count; 
      int elementCount = 0; 

      while (startIndex < count && (!maxCount.HasValue || (maxCount.HasValue && startIndex < maxCount))) 
      { 
       elementCount = (startIndex + groupSize > count) ? count - startIndex : groupSize; 
       result.Add(valueList.GetRange(startIndex, elementCount)); 
       startIndex += elementCount; 
      } 
     } 


     return result; 
    } 
+0

谢谢。不知道你是否可以用maxCount参数定义更新注释?安全网? – 2016-03-21 22:54:56

222

我建议使用此扩展方法将源列表按指定的块大小分块到子列表中:

/// <summary> 
/// Helper methods for the lists. 
/// </summary> 
public static class ListExtensions 
{ 
    public static List<List<T>> ChunkBy<T>(this List<T> source, int chunkSize) 
    { 
     return source 
      .Select((x, i) => new { Index = i, Value = x }) 
      .GroupBy(x => x.Index/chunkSize) 
      .Select(x => x.Select(v => v.Value).ToList()) 
      .ToList(); 
    } 
} 

例如,如果您按每块5个项目查看18个项目的列表,则会给出4个子列表的列表,其中包含以下项目:5-5-5-3。

+7

真棒解决方案 – MonsterMMORPG 2016-06-28 01:41:50

+3

在生产中使用它之前,请确保您了解运行时对内存和性能的影响。仅仅因为LINQ可以简洁,并不意味着它是一个好主意。 – Nick 2017-06-19 21:11:27

+3

当然,我会建议@Nick在做任何事之前总体思考。 使用LINQ进行分块不应该是经常重复数千次的操作。通常情况下,您需要将批处理清单分批处理和/或并行处理。 – 2017-06-23 12:13:08

9

Serj-TM解决方案是好的,也是这是通用版本的扩展方法列表(把它变成一个静态类):

public static List<List<T>> Split<T>(this List<T> items, int sliceSize = 30) 
{ 
    List<List<T>> list = new List<List<T>>(); 
    for (int i = 0; i < items.Count; i += sliceSize) 
     list.Add(items.GetRange(i, Math.Min(sliceSize, items.Count - i))); 
    return list; 
} 
6

我发现接受的答案(Serj-TM)最强大的,但我想推荐一个通用版本。

public static List<List<T>> splitList<T>(List<T> locations, int nSize = 30) 
    { 
     var list = new List<List<T>>(); 

     for (int i = 0; i < locations.Count; i += nSize) 
     { 
      list.Add(locations.GetRange(i, Math.Min(nSize, locations.Count - i))); 
     } 

     return list; 
    } 
1

库MoreLinq有方法称为Batch

List<int> ids = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; // 10 elements 
int counter = 1; 
foreach(var batch in ids.Batch(2)) 
{ 
    foreach(var eachId in batch) 
    { 
     Console.WriteLine("Batch: {0}, Id: {1}", counter, eachId); 
    } 
    counter++; 
} 

结果是

Batch: 1, Id: 1 
Batch: 1, Id: 2 
Batch: 2, Id: 3 
Batch: 2, Id: 4 
Batch: 3, Id: 5 
Batch: 3, Id: 6 
Batch: 4, Id: 7 
Batch: 4, Id: 8 
Batch: 5, Id: 9 
Batch: 5, Id: 0 

ids被分裂成5块与2个元素。

+0

感谢您告诉[ModeLinq](https://morelinq.github.io/)。这是一个不错的图书馆。 – 2017-11-04 07:23:39

2

虽然大多数解决方案可能有效,但我认为它们效率不高。假设你只想要前几个块的前几个项目。然后你不想遍历序列中的所有(数十亿)项。

以下将最多枚举两次:一次为Take,一次为Skip。它不会枚举任何更多的元素比你将使用:

public static IEnumerable<IEnumerable<TSource>> ChunkBy<TSource> 
    (this IEnumerable<TSource> source, int chunkSize) 
{ 
    while (source.Any())      // while there are elements left 
    { // still something to chunk: 
     yield return source.Take(chunkSize); // return a chunk of chunkSize 
     source = source.Skip(chunkSize);  // skip the returned chunk 
    } 
}