Sortuj listę od innych identyfikatorów list

150

Mam listę z takimi identyfikatorami:

List<long> docIds = new List<long>() { 6, 1, 4, 7, 2 };

Ponadto mam inną listę <T>pozycji, które są reprezentowane przez opisane powyżej identyfikatory.

List<T> docs = GetDocsFromDb(...)

Muszę zachować tę samą kolejność w obu kolekcjach, aby pozycje w nich List<T>były w tej samej pozycji, co w pierwszej (ze względu na punktację w wyszukiwarkach). A tego procesu nie da się wykonać w GetDocsFromDb()funkcji.

W razie potrzeby można zmienić drugą listę na inną strukturę ( Dictionary<long, T>na przykład), ale wolałbym tego nie zmieniać.

Czy istnieje prosty i skuteczny sposób wykonania tej „porządkowania w zależności od niektórych identyfikatorów” za pomocą LINQ?

Borja López
źródło
Czy masz pewność, że każde docIdwystąpi dokładnie raz w docs, jaka właściwość będzie zawierała Idselektor, czy też Func<T, long>będzie potrzebny selektor ?
Jodrell
Czy pierwsza lista reprezentuje „listę główną”? Innymi słowy, czy druga lista będzie podzbiorem reprezentującym część (lub całość) pierwszej listy?
code4life

Odpowiedzi:

332
docs = docs.OrderBy(d => docsIds.IndexOf(d.Id)).ToList();
Denys Denysenko
źródło
@Kaf, dlatego też zagłosowałem za głosem, polega na znajomości właściwości identyfikatora dokumentu Id. Nie jest to określone w pytaniu.
Jodrell
3
@ BorjaLópez, krótka notatka. W swoim pytaniu wspominasz o efektywności. IndexOfjest całkowicie do przyjęcia dla twojego przykładu i jest ładny i prosty. Gdybyś miał dużo danych, moja odpowiedź mogłaby być bardziej odpowiednia. stackoverflow.com/questions/3663014/…
Jodrell
2
@DenysDenysenko Fantastic. Dzięki wielkie; dokładnie to, czego szukałem.
Silkfire
3
nie działa, jeśli masz elementy w dokumentach, które nie mają identyfikatorów na liście zamówienia
Dan Hunex
4
Dość nieefektywne - IndexOf jest wywoływane dla każdego elementu w kolekcji źródłowej, a OrderBy musi uporządkować elementy. Rozwiązanie @Jodrell jest znacznie szybsze.
sdds
25

Ponieważ nie określasz T,

IEnumerable<T> OrderBySequence<T, TId>(
       this IEnumerable<T> source,
       IEnumerable<TId> order,
       Func<T, TId> idSelector)
{
    var lookup = source.ToDictionary(idSelector, t => t);
    foreach (var id in order)
    {
        yield return lookup[id];
    }
}

To ogólne rozszerzenie tego, czego chcesz.

Możesz użyć rozszerzenia w ten sposób,

var orderDocs = docs.OrderBySequence(docIds, doc => doc.Id);

Mogłaby być bezpieczniejsza wersja

IEnumerable<T> OrderBySequence<T, TId>(
       this IEnumerable<T> source,
       IEnumerable<TId> order,
       Func<T, TId> idSelector)
{
    var lookup = source.ToLookup(idSelector, t => t);
    foreach (var id in order)
    {
        foreach (var t in lookup[id])
        {
           yield return t;
        }
    }
}

który zadziała, jeśli sourcenie zostanie dokładnie zapakowany order.

Jodrell
źródło
Użyłem tego rozwiązania i zadziałało. Po prostu musiałem uczynić metodę statyczną, a klasę statyczną.
vinmm
5

Odpowiedź Jodrella jest najlepsza, ale tak naprawdę to on ponownie zaimplementował System.Linq.Enumerable.Join. Join używa również Lookup i zachowuje kolejność źródeł.

    docIds.Join(
      docs,
      i => i,
      d => d.Id,
      (i, d) => d);
Kladzey
źródło
To jest odpowiedź, której szukamy
Orace
2
To tylko dowodzi, że Join jest zbyt trudny do zrozumienia, ponieważ wszyscy zgodzili się, że przepisanie go było łatwiejsze.
PRMan
-3

Jednym prostym podejściem jest spakowanie z sekwencją zamawiania:

List<T> docs = GetDocsFromDb(...).Zip(docIds, Tuple.Create)
               .OrderBy(x => x.Item2).Select(x => x.Item1).ToList();
Albin Sunnanbo
źródło
po co zamawiać po zipie?
Jodrell
Ponieważ Zipłączy każdy indeks (w krotkę) z dokumentem na tej samej pozycji na odpowiedniej liście. Następnie OrderBy sortuje krotki według części indeksu, a następnie wybierz wykopuje tylko nasze dokumenty z uporządkowanej listy.
Albin Sunnanbo
ale wynik GetDocsFromDb jest nieuporządkowany, więc będziesz tworzyć krotki, gdzie Item1nie jest to powiązane Item2.
Jodrell,
1
Myślę, że to da niepoprawne wyniki od momentu zamówienia wykonującego uppon id, a nie indeksu.
Michael Logutov