Sortieren Sie eine Liste aus anderen Listen-IDs

149

Ich habe eine Liste mit einigen Bezeichnern wie diesen:

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

Außerdem habe ich eine andere Liste von <T>Elementen, die durch die oben beschriebenen IDs dargestellt werden.

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

Ich muss in beiden Sammlungen die gleiche Reihenfolge einhalten, damit sich die Artikel in List<T>derselben Position befinden wie in der ersten (aus Gründen der Suchmaschinenbewertung). Und dieser Vorgang kann in der GetDocsFromDb()Funktion nicht durchgeführt werden .

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

Gibt es eine einfache und effiziente Möglichkeit, diese "Ordenation abhängig von einigen IDs" mit LINQ durchzuführen?

Borja López
quelle
Sind Sie sicher, dass jeder docIdgenau einmal vorkommt docs, welche Eigenschaft wird die halten Idoder wird ein Selektor Func<T, long>benötigt?
Jodrell
Stellt die erste Liste eine "Hauptliste" dar? Mit anderen Worten, wird die zweite Liste eine Teilmenge sein, die einen Teil (oder die Gesamtheit) der ersten Liste darstellt?
Code4life

Antworten:

331
docs = docs.OrderBy(d => docsIds.IndexOf(d.Id)).ToList();
Denys Denysenko
quelle
@Kaf, deshalb habe ich auch upvoted, setzt voraus, dass die Document ID-Eigenschaft aufgerufen wird Id. Es ist nicht in der Frage angegeben.
Jodrell
3
@ BorjaLópez, eine kurze Notiz. Sie erwähnen Effizienz in Ihrer Frage. IndexOfist für Ihr Beispiel durchaus akzeptabel und nett und einfach. Wenn Sie viele Daten hätten, wäre meine Antwort möglicherweise besser geeignet. stackoverflow.com/questions/3663014/…
Jodrell
2
@ DenysDenysenko Fantastisch. Vielen Dank; genau das, wonach ich gesucht habe.
Seidenfeuer
3
funktioniert nicht, wenn Sie Elemente in Dokumenten haben, die keine IDs in der
Bestellliste
4
Ziemlich ineffizient - IndexOf wird für jedes Element in der Quellensammlung aufgerufen und OrderBy muss die Elemente ordnen. Die Lösung von @Jodrell ist viel schneller.
sdds
25

Da Sie nicht angeben 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];
    }
}

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

Sie könnten die Erweiterung vielleicht so verwenden,

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

Eine sicherere Version könnte sein

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

was funktioniert, wenn sourcenicht genau mit zip order.

Jodrell
quelle
Ich habe diese Lösung verwendet und es hat funktioniert. Nur das, ich musste die Methode statisch und die Klasse statisch machen.
Vinmm
5

Jodrells Antwort ist die beste, aber tatsächlich hat er sie neu implementiert System.Linq.Enumerable.Join. Join verwendet auch Lookup und ordnet die Reihenfolge der Quelle weiter.

    docIds.Join(
      docs,
      i => i,
      d => d.Id,
      (i, d) => d);
Kladzey
quelle
Das ist die Antwort, nach der wir suchen
Orace
2
Dies beweist nur, dass Join zu schwer zu verstehen ist, da sich alle einig waren, dass das Umschreiben einfacher war.
PRMan
-3

Ein einfacher Ansatz besteht darin, mit der Bestellreihenfolge zu zippen:

List<T> docs = GetDocsFromDb(...).Zip(docIds, Tuple.Create)
               .OrderBy(x => x.Item2).Select(x => x.Item1).ToList();
Albin Sunnanbo
quelle
Warum nach einem Reißverschluss bestellen?
Jodrell
Denn Zipkombiniert jeden Index (zu einem Tupel) mit dem Dokument an derselben Position in der entsprechenden Liste. Dann sortiert der OrderBy die Tupel nach dem Indexteil und dann wählt die Auswahl nur die Dokumente aus der jetzt geordneten Liste aus.
Albin Sunnanbo
sondern ist das Ergebnis von GetDocsFromDb ungeordnete so werden Sie Tupeln erschaffen , wo Item1, ist in keinem Zusammenhang Item2.
Jodrell
1
Ich denke, dies wird zu falschen Ergebnissen führen, da die Bestellung die Uppon-ID und nicht den Index ausführt.
Michael Logutov