2013-06-11 14 views
5

Ich habe eine Sammlung, die 96 Werte hat. Ich möchte Werte für 4 sequentielle Indizes summieren. Wie kann ich das mit Linq machen?Verwenden Sie Linq zu Summe von Index

Beispiel

collection = {100, 101, 200, 150, 103, 105, 100, 104, .........., 20, 40, 60, 80}; 

Summe (100, 101, 200, 150;) dann Summe von (103, 105, 100, 104;) ... dann Summe von (20, 40, 60, 80;) Das bedeutet jetzt meine neue Kollektion 24 Werte haben.

Wie kann ich dies mit Linq tun?

+0

Sollten Ihre Daten tatsächlich gruppiert werden, wenn sie eingefügt werden? Z.B. 'List >' –

Antwort

11

Wir können bis mit dieser Nutzenfunktion Batch Artikel starten auf einer bestimmten Chargengröße basiert:

public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int batchSize) 
{ 
    List<T> buffer = new List<T>(batchSize); 

    foreach (T item in source) 
    { 
     buffer.Add(item); 

     if (buffer.Count >= batchSize) 
     { 
      yield return buffer; 
      buffer = new List<T>(batchSize); 
     } 
    } 
    if (buffer.Count > 0) 
    { 
     yield return buffer; 
    } 
} 

Danach ist es wie so einfach:

var query = data.Batch(4) 
    .Select(batch => batch.Sum()); 
+0

Fehle ich etwas, oder wird das die ersten 4 Items _jeverything_ zurückgeben? – DonBoitnott

+1

@DonBoitnott Du verpasst etwas. Nachschlagen "Iterator Blöcke". – Servy

+0

Sehr schön. Danke, ich habe heute etwas gelernt! – DonBoitnott

4

können Sie Gruppe von index/4 zu erhalten Ihre Summen, wie folgt:

var res = collection 
    .Select((v,i) => new {v, i}) 
    .GroupBy(p => p.i/4) 
    .Select(g => g.Sum(p.v)); 
+0

@Servy Ich wusste, dass mir etwas fehlte! Es war zu süß, um richtig zu sein! Vielen Dank! – dasblinkenlight

3

Sie können einen Gruppenindex aus berechnen Index-Gruppe auf das, und die Summe aus den Werten in jeder Gruppe erhalten:

var sums = collection 
.Select((n, i) => new { Group = i/4, Value = n }) 
.GroupBy(x => x.Group) 
.Select(g => g.Sum(y => y.Value)); 
1

Sie müßten eine neue Erweiterung Methode Partition:

public static IEnumerable<IEnumerable<T>> Partition<T>(
    this IEnumerable<T> source, int partitionSize) 
{ 
    var counter = 0; 
    var result = new T[partitionSize]; 
    foreach(var item in source) 
    { 
     result[counter] = item; 
     ++counter; 
     if(counter >= partitionSize) 
     { 
      yield return result; 
      counter = 0; 
      result = new T[partitionSize]; 
     } 
    } 

    if(counter != 0) 
     yield return result.Take(counter); 
} 

Verwendung wäre:

collection.Partition(4).Select(x => x.Sum()) 

Dies ist ein alternativer Weg zur Batch-Methode, die von Servy gepostet wird.

1

Machen Sie zuerst eine Möglichkeit, Ihre Sets nach Index zu gruppieren. In diesem Fall habe ich mich für eine Ganzzahldivision entschieden, um Elemente 0-3 Gruppe 0, 4-7 Gruppe 1 usw. zu erstellen.

Als Nächstes gruppieren Sie Ihre Elemente in die verschiedenen Mengen, die summiert werden müssen (durch die Gruppierung) Schlüssel).

Wählen Sie schließlich die Summe der Elemente aus, die zu jeder Gruppe gehören.

values.Select((x, i) => new { GroupingKey = i/4, Value = x }) 
     .GroupBy(x => x.GroupingKey) 
     .Select(x => new { Group = x.Key, Sum = x.Sum() }); 
0

Dies tun:

static IEnumerable<int> BatchSum(int batchSize, IEnumerable<int> collection) 
{ 
    var batch = collection.Take(batchSize).ToList(); 
    if (batch.Count == 0) yield break; 

    yield return batch.Sum(); 

    var rest = collection.Skip(batchSize); 
    foreach (var sum in BatchSum(batchSize, rest)) yield return sum; 
} 

Und es zu verwenden:

var collection = new[] { 100, 101, 200, 150, 103, 105, 100, 104, 20, 40, 60, 80, 11, 13 }; 

foreach (var sum in BatchSum(4, collection)) Show(sum); 

Und würde der Ausgang sein:

551 
412 
200 
24 

Wie Sie sehen, Ihre Sammlung Länge muss kein Faktor vonsein.

+0

Im Allgemeinen sollten rekursive Iteratorblöcke möglichst vermieden werden. Herkömmliche Methoden haben bei jedem Aufruf einer Methode einen relativ geringen Overhead, aber Iteratorblöcke (sowie Async-Methoden) haben einen viel höheren Aufwand für die "Installation", die eingerichtet wird, um die Zustandsmaschine zu arbeiten. Eine rekursive Funktion wie diese fügt eine Menge zusätzlichen Overhead hinzu, der all diese zusätzlichen State-Maschinen erstellt und jeden der geschachtelten Werte bis ganz nach oben liefert. Es hat auch keinen wirklichen Vorteil, dies gegenüber einem iterativen Ansatz zu verwenden. Oh, und du iterierst mehrmals "collection". – Servy

+1

Ich stimme nicht zu; rekursive Iteratorblöcke sind kein schlechter Design-Geschmack. F # hat zum Beispiel eine gerade Syntax zum Reduzieren von rekursiven Iteratorblöcken, was hier mittels einer 'foreach' erreicht wird. Und über Kopf, offensichtlich sollte man mit der einen oder anderen Implementierung gehen, die auf ihren/ihren Prioritäten basiert; Performance? schnelle Entwicklung? In dieser Frage wird erwähnt, dass es nur 96 Elemente gibt. Ich bin mir nicht sicher, ob ich die richtige Lösung vorschlage, aber es kann angemessen sein, wenn (z. B.) Leistung hier keine Rolle spielt. Wenn Leistung die Hauptsorge in der Softwareentwicklung wäre, dann würde Ruby nicht existieren. –

+0

@KavehShahbazian: Das Problem bleibt immer noch, dass 'collection' mehrfach durchlaufen wird. In diesem Fall sollte Ihre Methode nicht "IEnumerable ", sondern etwas wie "ICollection " oder ähnliches verwenden. –