Laden...

Linq Order: DataTable mit mehreren Spalten sortieren

Erstellt von BeZi vor 8 Jahren Letzter Beitrag vor 8 Jahren 3.764 Views
B
BeZi Themenstarter:in
153 Beiträge seit 2007
vor 8 Jahren
Linq Order: DataTable mit mehreren Spalten sortieren

Tag die Damen und Herren,

ich hoffe mir kann jemand helfen der sich gut mit LINQ auskennt....

Ich habe eine Datatable und diese möchte ich sortieren anhand von mehreren Spalten UND einer Datumspalte. Das wäre jetzt nicht das Problem, aber die Datumspalte muss bei jeder anderen Spalte mit abgefragt werden.

Eine Tabelle hat mehrere Spalten und eine Spalte mit Datum...

Jetzt müssen immer Gruppierungen erstellt werden

diese Gruppierungen müssen dann nach Datum sortiert werden.

SP1 - komplett A kommt vor B weil A mit kleinstem Datum beginnt... also ALLE A nacheinander, egal ob ein B dann zwischen den A liegt...

SP2 - komplett C vor D weil kleinstes Datum C kleiner als kleinstes Daum von D , egal ob ein D dann zwischen den C liegt...

SP3 ...... usw....

SP1  SP2    SP3     SP4         DAT1
A      C       ..     ..     1.1.2000
A      C       ..     ..     1.1.2004
A      C       ..     ..     1.1.2008
A      D       ..     ..     1.1.2006

B      C      ..      ..    1.1.2002
B      D      ..      ..    1.1.2006
B      D      ..      ..    1.1.2008

Ich hoffe das ist verständlich....

ich bekomme das nicht mit dem Datum hin, also das binden an alle Spalten mit dem Datum


            DataTable tmpSortTbl = tmpTbl.AsEnumerable()

                    .GroupBy(M_A => new { MA = M_A.Field<string>("SP1")})
                    .OrderBy(f => f.Key.MA)
                    .SelectMany(a2 => a2
                        .GroupBy(S_T => new { ST = S_T.Field<string>("SP2") })
                        .OrderBy(f => f.Key.ST)
                        .SelectMany(a3 => a3
                            .GroupBy(T_C => new { TC = T_C.Field<string>("SP3")})
                            .OrderBy(f => f.Key.TC)
                            .SelectMany(a4 => a4
                                        .GroupBy(A_R => new { AR = A_R.Field<string>("SP4") })
                                        .OrderBy(f => f.Key.AR)
                                        .SelectMany(a6 => a6
                                                .GroupBy(E_S => new { ES = E_S.Field<string>("SP5") })
                                                .OrderBy(f => f.Key.ES)
                                                .SelectMany(a8 => a8                                                        
                                         )
                             )
                       )
                 )
            )

               .CopyToDataTable();

            return tmpSortTbl;

Fettes merci

P
1.090 Beiträge seit 2011
vor 8 Jahren

Hi ich sehe jetzt keine Möglichkeit wie du die Abfrage, direkt mit Linq umsetzen kannst. (Vieleicht hast du ja Glück und jemand anderes hat eine Idee).

Mein Ansatz wäre, die Listen in passende Teillisten zu zerlegen. Also erst mal Teilliste die nur als SP1 = A enthalt, daraus wider Teillisten bilden, die nur SP2 = C enthalten u.s.w bis SP4. Und dann würde ich hingehen und die Liste Rückwerts wieder zusammen zu setzen. Also SP4 passend nach Datum sortiert und dann SP2 usw.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

B
BeZi Themenstarter:in
153 Beiträge seit 2007
vor 8 Jahren

Hmmm die frage die sich mir jetzt stellt, versteht man mich nicht oder ist das in LINQ nicht möglich ?

74 Beiträge seit 2014
vor 8 Jahren

var values = new []
{
	new { SP1 = "A", SP2 = "C", Date = new DateTime(2000, 1, 1) },
	new { SP1 = "A", SP2 = "C", Date = new DateTime(2004, 1, 1) },
	new { SP1 = "A", SP2 = "C", Date = new DateTime(2008, 1, 1) },
	new { SP1 = "A", SP2 = "D", Date = new DateTime(2006, 1, 1) },
	new { SP1 = "B", SP2 = "C", Date = new DateTime(2002, 1, 1) },
	new { SP1 = "B", SP2 = "D", Date = new DateTime(2006, 1, 1) },
	new { SP1 = "B", SP2 = "D", Date = new DateTime(2008, 1, 1) }
};

