2010-10-08 3 views
10

Ich versuche, eine Sammlung in mehrere Sammlungen zu teilen, während eine Art, die ich auf der Sammlung haben. Ich habe versucht, die folgende Erweiterungsmethode zu verwenden, aber es bricht sie falsch. Wenn ich mir die Artikel in der Sammlung ansehen sollte, sollte die Reihenfolge im Vergleich zu den zusammengebrochenen Kollektionen gleich sein. Hier ist der Code ich, dass ich mit nicht funktioniert:Split C# Sammlung in gleiche Teile, Pflege Sortierung

public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts) 
     { 
      int i = 0; 
      var splits = from name in list 
         group name by i++ % parts into part 
         select part.AsEnumerable(); 
      return splits; 
     } 
  • int Teile = Anzahl der Unter enumerables
+0

möglich Duplikat [LINQ Partitionsliste in Listen von 8 Mitgliedern.] (Http://stackoverflow.com/questions/3773403/linq-partition-list-into-lists- von-8-Mitgliedern) –

+1

@Kirk Woll: Es ist nicht das gleiche, in der Frage, die Sie geben, Erweiterung Methode dauert maximale Anzahl von Elementen in einem Unter-Enumerable, während hier, wie ich verstehe, haben wir gewünschte Anzahl von Unterzählungen. –

+0

@Andrew, du hast Recht, ich sehe du bist Punkt –

Antwort

10

Ich musste dies verwenden, um eine Liste von Objekten miteinander in Gruppen von 4 zu vergleichen ... es wird die Objekte in der Reihenfolge beibehalten, die das Original besaß. Könnte erweitert werden genau etwas zu tun, andere als ‚Liste‘

/// <summary> 
/// Partition a list of elements into a smaller group of elements 
/// </summary> 
/// <typeparam name="T"></typeparam> 
/// <param name="list"></param> 
/// <param name="totalPartitions"></param> 
/// <returns></returns> 
public static List<T>[] Partition<T>(List<T> list, int totalPartitions) 
{ 
    if (list == null) 
     throw new ArgumentNullException("list"); 

    if (totalPartitions < 1) 
     throw new ArgumentOutOfRangeException("totalPartitions"); 

    List<T>[] partitions = new List<T>[totalPartitions]; 

    int maxSize = (int)Math.Ceiling(list.Count/(double)totalPartitions); 
    int k = 0; 

    for (int i = 0; i < partitions.Length; i++) 
    { 
     partitions[i] = new List<T>(); 
     for (int j = k; j < k + maxSize; j++) 
     { 
      if (j >= list.Count) 
       break; 
      partitions[i].Add(list[j]); 
     } 
     k += maxSize; 
    } 

    return partitions; 
} 
+0

Danke. Hat super funktioniert. –

+0

Gibt es keinen Fehler mit diesem Algorithmus .. Hat jemand versucht, es zu überprüfen? Scheint mir, dass die Zeile int maxSize = (int) Math.Ceiling (list.Count/(double) totalPartitions) zu ungültigen maxsize counts führen kann. Zum Beispiel; eine Liste von 21 Elementen, die in die gewünschten 10 Gruppen aufgeteilt werden sollen; wird tatsächlich nur 7 Gruppen von 3 Elementen mit 3 leeren Gruppen erstellen. Grund dafür ist, dass die Größenberechnung von 2.1 auf 3 aufrunden wird. –

+0

Ja, ich stelle mir vor, dass es so ist. Wenn ich eine Liste des Alphabets habe und ich es auf 100 Partitionen aufteilen möchte, bekomme ich ein Objekt zurück, bei dem die ersten 0-25 mit je 1 Element gefüllt sind und 26-99 leer sind. LINQ ab 3.5 wird wahrscheinlich das Problem zu korrigieren, ich habe nur in ein paar Jahren nicht darauf geschaut. Es hat getan, was ich tun musste, und natürlich, was das OP brauchte. –

0

Wie ich verstehe Sie auf mehrere Teile mit gleicher Größe enumerable brechen wollen und ohne die Reihenfolge deiner Elemente zu brechen. Es sieht so aus, als ob die einzige Option darin besteht, zuerst die Länge Ihres Eingabe-Enumerables zu erhalten, sodass Sie mindestens zwei Iterationen durch das Enumerable benötigen.

+0

@Kirk Woll: Es hängt davon ab, was benötigt wird. Wenn "Teile" die Anzahl der Elemente in einem Aufzählungszeichen ist, ist es einfach zu lösen. Aber der obige Code lässt mich denken, dass "Teile" die Anzahl der gewünschten Unterenumerablen, nicht Elemente in den Enumerablen, sind. –

+0

"Teile" ist die Anzahl der Unterzählungen –

1
double partLength = list.Count()/(double)parts; 

    int i = 0; 
    var splits = from name in list 
       group name by Math.Floor((double)(i++/partLength)) into part 
       select part; 
+0

In Ihrem Beispiel wird "splits" zu IEnumerable > aber es muss ein IEnumerable sein > –

+0

Korrigieren Sie mich, wenn ich falsch liege, aber nicht IGrouping ein IEnumerable ? – kevev22

2

Jon Skeet MoreLINQ Bibliothek könnte den Trick für Sie tun:

https://code.google.com/p/morelinq/source/browse/MoreLinq/Batch.cs

var items = list.Batch(parts); // gives you IEnumerable<IEnumerable<T>> 
var items = list.Batch(parts, seq => seq.ToList()); // gives you IEnumerable<List<T>> 
// etc... 

Ein weiteres Beispiel:

public class Program 
{ 
    static void Main(string[] args) 
    { 
     var list = new List<int>(); 
     for (int i = 1; i < 10000; i++) 
     { 
      list.Add(i); 
     } 

     var batched = list.Batch(681); 

     // will print 15. The 15th element has 465 items... 
     Console.WriteLine(batched.Count().ToString()); 
     Console.WriteLine(batched.ElementAt(14).Count().ToString()); 
     Console.WriteLine(); 
     Console.WriteLine("Press enter to exit."); 
     Console.ReadLine(); 
    } 
} 

Wenn ich den Inhalt der Chargen gescannt, die Bestellung wurde erhalten.

+0

In diesen Beispielen scheint es, dass "Teile" die Menge der Artikel in jeder Sammlung sein sollen. In meinem Beispiel soll "Teile" die Anzahl der zu erstellenden Untersammlungen sein. –

0
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts) 
    { 
     int nGroups = (int)Math.Ceiling(list.Count()/(double)parts); 

     var groups = Enumerable.Range(0, nGroups); 

     return groups.Select(g => list.Skip(g * parts).Take(parts)); 
    } 
