2013-03-07 2 views
61

Ich habe eine Liste mit einigen Bezeichner wie folgt aus:Sortieren einer Liste aus einer anderen Liste IDs

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

Morover, ich habe eine andere Liste von <T> Elemente, die durch die ids oben beschrieben dargestellt werden.

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

Ich brauche die gleiche Reihenfolge in beiden Sammlungen zu halten, so dass die Elemente in List<T> in der gleichen Position sein muss als in dem ersten (wegen Motor Scoring Gründe suchen). Und dieser Prozess kann nicht in der GetDocsFromDb() Funktion durchgeführt werden.

Bei Bedarf ist es möglich, die zweite Liste in eine andere Struktur (Dictionary<long, T> zum Beispiel) zu ändern, aber ich würde es vorziehen, es nicht zu ändern.

Gibt es eine einfache und effiziente Möglichkeit, diese "ORDENATION abhängig von einigen IDs" mit LINQ zu tun?

+0

versichern Sie, dass jeder 'docId' genau einmal in' docs' auftritt, welche Eigenschaft die 'Id' halten oder wird ein Selektor' Func 'erforderlich? – Jodrell

+0

Stellt die erste Liste eine "Masterliste" dar? Ein anderes Wort, wird die zweite Liste eine Untermenge sein, die einen Teil (oder die Gesamtheit) der ersten Liste darstellt? – code4life

Antwort

170
docs = docs.OrderBy(d => docsIds.IndexOf(d.Id)).ToList(); 
+0

@Kaf das ist, warum ich auch upvoted, verlässt sich darauf zu wissen, dass die Dokument-ID-Eigenschaft heißt "Id". Es ist nicht in der Frage angegeben. – Jodrell

+0

@ BorjaLópez, eine kurze Notiz. Sie erwähnen Effizienz in Ihrer Frage. 'IndexOf' ist vollkommen akzeptabel für Ihr Beispiel und schön und einfach. Wenn Sie viele Daten haben, ist meine Antwort möglicherweise besser geeignet. http://stackoverflow.com/questions/3663014/why-is-is-list-indexof-code-so-much-faster-than-the-lis-and-manual-compa – Jodrell

+0

Ich habe diese Methode und das Benchmarking durchgeführt eins mit dem Wörterbuch (siehe unten) und es ist fast doppelt so schnell. –

-1

Ein einfacher Ansatz ist mit der Bestellsequenz zip:

List<T> docs = GetDocsFromDb(...).Zip(docIds, Tuple.Create) 
       .OrderBy(x => x.Item2).Select(x => x.Item1).ToList(); 
+0

warum nach einem zip bestellen? – Jodrell

+0

Da 'Zip' jeden Index (in einem Tuple) mit dem Dokument an der gleichen Position in der entsprechenden Liste kombiniert.Dann sortiert die OrderBy die Tupel nach dem Indexteil und dann graviert die Auswahl unsere Dokumente aus der nun geordneten Liste. –

+0

, aber das Ergebnis von GetDocsFromDb ist ungeordnet, so dass Sie Tupel erstellen, in denen 'Item1' nicht mit' Item2' verwandt ist. – Jodrell

5

Da Sie nicht T angeben,

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

ist eine generische Erweiterung für das, was Sie wollen.

Sie konnten die Erweiterung wie diese vielleicht verwenden,

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

Eine sicherere Version

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

, die funktionieren könnte, wenn source nicht genau mit order nicht sehen lassen.