values
	.OrderBy(item => item.SP1)
	.ThenBy(item => item.SP2)
	.ThenBy(item => item.Date)
	.ToList()
	.ForEach(item => Console.WriteLine("{0}  {1}  {2}", item.SP1, item.SP2, item.Date.ToString("dd.MM.yyyy")));

Ist die ThenBy-Methode das was du suchst?

B
BeZi Themenstarter:in
153 Beiträge seit 2007
vor 8 Jahren

Danke erstmal für die Antwort,

Ich glaube aber nicht.

Jeder Spalte (außer der Datumspalte) muss mit der Datumsspalte sortiert werden.

Also erst SP1 nach Datum - innerhalb davon SP2 mit Datum und innerhalb davon SP3 mit Datum....

Es kann sein das C aus SP2 ind SP1 A und B vorkommt....

5.657 Beiträge seit 2006
vor 8 Jahren

Hi BeZi,

Also erst SP1 nach Datum - innerhalb davon SP2 mit Datum und innerhalb davon SP3 mit Datum....

Ist das nicht genau das, was Landos Lösung macht? Wenn nicht, mußt du ein bißchen konkreter werden, was noch nicht richtig funktioniert.

Christian

Weeks of programming can save you hours of planning

P
1.090 Beiträge seit 2011
vor 8 Jahren

Wenn in SP1 das kleinste Datum von B kleiner ist als alle von A, dann sollen erst B's kommen und dann erst alle A's. Und das gleiche bei den Anderen Spalten.

(Hat BeZi aber auch so geschrieben, das Beispiel ist nur missverständlich. Das Ergebiss liefert Landos Abfrage.)

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

B
BeZi Themenstarter:in
153 Beiträge seit 2007
vor 8 Jahren

Ich habe das Beispiel nun getestet und muss leider sagen das es so nicht funktioniert.

Von jeder Spalte muss es einen Rückschluss auf das kleinste Datum geben.

Bei mir wird nur jede Spalte alphabetisch sortiert ...

Ich weiss es lässt sich schwierig ausdrücken....

B
BeZi Themenstarter:in
153 Beiträge seit 2007
vor 8 Jahren

Das witzige ist das die eingabetabelle von Lando eigentlich schon das Ergebnis wiederspiegelt so wie es hinten rauspurzeln soll... Egal wie ich sie anlege



SP1  SP2    SP3     SP4         DAT1
A      C       ..     ..     1.1.2000               <-----
A      C       ..     ..     1.1.2004
A      C       ..     ..     1.1.2008
A      D       ..     ..     1.1.2006

B      C      ..      ..    1.1.2002
B      D      ..      ..    1.1.2006
B      D      ..      ..    1.1.2008


ersten Eintrag geändert:
auf 2003 dann erwarte ich folgende Ausgabe



SP1  SP2    SP3     SP4         DAT1

B      C      ..      ..    1.1.2002
B      D      ..      ..    1.1.2006
B      D      ..      ..    1.1.2008

A      C       ..     ..     1.1.2003              <-----
A      C       ..     ..     1.1.2004
A      C       ..     ..     1.1.2008
A      D       ..     ..     1.1.2006


1.029 Beiträge seit 2010
vor 8 Jahren

Hi,

dann sollte Folgendes machen was du erwartest oder? (Habe 2016 noch ergänzt, da ich die Sortierung nach Datum allgemein noch mittesten wollte)

var values = new[]
            {
                new { SP1 = "A", SP2 = "C", Date = new DateTime(2003, 1, 1) },
                new { SP1 = "A", SP2 = "C", Date = new DateTime(2016, 1, 1) },
                new { SP1 = "A", SP2 = "C", Date = new DateTime(2004, 1, 1) },
                new { SP1 = "A", SP2 = "C", Date = new DateTime(2008, 1, 1) },
                new { SP1 = "A", SP2 = "D", Date = new DateTime(2006, 1, 1) },
                new { SP1 = "B", SP2 = "C", Date = new DateTime(2002, 1, 1) },
                new { SP1 = "B", SP2 = "D", Date = new DateTime(2006, 1, 1) },
                new { SP1 = "B", SP2 = "D", Date = new DateTime(2008, 1, 1) }
            };

            values
                .OrderBy(v => v.Date)
                .ThenBy(v => v.SP1)
                .ThenBy(v => v.SP2)
                .GroupBy(v => v.SP1)
                .OrderBy(g => g.Select(e => e.Date).Min())
                .ToList()
                .ForEach
                    (
                        grouping =>
                            grouping
                                .ToList()
                                .ForEach(item => Console.WriteLine("{0}  {1}  {2}", item.SP1, item.SP2, item.Date.ToString("dd.MM.yyyy")))
                    );

