2012-07-13 153 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 
    } 
}