2012-07-13 13 views
96

Próbuję podzielić listę na serię mniejszych list.Podział listy na mniejsze listy o rozmiarze N

Mój problem: Moja funkcja podziału list nie dzieli ich na listy o prawidłowym rozmiarze. Powinien podzielić je na listy o rozmiarze 30, ale zamiast tego podzieli je na listy o rozmiarze 114?

Jak mogę ustawić funkcję podziału listy na liczbę X list o rozmiarze 30 lub mniejszą?

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

przypadku korzystania z funkcji na liście wielkości 144, to wyjście jest:

Index: 4, wymiary: 120
Index: 3, wymiary: 114
Index: 2 Rozmiar: 114
Index: 1, Powierzchnia: 114
Index: 0 Rozmiar: 114

+1

Jeśli rozwiązanie LINQ jest dopuszczalne, [to pytanie może być pomocne] (http://stackoverflow.com/questions/419019/split -list-into-sublists-with-linq). –

+0

W szczególności odpowiedź Sam Saffron na to poprzednie pytanie. A jeśli nie jest to zadanie szkolne, po prostu użyłbym jego kodu i przestałbym. – jcolebrand

Odpowiedz

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

wersja Generic:

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 'zwrot plonów' – lostmylogin

+0

Więc jeśli mam Listę miliona listów i chcę podzielić na mniejsze listy Długość 30, i z każdej mniejszej listy, którą chcę tylko wziąć (1), to nadal tworzę listy 30 pozycji które wyrzucam 29 przedmiotów. Można to zrobić mądrzej! –

24

jak about:

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

Czy to zajmie dużo pamięci? Za każdym razem, gdy lokalizacje.Skip.ToList się dzieje, zastanawiam się, czy przydzielono więcej pamięci, a elementy unskipped są przywoływane przez nową listę. – Zasz

+0

Tak, nowa lista tworzona jest w każdej pętli. Tak, zużywa pamięć. Ale jeśli masz problemy z pamięcią, nie jest to miejsce do optymalizacji, ponieważ instancje tych list są gotowe do zebrania w następnej pętli. Możesz wymieniać wydajność pamięci, pomijając 'ToList', ale nie będę starał się go zoptymalizować - jest tak trywialny i mało prawdopodobny jest wąskie gardło. Główną zaletą tej implementacji jest jej trywialność, którą łatwo zrozumieć. Jeśli chcesz, możesz użyć zaakceptowanej odpowiedzi, ale nie tworzy ona tych list, ale jest nieco bardziej złożona. – Rafal

+0

'.Ski (n)' iteruje po 'n' elementach za każdym razem, gdy jest wywoływany, podczas gdy może to być ok, ważne jest, aby wziąć pod uwagę kod krytyczny dla wydajności. http://stackoverflow.com/questions/20002975/performance-of-skip- i-similar-functions-like-take – Chakrava

5

Mam metoda rodzajowa, które podejmują wszelkie typy to pływak, i to urządzenie testowane były, nadzieję, że to pomaga:

/// <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

Dzięki. Ciekawe, czy mógłbyś zaktualizować komentarze definicją parametru maxCount? Sieć bezpieczeństwa? –

222

Proponuję użyć tej metody rozszerzania do podzielenia listy źródeł na listy podrzędne o określonym rozmiarze:

/// <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(); 
    } 
} 

Na przykład, jeśli rzucisz listę 18 przedmiotów o 5 sztuk na porcję, otrzymasz listę 4 podlist z następującymi pozycjami w środku: 5-5-5-3.

+7

niesamowite rozwiązanie – MonsterMMORPG

+3

Przed użyciem tego w produkcji upewnij się, że wiesz, jakie są implikacje czasu pracy dla pamięci i wydajności. To, że LINQ może być zwięzły, nie oznacza, że ​​jest to dobry pomysł. – Nick

+3

Zdecydowanie, @Nick Proponuję generalnie myśleć przed zrobieniem czegokolwiek. Chunking z LINQ nie powinien być częstą operacją powtarzaną tysiąc razy. Zwykle trzeba porysować listy w celu przetworzenia elementów partii według partii i/lub równolegle. –

9

rozwiązanie Serj-Tm jest w porządku, także jest to wersja generic jako metodę rozszerzenia dla list (umieścić go w klasie statycznej):

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

znajdę akceptowane odpowiedź (Serj-Tm) najbardziej wytrzymałe, ale chciałbym zaproponować ogólną wersję.

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

Biblioteka MoreLinq mieć metoda zwana 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++; 
} 

Wynik jest

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 te podzielone są na 5 kawałków z 2 elementów.

+0

Dzięki za opowiadanie o [ModeLinq] (https://morelinq.github.io/). To fajna biblioteka. –

2

Chociaż większość rozwiązań może działać, myślę, że nie są one zbyt wydajne. Załóżmy, że chcesz tylko kilka pierwszych elementów z pierwszych kilku porcji. Wtedy nie chciałbyś iterować nad wszystkimi (zillion) przedmiotami w sekwencji.

Poniższe informacje zostaną podane z maksymalną dokładnością dwukrotnie: raz dla Take i Once for the Skip. To nie będzie wyliczyć na jakiekolwiek więcej elementów niż można użyć:

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 
    } 
} 
Powiązane problemy