LG

B
BeZi Themenstarter:in
153 Beiträge seit 2007
vor 8 Jahren

Erstmal merci Taipi88,

ich habe eine Datatable und diese muss dann später nach der Sortierung wieder in eine Datatable, also am schluss ".CopyToDataTable();"

ich bekomme das so nicht hin mit foreach, da Foreach keinen Rückgabewert hat.
Wenn ich es durchlaufen lasse ist es auch so das er erst nach datum sortiert und dann nach SP1 ich will aber alle Werte innerhalb SP1 zusammen gesetzt und dann sortiert nach den Blöcken in SP1

1.029 Beiträge seit 2010
vor 8 Jahren

Hi,

ja - er sortiert in der Tat zuerst nach Datum - allerdings nur um das später nicht für jeden einzelne Gruppe tun zu müssen - denke so wie folgt sollte es eher deinem Sinn entsprechen:

  1. Die Erweiterungsmethode, die wieder dein Object aus den Gruppen rausholt
public static class Extensions
    {
        public static IEnumerable<T> Unwind<T>(this IEnumerable<IEnumerable<T>> list)
        {
            foreach (var l in list)
                foreach (var e in l)
                    yield return e;
        }
    }
  1. Sortierung
        static void T2()
        {
            var values = new[]
            {
                new { SP1 = "A", SP2 = "C", Date = new DateTime(2003, 1, 1) },
                new { SP1 = "A", SP2 = "C", Date = new DateTime(2016, 1, 1) },
                new { SP1 = "A", SP2 = "C", Date = new DateTime(2004, 1, 1) },
                new { SP1 = "A", SP2 = "C", Date = new DateTime(2008, 1, 1) },
                new { SP1 = "A", SP2 = "D", Date = new DateTime(2006, 1, 1) },
                new { SP1 = "B", SP2 = "C", Date = new DateTime(2002, 1, 1) },
                new { SP1 = "B", SP2 = "D", Date = new DateTime(2006, 1, 1) },
                new { SP1 = "B", SP2 = "D", Date = new DateTime(2008, 1, 1) }
            };

            values
                .OrderBy(v => v.Date)
                .GroupBy(v => v.SP1)
                .Select(g => g
                        .GroupBy(v => v.SP2)
                        .OrderBy(g2 => g2.Select(e2 => e2.Date).Min())
                        .Unwind())
                .OrderBy(g => g.Select(e => e.Date).Min())
                .Unwind()
                .ToList()
                .ForEach(item => Console.WriteLine("{0}  {1}  {2}", item.SP1, item.SP2, item.Date.ToString("dd.MM.yyyy")));
        }
  1. Vorgehensweise
  2. Zuerst wird nach Datum sortiert, womit man sich das später in den Gruppen spart
  3. Es wird gruppiert nach SP1
  4. Innerhalb der Gruppen von SP1 wird
  5. Gruppiert nach SP2
  6. Die Gruppen von SP2 werden anschließend nach dem kleinsten Datum sortiert, sodass
    SP2=D vor SP2=C kommt, sofern SP2=D ein geringeres Datum wie SP2=C enthält
  7. Die Untergruppen von SP2 werden nach wie vor beschriebener Sortierung wieder aufgelöst
  8. Nun werden die Gruppen von SP1 so sortiert, dass SP1=B vor SP1=A kommt, sofern SP1=B
    ein geringeres Datum enthält wie SP1=A
  9. Die nun sortierten Gruppen von SP1 werden aufgeöst
    -> ToList
    -> Ausgabe

Für die von mir notierten Eingangswerte - ergibt das folgende Ausgabe:
B C 01.01.2002
B D 01.01.2006
B D 01.01.2008
A C 01.01.2003
A C 01.01.2004
A C 01.01.2008
A C 01.01.2016
A D 01.01.2006

Oder habe ich jetzt noch was falsch verstanden? Wie genau hättest du denn die Ausgabe anders?

LG

B
BeZi Themenstarter:in
153 Beiträge seit 2007
vor 8 Jahren

