2015-09-07 10 views
5

Um eine Liste mit Linq zu bestellen, müssen wir OrderBy zuerst aufrufen ThenBy auf das Ergebnis für untergeordnete Ordnungen.Bestellliste, wo Top-Level-Reihenfolge unbekannt ist

Ich bin in einer Situation, in der ich nicht die Top-Level-Bestellung vor der Hand weiß. Ich habe eine Liste von Bestellungen, die unter bestimmten Bedingungen angewendet werden sollten.

So:

var list = new List<Tuple<int, string, DateTime>>(); 
list.Add(new Tuple<int, string, DateTime>(1, "B", new DateTime(2020, 1, 1))); 
list.Add(new Tuple<int, string, DateTime>(2, "A", new DateTime(2000, 1, 1))); 
list.Add(new Tuple<int, string, DateTime>(3, "C", new DateTime(1900, 1, 1))); 

var orderedList = list; 

if (sortByString) 
{ 
    orderdedList = orderedList.ThenBy(listItem => listItem.Item2); 
} 

if (sortByDateTime) 
{ 
    orderedList = orderedList.ThenBy(listItem => listItem.Item3); 
} 

orderList = orderedList.ThenBy(listItem => listItem.Item1); 

So ist die Liste wird immer von element1 bestellt werden, und bedingt durch Element2 und/oder Item3 zuerst.

Wie erreichen Sie dies in C#? Lösungen ohne Linq sind ebenfalls willkommen.

+1

eine Zustandsmaschine wie der Code zu verwenden, was mit falsch ist, was Sie haben - schreiben Sie einfach 'var orderedlist = list.OrderBy (t => t.Item1);' und behalte alles ** aber ** die letzte Zeile – Carsten

+0

'ThenBy' ist nur auf' IOrderedEnumerable' verfügbar, also könntest du einen Typprüfung verwenden. Wenn 'IOrderedEnumerable'' ThenBy' verwenden sonst 'OrderBy' – Jehof

+2

@Carsten Das Problem dabei ist, dass Johan das' Item1' bestellen will * zuletzt *. Es ist nicht gerade einfach zu lesen, aber das ist, was er geschrieben hat :) – Luaan

Antwort

7

Verwenden Sie einfach

var orderedItems = list.OrderBy(_ => 1); 

Dies gibt Ihnen die Standard (Nicht-) Bestellung, und ermöglicht es Ihnen, so viele andere hinzufügen Bestellungen, wie Sie danach nur mit ThenBy möchten.

EDIT:

Wie Tim erwähnt, tut dies eine Leistungseinbuße führen - es scheint, dass die Standard-LINQ-to-Objects-Provider nicht intelligent genug ist, um die Ordnung wieder aufzubauen des „nicht loswerden -Bestellung". Dies ist kein Problem, wenn Ihre Liste klein ist, aber wenn es eine nicht zu vernachlässigende Menge an Zeit benötigt, möchten Sie wahrscheinlich dies auf die harte Tour machen.

Zum Beispiel könnten Sie eine Hilfsmethode wie

verwenden
public static IEnumerable<T> AppendOrdering<T, U>(this IEnumerable<T> @this, 
                Func<T, U> selector) 
{ 
    if (@this is IOrderedEnumerable<T>) return @this.ThenBy(selector); 

    return @this.OrderBy(selector); 
} 

Dies ist nicht genau das gleiche Sie tun, aber wenn man auf einem enumerable arbeiten, die vor bestellt wurde, es wird genauso funktionieren.

+0

Technisch ist dies _nicht_ eine Nicht-Ordnung, sondern eine Ordnung durch den gleichen konstanten Wert. Da das für jedes Item gleich ist, wird das erste 'ThenBy' verwendet und so weiter. Wahrscheinlich Mikro-Optimierung, aber auf großen Sammlungen kann es einen Unterschied machen.Warum mit einem Pseudo-Vergleich, wenn Sie mit dem Recht beginnen können? Beispiel: http://csharppad.com/gist/6d19c47c672c81dc2c52 (30 und 17 Sekunden auf meinem PC) –

+0

@TimSchmelter Interessant, der Unterschied ist viel größer als ich erwartet hatte (in SQL ist das natürlich ein No-Op). Auf meinem Computer bekomme ich 16s vs 12s - immer noch ziemlich teuer, wenn Sie mit langen Listen arbeiten müssen, aber nicht ganz so furchterregend wie Ihre 100% ige Laufzeitverlängerung. Aber vielleicht habe ich nur einen größeren CPU-Cache als du. Ich würde definitiv mit Ihrer Lösung gehen (nun, nachdem Sie es so gemacht haben, wie es das OP will: D). Es ist nur eine dieser seltsamen Design-Entscheidungen - ich verstehe das Warum, aber es macht die Abfrage-Komposition ein bisschen schwieriger als es sein muss. – Luaan

2

Verwenden Sie ein IOrderedEnumerable statt einer Liste und ein if ... else:

IOrderedEnumerable<Tuple<int, string, DateTime>> orderedItems = null; 

if (sortByDateTime) 
    orderedItems = list.OrderBy(listItem => listItem.Item3); 
else if (sortByString) 
    orderedItems = list.OrderBy(listItem => listItem.Item2); 

orderedItems = orderedItems.ThenBy(listItem => listItem.Item1); 
list = orderedItems.ToList(); 
+0

Beachten Sie, dass meine Sortierbedingungen kein 'if {} else if {} 'außer' if {} if {} 'waren. Wenn "sortByString" und "sortByDateTime" wahr sind, würde die zweite Reihenfolge die erste überschreiben. –

+0

@JohanvanderSlikke: ok, war nicht klar. Bearbeitete meine Antwort. Aber wenn 'sortByDateTime' Priorität hat, sollten Sie' if (sortByDateTime) {} verwenden sonst if (sortByString) {} ' –

-1

Sie müssen unter

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     enum State 
     { 
      One, 
      Two, 
     } 
     static void Main(string[] args) 
     { 
      State state = State.A; 

      switch (state) 
      { 
       case State.One: 
        //Order by item one 
        state = State.Two; 
        break; 
       case State.Two: 
        //Order by item two or three 
        state = State.One; 
        break; 
      } 
     } 
    } 
} 


​