2011-03-07 8 views

Odpowiedz

15
var groups = allPendingPersons.Select((p, index) => new {p,index}) 
           .GroupBy(a =>a.index/10); 

jeśli chcesz przetworzyć IGrouping<,>. Jeśli szukasz listy> powrót można spróbować

var listOfLists = allPendingPersons.Select((p, index) => new {p, index}) 
    .GroupBy(a => a.index/10) 
    .Select((grp => grp.Select(g => g.p).ToList())) 
    .ToList(); 
+0

nie jest najbardziej wydajne, GroupBy buforuje wszystkie elementy i buduje wyszukiwanie. Tak więc, narzut na pamięć + procesor cpu + nie na parze rozwiązanie –

46

Możesz napisać własną metodę rozszerzenia:

public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> sequence, int size) { 
    List<T> partition = new List<T>(size); 
    foreach(var item in sequence) { 
     partition.Add(item); 
     if (partition.Count == size) { 
      yield return partition; 
      partition = new List<T>(size); 
     } 
    } 
    if (partition.Count > 0) 
     yield return partition; 
} 

I explored this in more depth w moim blogu.

+1

nieznacznie lepiej zrobić: 'partycja = new List (rozmiar);' – nawfal

+0

@nawfal: Masz rację; naprawiony. – SLaks

+0

to jest takie dobre! Użyłem go bezpośrednio na mojej liście przedmiotów, które wymagały działania. foreach (var b w senders.Partition (threshold)) {handleBatch (b); } prosty i elegancki, dzięki! –

2

Nie jest to najbardziej wydajna technika, ale to będzie produkować sekwencję IEnumerable<IEnumerable<Person>>, z każdej wewnętrznej sekwencji zawierającej dziesięć elementów:

var query = allPendingPersons.Select((x, i) => new { Value = x, Group = i/10 }) 
          .GroupBy(x => x.Group, 
             (k, g) => g.Select(x => x.Value)); 

a jeśli wynik naprawdę nie trzeba być list-of-list zamiast prostej sekwencji następnie można utworzyć List<List<Person>> zamiast dodając w kilku ToList połączeń:

var query = allPendingPersons.Select((x, i) => new { Value = x, Group = i/10 }) 
          .GroupBy(x => x.Group, 
             (k, g) => g.Select(x => x.Value).ToList()) 
          .ToList(); 
+1

Możesz nieco uprościć swoją pierwszą wersję, pisząc 'x => x.Value' zamiast' (k, g) => g.Selekt (x => x. Wartość) '. –

0

Spróbuj blok iterator:

public static IEnumerable<List<Person>> AsGroups(this List<Person> persons) 
{ 
    var buf = new List<Person>(10); 
    for (int i = 0; i<persons.Count i++;) 
    { 
     buf.Add(persons[i]); 
     if (i%10 == 0 && buf.Count > 0) 
     { 
      yield return buf; 
      buf = new List<Person>(10); 
     } 
    } 
    yield return buf; 
} 
6

Reactive Extensions for .NET (Rx) ma metodę rozszerzenia, który robi dokładnie to, co chcesz:

var buffered = allPendingPersons.BufferWithCount(10); 

Jeśli chcesz to zrobić przy użyciu LINQ można to zrobić:

var buffered = 
    allPendingPersons 
     .Select((p, i) => new { Group = i/10, Person = p }) 
     .GroupBy(x => x.Group, x => x.Person) 
     .Select(g => g.ToArray()); 
4
+0

+1 dla "Batch" MoreLink, patrz źródło tutaj (https://github.com/morelinq/MoreLINQ/blob/master/MoreLinq/Batch.cs) – lagerone

0

Czy istnieje elegancki sposób w LINQ

The elegant way jest niezbyt wydajnych. Oto bardziej wydajnych sposobów ...

public static List<List<T>> Chunk<T>(
     this List<T> theList, 
     int chunkSize 
    ) 
    { 
     if (!theList.Any()) 
     { 
      return new List<List<T>>(); 
     } 

     List<List<T>> result = new List<List<T>>(); 
     List<T> currentList = new List<T>(); 
     result.Add(currentList); 

     int i = 0; 
     foreach(T item in theList) 
     { 
      if (i >= chunkSize) 
      { 
       i = 0; 
       currentList = new List<T>(); 
       result.Add(currentList); 
      } 
      i += 1; 
      currentList.Add(item); 
     } 
     return result; 
    } 
+1

Możesz być bardziej skuteczny niż to, pisząc iterator. Zobacz moją odpowiedź. – SLaks

Powiązane problemy