Also irgendwie bekomme ich es nicht hin, ich habe folgendes:



            DataTable tmpSortTbl = tmpTbl.AsEnumerable()

            .GroupBy(A_R => new { ES = A_R.Field<string>("SP1") })
            .OrderBy(f => f.Key.ES).ToList()
            .SelectMany( a1=>a1

                .GroupBy(A_R => new { AR = A_R.Field<string>("SP2") })
                .OrderBy(f => f.Key.AR)
                .SelectMany(a2 => a2

                    .GroupBy(T_C => new { TC = T_C.Field<string>("SP3").Split(' ')[1] })
                    .OrderBy(f => f.Key.TC)
                    .SelectMany(a3 => a3

                        .GroupBy(T_C => new { TC = T_C.Field<string>("SP4") })
                        .OrderBy(f => f.Key.TC)
                        .SelectMany(a4 => a4

                                .GroupBy(M_A => new { MA = M_A.Field<string>("SP6") })
                                .OrderByDescending(f => f.Key.MA)
                                .SelectMany(a6 => a6

                                .GroupBy(S_T => new { ST = S_T.Field<string>("SP7") })
                                .OrderBy(f => f.Key.ST)
                                .SelectMany(a7 => a7

                                    .GroupBy(M_L => new { ML = M_L.Field<DateTime>("SP8")})
                                    .OrderBy(f => f.Key.ML)
                                    .SelectMany(a8 => a8)
                                    )
                                )
                            )
                        )
                    )
                )
            ).CopyToDataTable();


also mehrere Spalten. Alle diese werden nur nach dem Alphabet sortiert... Aber leider nicht nach datum ? Ich weiss auch leider nicht wie ich dieses Datum dort hineinbekomme.....

Ich sehe den Wald wohl vor lauter Bäumen nicht....

ARGGGGHHHHHHHH - HELP 🤔

1.029 Beiträge seit 2010
vor 8 Jahren

Hast du meinen letzten Code überhaupt probiert?

Was du da treibst ist jeweils sortiert gruppieren - beginnend mit SP1.
Im Endeffekt nichts anderes wie (Pseudo-Code)
.OrderBy(SP1)
.ThenBy(SP2)
...
.ThenBy(SP8)

Nur deutlich umständlicher. So wie ich dich verstanden hatte - wolltest du jedoch gruppiert sortieren, wobei die Reihenfolge der jeweiligen Gruppen anhand des kleinsten Datums der Gruppe ermittelt wird. Das kleinste Datum verwendest du allerdings nicht mal irgendwo.

LG

Edit:

Mal auf dein Vorhaben zum durchtesten - aber hübsch ist das schon lange nicht mehr^^

static DataTable SortTable2(DataTable table)
        {
            return table.AsEnumerable()
                .OrderBy(v => v["SP8"])
                .GroupBy(v => v["SP1"])
                .Select(g => g
                    .GroupBy(v => v["SP2"])
                    .OrderBy(g2 => g2.Select(e2 => e2["SP8"]).Min())
                    .Select(g3 => g3
                        .GroupBy(v2 => v2["SP3"].ToString().Split(' ')[1])
                        .OrderBy(g4 => g4.Select(e3 => e3["SP8"]).Min())
                        .Select(g4 => g4
                            .GroupBy(v2 => v2["SP4"])
                            .OrderBy(g5 => g5.Select(e3 => e3["SP8"]).Min())
                            .Select(g5 => g5
                                .GroupBy(v2 => v2["SP5"])
                                .OrderBy(g6 => g6.Select(e3 => e3["SP8"]).Min())
                                .Select(g7 => g7
                                    .GroupBy(v2 => v2["SP6"])
                                    .OrderBy(g8 => g8.Select(e3 => e3["SP8"]).Min())
                                    .Select(g8 => g8
                                        .GroupBy(v2 => v2["SP6"])
                                        .OrderBy(g9 => g9.Select(e3 => e3["SP8"]).Min())
                                        .SelectMany(r1 => r1))
                                    .SelectMany(r1 => r1))
                                .SelectMany(r1 => r1))
                            .SelectMany(r1 => r1))
                        .SelectMany(r1 => r1))
                    .SelectMany(r1 => r1))
                .OrderBy(g => g.Select(e => e["SP8"]).Min())
                .SelectMany(r1 => r1)
                .CopyToDataTable();
        }
B
BeZi Themenstarter:in
153 Beiträge seit 2007
vor 8 Jahren

Sali @Taipi88 und merci...

Habe es getestet und es funktioniert doch, hatte einen fehler beim übernehmen...

Wie bekomme ich es hin noch weitere Spalten nach dem gleichen Schema hinzuzufügen. Es ist mir nicht ganz klar....

und danke nochmals.... Echt top

1.029 Beiträge seit 2010
vor 8 Jahren

Siehe letzte Variante?

B
BeZi Themenstarter:in
153 Beiträge seit 2007
vor 8 Jahren

WOW, OPS..... Übersehen, sorry

und fettes merci nochmals

megacool... das nenn ich Hilfe