Ordina un elenco da un altro ID elenco


150

Ho un elenco con alcuni identificatori come questo:

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

Inoltre, ho un altro elenco di <T>elementi, che sono rappresentati dagli ID sopra descritti.

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

Devo mantenere lo stesso ordine in entrambe le raccolte, in modo che gli articoli in List<T>siano nella stessa posizione rispetto al primo (per motivi di punteggio del motore di ricerca). E questo processo non può essere eseguito nella GetDocsFromDb()funzione.

Se necessario, è possibile modificare il secondo elenco in un'altra struttura ( Dictionary<long, T>ad esempio), ma preferirei non modificarlo.

Esiste un modo semplice ed efficace per eseguire questa "ordinazione in base ad alcuni ID" con LINQ?


hai la certezza che ogni cosa si docIdverifica esattamente una volta dentro docs, quale proprietà conterrà Ido sarà richiesto un selettore Func<T, long>?
Jodrell

Il primo elenco rappresenta un "elenco principale"? In altre parole, il secondo elenco sarà un sottoinsieme che rappresenta una parte (o l'intero) del primo elenco?
code4life

Risposte:


332
docs = docs.OrderBy(d => docsIds.IndexOf(d.Id)).ToList();

@Kaf è anche per questo che ho effettuato l'upgrade, facendo affidamento sul fatto che viene chiamata la proprietà ID documento Id. Non è specificato nella domanda.
Jodrell

3
@ BorjaLópez, una breve nota. Citi l'efficienza nella tua domanda. IndexOfè perfettamente accettabile per il tuo esempio e simpatico e semplice. Se avessi molti dati, la mia risposta potrebbe essere più adatta. stackoverflow.com/questions/3663014/…
Jodrell

2
@DenysDenysenko Fantastic. Grazie mille; esattamente quello che stavo cercando.
Silkfire,

3
non funziona se hai elementi nei documenti che non hanno ID nella lista degli ordini
Dan Hunex,

4
Abbastanza inefficiente: IndexOf viene chiamato per ogni elemento nella raccolta di origine e OrderBy deve ordinare gli elementi. La soluzione di @Jodrell è molto più veloce.
sdds,

25

Dal momento che non specifichi 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];
    }
}

È un'estensione generica per quello che vuoi.

Potresti usare l'estensione in questo modo forse,

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

Una versione più sicura potrebbe essere

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

che funzionerà se sourcenon si comprime esattamente con order.


Ho usato questa soluzione e ha funzionato. Solo che, ho dovuto rendere statico il metodo e la classe statica.
Vinmm

5

La risposta di Jodrell è la migliore, ma in realtà ha reimplementato System.Linq.Enumerable.Join. Join utilizza anche Lookup e continua a ordinare la fonte.

    docIds.Join(
      docs,
      i => i,
      d => d.Id,
      (i, d) => d);

Questa è la risposta che stiamo cercando
Orace,

2
Ciò dimostra che Join è troppo difficile da capire poiché tutti concordavano sul fatto che riscriverlo fosse più facile.
PRMan

-3

Un approccio semplice è comprimere con la sequenza di ordinazione:

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

perché ordinare dopo una zip?
Jodrell

Perché Zipcombina ogni indice (in una Tupla) con il documento nella stessa posizione nell'elenco corrispondente. Quindi OrderBy ordina le Tuple in base alla parte dell'indice e quindi seleziona seleziona i nostri documenti dall'elenco ora ordinato.
Albin Sunnanbo,

ma il risultato di GetDocsFromDb non è ordinato, quindi creerai Tuple in cui Item1non è correlato Item2.
Jodrell

1
Penso che questo produrrà risultati errati dall'ordinare eseguendo id uppon e non l'indice.
Michael Logutov,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.