+0

Dies ist nicht korrekt.Dadurch werden Gruppen mit einer Größe erzeugt, die den Teilen entspricht. –

0

wird dies tun, wie gewünscht. Es wird auch für ungleiche Gruppierungen sorgen, d.h.27 Elemente in 10 Gruppen ergeben 7 Gruppen von drei und 3 Gruppen von zwei

 public static IEnumerable<IEnumerable<T>> SplitMaintainingOrder<T>(this IEnumerable<T> list, int parts) 
    { 
     if (list.Count() == 0) return Enumerable.Empty<IEnumerable<T>>(); 

     var toreturn = new List<IEnumerable<T>>(); 

     var splitFactor = Decimal.Divide((decimal)list.Count(), parts); 
     int currentIndex = 0; 

     for (var i = 0; i < parts; i++) 
     { 
      var toTake = Convert.ToInt32(
       i == 0 ? Math.Ceiling(splitFactor) : (
        (Decimal.Compare(Decimal.Divide(Convert.ToDecimal(currentIndex), Convert.ToDecimal(i)), splitFactor) > 0) ? 
         Math.Floor(splitFactor) : Math.Ceiling(splitFactor))); 

      toreturn.Add(list.Skip(currentIndex).Take(toTake)); 
      currentIndex += toTake; 
     } 

     return toreturn; 
    } 

zu Demonstrationszwecken

 [TestMethod] 
    public void splitlist() 
    { 
     var list = new decimal[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 }; 

     var splitlists = list.SplitMaintainingOrder(10); 

     int i = 1; 

     foreach (var group in splitlists) 
     { 
      Console.WriteLine("Group {0} elements {1}", i++, String.Join(",", group));     
     } 
    } 

die oben Demo Ausbeute

Test Name: splitlist 
Test Outcome: Passed 
Result StandardOutput: 
Group 1 elements 1,2,3 
Group 2 elements 4,5 
Group 3 elements 6,7,8 
Group 4 elements 9,10,11 
Group 5 elements 12,13 
Group 6 elements 14,15,16 
Group 7 elements 17,18,19 
Group 8 elements 20,21 
Group 9 elements 22,23,24 
Group 10 elements 25,26,27 
4

Eine etwas saubere LINQ Ansatz für diese ziemlich alte Frage:

public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> source, int n) 
{ 
    var count = source.Count(); 

    return source.Select((x, i) => new { value = x, index = i }) 
     .GroupBy(x => x.index/(int)Math.Ceiling(count/(double)n)) 
     .Select(x => x.Select(z => z.value)); 
} 
0

Um eine generische Liste in Split gleich Brocken unter generische Methode verwenden

private IEnumerable<IEnumerable<T>> SplitMaintainingOrder<T>(IEnumerable<T> list, int columnCount) 
 
       { 
 
        var elementsCount = list.Count(); 
 
        int rowCount = elementsCount/columnCount; 
 
        int noOfCells = elementsCount % columnCount; 
 

 
        int finalRowCount = rowCount; 
 
        if (noOfCells > 0) 
 
        { 
 
         finalRowCount++; 
 
        } 
 

 
        var toreturn = new List<IEnumerable<T>>(); 
 
        var pushto = 0; 
 
        for (int j = 0; j < columnCount; j++) 
 
        { 
 
         var start = j; 
 
         int i = 0; 
 
         var end = i; 
 
         for (i = 0; i < finalRowCount; i++) 
 
         { 
 
          if ((i < rowCount) || ((i == rowCount) && (j < noOfCells))) 
 
          { 
 
           start = j; 
 
           end = i; 
 
          } 
 
         } 
 
         toreturn.Add(list.Skip(pushto).Take(end + 1)); 
 
         pushto += end + 1; 
 
        } 
 

 
        return toreturn; 
 

 
       }

List<int> recordNumbers = new List<int>() { 1, 2, 3, 4, 5, 6,7,8,9,10,11}; 
 

 
var splitedItems = SplitMaintainingOrder<int>(recordNumbers , 4);

Output will be: 
 

 
List 1 : 1,2,3 
 
List 2 : 4,5,6 
 
List 3 : 7,8,9 
 
List 4 : 10,11

~ Glücklich Codierung ..