11 Stimmen

Erstellen Sie Elemente aus 3 Sammlungen mit Linq

Ich habe 3 Collections mit genau der gleichen Anzahl von Elementen.

Ich muss eine neue Collection basierend auf den Werten dieser 3 Collections erstellen.

Beispiel:

List list1;
List list2;
List list3;

List list4;

public class Item
{
   public double Value1{get;set;}
   public double Value2{get;set;}
   public double Value3{get;set;}
}

Ich versuche dies mit Linq zu erreichen.

Ich habe versucht:

    var query = from pt in list1
                from at in list2
                from ct in list3
                select new Item
                           {
                               Value1 = pt,
                               Value2 = at,
                               Value3 = ct
                           };

Aber ich habe eine OutOfMemoryException bekommen, meine 3 Listen sind riesig.

Einige Hilfe?

11voto

Ani Punkte 107342

Weil du über List sprichst (der einen schnellen Indexer hat) und du die Garantie gibst, dass alle drei Listen die gleiche Länge haben, wäre der einfachste Weg:

var items = from index in Enumerable.Range(0, list1.Count)
            select new Item
            {
                Value1 = list1[index],
                Value2 = list2[index],
                Value3 = list3[index]
            }; 

Dieser Ansatz funktioniert offensichtlich nicht gut bei Sammlungen, die keinen schnellen Indexer unterstützen. Ein allgemeinerer Ansatz wäre das Schreiben einer Zip3-Methode, wie sie im F# Collections.Seq-Modul enthalten ist: Seq.zip3<'T1,'T2,'T3>. Andernfalls könntest du zwei Enumerable.Zip Aufrufe zusammenketten, um ein ähnliches Verhalten zu erzeugen (wie in anderen Antworten erwähnt), obwohl das ziemlich hässlich aussieht.

7voto

BrokenGlass Punkte 153950

Sie könnten sie zusammen zippen - dies ist das Zusammenführen von list2 und list3, danach werden die kombinierten Listen mit list1 zusammengeführt:

list4 = list1.Zip(list2.Zip(list3, (b, c) => new { b, c }),
                  (a, b) => new Item { Value1 = a, Value2 = b.b, Value3 = b.c })
             .ToList();

4voto

Tony Tanzillo Punkte 141

Das Konzept des Mappings von Sequenzen oder Listen in die Argumente von Funktionen wurde in der Programmiersprache LISP vor etwas mehr als 50 Jahren entwickelt. In LISP ist es trivial aufgrund seiner untypisierten und listenorientierten Natur. Aber in einer stark typisierten Sprache ist es schwierig, zumindest in Bezug auf die Lösung des allgemeinen Problems des Mappings von n Sequenzen zu einer Funktion, die n Argumente annimmt.

Hier ist ein schwacher Versuch, der den meisten Bedürfnissen gerecht werden sollte:

// Methoden, die wie LISP's (mapcar) funktionieren, wenn sie mit mehr als 1 Listenargument verwendet werden (hier sind 2 bis 4 enthalten, fügen Sie Versionen für mehr Argumente hinzu, wie benötigt).
//
// Die Methoden liefern nur so viele Ergebnisse, wie Elemente in der Argumentsequenz, die die wenigsten Elemente liefert, in Fällen, in denen die Argumentsequenzen nicht alle dieselbe Anzahl von Elementen liefern (was dasselbe Verhalten wie ihr LISP-Gegenstück ist).
//
// Eine interessante Wendung ist, dass wir diese Methoden als Erweiterungsmethoden der aufgerufenen Funktion machen, weil es nicht natürlich erscheint, eine der Sequenzen von Argumenten zum Ziel zu machen.
//
// Dennoch können sie immer noch als nicht-erweiterungsmethoden über den deklarierten Typ aufgerufen werden:
//
// (Nicht getestet):
//
//   string[] fruit = new string[]
//      {"Äpfel", "Orangen", "Birnen", "Bananen"};
//
//   double[] preise = new double[] {1,25, 1,50, 1,00, 0,75};
//
//   int[] mengen = new int[] {12, 8, 24, 5};
//
//
//   Func func =
//     ( menge, name, preis ) => string.Format(
//        "{{0} lbs. von {1} @ ${2:F2} / lb. = ${3:F2}",
//         menge, name, preis, menge * preis );
//
//   var rechnung = func.Map( mengen, fruit, preise );
//
//   foreach( string item in rechnung )
//      Console.WriteLine( item );
//
// Es ist auch erwähnenswert, dass CLR 3.5 die
// "Zip" -Erweiterungsmethode einführt, die das Mapping von zwei
// Sequenzen zu einer Funktion ermöglicht, die zwei Argumente erfordert, aber
// ohne einige wilde Verrenkungen unter Verwendung von Currying und
// mehreren Aufrufen von Zip kann es das allgemeine Problem nicht lösen
// (Mapping von n Sequenzen zu einer Funktion, die so viele Argumente erfordert).

