Trier une liste à partir d'autres ID de liste


150

J'ai une liste avec des identifiants comme celui-ci:

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

Morover, j'ai une autre liste d' <T>éléments, qui sont représentés par les identifiants décrits ci-dessus.

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

Je dois garder le même ordre dans les deux collections, de sorte que les éléments dans List<T>doivent être dans la même position que dans la première (pour des raisons de notation des moteurs de recherche). Et ce processus ne peut pas être effectué dans la GetDocsFromDb()fonction.

Si nécessaire, il est possible de changer la deuxième liste en une autre structure ( Dictionary<long, T>par exemple), mais je préfère ne pas la changer.

Existe-t-il un moyen simple et efficace de faire cette «classification en fonction de certains ID» avec LINQ?


êtes-vous assuré que tout docIdse produit exactement une fois docs, quelle propriété tiendra le Idou un sélecteur Func<T, long>sera-t-il nécessaire?
Jodrell

La première liste représente-t-elle une «liste maîtresse»? Un autre mot, la deuxième liste sera-t-elle un sous-ensemble représentant une partie (ou l'intégralité) de la première liste?
code4life

Réponses:


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

@Kaf c'est pourquoi j'ai voté pour moi aussi, repose sur le fait de savoir que la propriété ID de document est appelée Id. Ce n'est pas spécifié dans la question.
Jodrell

3
@ BorjaLópez, un petit mot. Vous parlez d'efficacité dans votre question. IndexOfest parfaitement acceptable pour votre exemple et agréable et simple. Si vous aviez beaucoup de données, ma réponse serait peut-être mieux adaptée. stackoverflow.com/questions/3663014/…
Jodrell

2
@DenysDenysenko Fantastique. Merci beaucoup; exactement ce que je cherchais.
silkfire

3
ne fonctionne pas si vous avez des éléments dans les documents qui n'ont pas d'identifiant dans la liste de commande
Dan Hunex

4
Assez inefficace - IndexOf est appelé pour chaque élément de la collection source et OrderBy doit ordonner les éléments. La solution de @Jodrell est beaucoup plus rapide.
sdds

25

Puisque vous ne spécifiez pas 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];
    }
}

Est une extension générique pour ce que vous voulez.

Vous pouvez peut-être utiliser l'extension comme celle-ci,

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

Une version plus sûre pourrait être

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

qui fonctionnera si sourcene ferme pas exactement avec order.


J'ai utilisé cette solution et cela a fonctionné. Juste ça, j'ai dû rendre la méthode statique et la classe statique.
vinmm

5

La réponse de Jodrell est la meilleure, mais en fait, il a réimplémenté System.Linq.Enumerable.Join. Join utilise également Lookup et continue de classer la source.

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

C'est la réponse que nous recherchons
Orace le

2
Cela prouve simplement que Join est trop difficile à comprendre puisque tout le monde a convenu que la réécriture était plus facile.
PRMan

-3

Une approche simple consiste à compresser avec la séquence de commande:

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

pourquoi commander après un zip?
Jodrell

Parce que Zipcombine chaque index (dans un tuple) avec le document à la même position dans la liste correspondante. Ensuite, OrderBy trie les tuples par la partie d'index, puis la sélection ne creuse que les documents de la liste maintenant ordonnée.
Albin Sunnanbo

mais, le résultat de GetDocsFromDb n'est pas ordonné, vous allez donc créer des tuples là où il Item1n'y a aucun rapport Item2.
Jodrell

1
Je pense que cela produira des résultats incorrects depuis la commande exécutant uppon id et non l'index.
Michael Logutov
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.