2015-09-07 14 views
5

Aby zamówić listę z Linq, musimy zadzwonić pod numer OrderBy po raz pierwszy pod numerem ThenBy w sprawie wyniku dla podrzędnych zamówień.lista zamówień, w których zamówienie na najwyższym poziomie jest nieznane

Jestem w sytuacji, w której nie znam kolejności na najwyższym poziomie przed rozdaniem. Mam listę zamówień, które powinny być stosowane warunkowo.

Jak to:

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); 

więc lista będzie zawsze być sortowane według Element 1, a warunkowo przez ITEM2 i/lub ITEM3 pierwszy.

Jak to zrobić w języku C#? Rozwiązania bez Linq są również mile widziane.

+1

co jest nie tak z tym, co masz - wystarczy napisać 'var orderedlist = list.OrderBy (t => t.Item1);' a zachowaj wszystko ** ale ** ostatnia linia – Carsten

+0

'ThenBy' jest dostępna tylko na' IOrderedEnumerable', więc możesz użyć sprawdzenia typu. Jeśli 'IOrderedEnumerable' użyje' ThenBy' else 'OrderBy' – Jehof

+2

@Carsten Problem z tym, że Johan chce zamówić' Item1' * ostatni *. Nie jest to łatwe do odczytania, ale to właśnie napisał :) – Luaan

Odpowiedz

7

Wystarczy użyć

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

Daje to domyślny (nie) uporządkowania i pozwala na dodawanie wielu innych zamówienia, które chcesz później, używając tylko ThenBy.

EDIT:

Jak zauważył Tim, to niesie karę wydajności - wydaje się, że domyślna LINQ-obiekty dostawca nie jest wystarczająco silny, aby odbudować kolejność aby pozbyć się „non -rozporządkowanie ". Nie stanowi to problemu, jeśli twoja lista jest niewielka, ale jeśli zabiera mało znaczącą ilość czasu, prawdopodobnie chcesz to zrobić w trudny sposób.

Na przykład, można użyć metody pomocnika jak

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); 
} 

To nie jest dokładnie samo robisz, ale jeśli pracujesz na przeliczalne, które zostały zamówione wcześniej, to będzie działać w ten sam sposób.

+0

Z technicznego punktu widzenia _nie_ jest to zamawianie, ale zamawianie o tę samą stałą wartość. Ponieważ jest to takie samo dla każdego przedmiotu, używane jest pierwsze 'ThenBy' i tak dalej. Prawdopodobnie mikro-optymalizacja, ale w dużych kolekcjach może to mieć znaczenie.Po co używać pseudo-porównania, jeśli możesz zacząć od tego prawa? Przykład: http://csharppad.com/gist/6d19c47c672c81dc2c52 (30 i 17 sekund na moim komputerze) –

+0

@TimSchmelter Interesujące, różnica jest znacznie większa niż się spodziewałem (oczywiście w SQL jest to operacja "no-op"). Na moim komputerze uzyskuję 16 vs 12s - wciąż dość drogie, jeśli trzeba pracować z długimi listami, ale nie tak przerażające, jak 100% wzrost czasu wykonywania. Ale może mam po prostu większy bufor pamięci CPU niż ty. Zdecydowanie pójdę z twoim rozwiązaniem (no cóż, po tym, jak faktycznie działa tak, jak chce OP: D). To tylko jedna z tych dziwacznych decyzji dotyczących projektowania - trochę rozumiem, dlaczego tak się dzieje, ale sprawia ona, że ​​skład zapytania jest nieco trudniejszy, niż powinien. – Luaan

2

Użyć IOrderedEnumerable zamiast listy i 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

Należy zauważyć, że moje warunki sortowania nie były "if {} else if {}", ale "if {} if {}". Jeśli "sortByString" i "sortByDateTime" są prawdziwe, to kolejność drugiego rzędu spowoduje nadpisanie pierwszej. –

+0

@JohanvanderSlikke: ok, nie było jasne. Zmieniono moją odpowiedź. Ale jeśli 'sortByDateTime' ma priorytet, powinieneś użyć' if (sortByDateTime) {} else if (sortByString) {} ' –

-1

Musisz użyć machiny państwowej jak kod poniżej

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; 
      } 
     } 
    } 
} 


​