2013-03-07 18 views
61

ho una lista con alcuni identificatori come questo:Ordinamento di un elenco da un altro ID Lista

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

Senza dimenticare che ho un altro elenco di <T> elementi, che sono rappresentati dalle ids sopra descritti.

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

ho bisogno di mantenere lo stesso ordine in entrambe le collezioni, in modo che i prodotti in List<T> devono trovarsi nella stessa posizione rispetto alla prima (a causa di cercare ragioni del motore di punteggio). E questo processo non può essere eseguito nella funzione GetDocsFromDb().

Se necessario, è possibile modificare la seconda lista in un'altra struttura (Dictionary<long, T> per esempio), ma preferirei non cambiarla.

Esiste un modo semplice ed efficace per eseguire questa "ordinazione dipendente da alcuni ID" con LINQ?

+0

si ha la certezza che ogni 'docId' si verifica esattamente una volta in' docs', quale proprietà terrà l'id 'o sarà richiesto un selettore 'Func '? – Jodrell

+0

Il primo elenco rappresenta una "lista principale"? Altre parole, la seconda lista sarà un sottoinsieme che rappresenta una porzione (o l'insieme) della prima lista? – code4life

risposta

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

@Kaf questo è il motivo per cui ho upvoted, fa affidamento sul fatto che la proprietà dell'ID del documento è chiamata 'Id'. Non è specificato nella domanda. – Jodrell

+0

@ BorjaLópez, una breve nota. Hai menzionato l'efficienza nella tua domanda. 'IndexOf' è perfettamente accettabile per il tuo esempio e piacevole e semplice. Se tu avessi molti dati la mia risposta potrebbe essere più adatta. http://stackoverflow.com/questions/3663014/why-is-this-list-indexof-code-so-much-faster-than-the-listi-and-manual-compa – Jodrell

+0

Ho benchmarkato questo metodo e il uno con il dizionario (vedi sotto) ed è quasi il doppio più veloce. –

-1

Un approccio semplice è quello di cerniera con la sequenza di ordinazione:

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

perché ordinare dopo una chiusura lampo? – Jodrell

+0

Poiché "Zip" combina ciascun indice (in una tupla) con il documento nella stessa posizione nell'elenco corrispondente.Quindi OrderBy ordina le tuple per la parte dell'indice e quindi seleziona i nostri soli documenti dall'elenco ora ordinato. –

+0

ma, il risultato di GetDocsFromDb non è ordinato, quindi creerai tuple in cui "Articolo1" non è correlato a "Elemento2". – Jodrell

5

Dal momento che non è necessario specificare 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 generico per ciò che si vuole.

si potrebbe usare l'estensione come questo, 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 source non zip esattamente con order.

Problemi correlati