Das Konzept des Mappings von Sequenzen oder Listen in die Argumente von Funktionen wurde vor etwas mehr als 50 Jahren in der Programmiersprache LISP entwickelt. In LISP ist es aufgrund seiner untypisierten und listenorientierten Natur trivial. Aber in einer stark typisierten Sprache ist es schwierig, zumindest wenn es darum geht, das allgemeine Problem des Mappings von n Sequenzen zu einer Funktion, die n Argumente annimmt, zu lösen.

Hier ist ein schwacher Versuch, der den meisten Anforderungen gerecht werden sollte:

// Methoden, die funktionieren wie Lisp's (mapcar) bei Verwendung mit
// mehr als 1 Listenargument (hier sind 2 bis 4 eingeschlossen, fügen Sie
// Versionen für mehr Argumente hinzu, wenn nötig).
//
// Die Methoden liefern nur so viele Ergebnisse wie
// Elemente in der Argumentsequenz, die die geringste Anzahl von
// Elementen liefern, wenn Argumentsequenzen nicht alle dieselbe
// Anzahl von Elementen liefern (was dasselbe Verhalten ist wie
// bei ihrem Lisp-Gegenstück).
//
// Eine interessante Wendung besteht darin, dass wir diese Methoden
// als Erweiterungsmethoden der aufgerufenen Funktion machen,
// weil es nicht natürlich erscheint, eine der Sequenzen von
// Argumenten als Ziel zu machen.
//
// Trotzdem können sie immer noch als Nicht-Erweiterungsmethoden
// über den deklarierenden Typ aufgerufen werden:
//
// (Ungetestet):
//
//   string[] obst = new string[]
//      {"Äpfel", "Orangen", "Birnen", "Bananen"};
//
//   double[] preise = new double[] {1.25, 1.50, 1.00, 0.75};
//
//   int[] mengen = new int[] {12, 8, 24, 5};
//
//
//   Func func =
//     ( menge, name, preis ) => string.Format(
//        "{{0} Pfund {1} @ ${2:F2} / Pfund = ${3:F2}",
//         menge, name, preis, menge * preis );
//
//   var rechnung = func.Map( mengen, obst, preise );
//
//   foreach( string artikel in rechnung )
//      Console.WriteLine( artikel );
//
// Es ist auch erwähnenswert, dass CLR 3.5 die
// "Zip" -Erweiterungsmethode einführt, die das Mapping von zwei
// Sequenzen zu einer Funktion ermöglicht, die zwei Argumente annimmt,
// aber ohne einige wilde Verrenkungen mit Currying und
// mehreren Aufrufen von Zip kann es das allgemeine Problem nicht lösen
// (Mapping von n Sequenzen zu einer Funktion, die so viele
// Argumente annimmt).

3voto

Robert Synoradzki Punkte 1480

Hier ist eine vereinfachte Version, die beliebig viele Sequenzen (als Array) desselben Typs nimmt und sie zusammenführt:

public static IEnumerable Zip(this IEnumerable[] sequences, Func resultSelector)
{
    var enumerators = sequences.Select(s => s.GetEnumerator()).ToArray();
    while(enumerators.All(e => e.MoveNext()))
        yield return resultSelector(enumerators.Select(e => e.Current).ToArray());
}

Vorteile

  • beliebige Anzahl von Sequenzen
  • vier Zeilen Code
  • weitere Überladung für LINQ .Zip() Methode
  • Alle Sequenzen werden gleichzeitig zusammengeführt, anstatt .Zip zu verketten, um jedes Mal eine weitere Sequenz hinzuzufügen

Nachteile

  • Gleicher Typ erforderlich für alle Sequenzen (in vielen Situationen kein Problem)
  • Keine Überprüfung auf gleiche Listenlänge (fügen Sie eine Zeile hinzu, wenn Sie dies benötigen)

Verwendung

Farben zusammenfügen

2voto

Bala R Punkte 104615

Ein wenig schäbig, aber das sollte funktionieren.

  List list4 =
            list1.Select((l1i, i) => new Item {Value1 = l1i, Value2 = list2[i], Value3 = list3[i]}).ToList();

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X