Ordenar una lista de otras ID de lista


150

Tengo una lista con algunos identificadores como este:

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

Además, tengo otra lista de <T>elementos, que están representados por los identificadores descritos anteriormente.

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

Necesito mantener el mismo orden en ambas colecciones, de modo que los elementos en List<T>deben estar en la misma posición que en la primera (debido a razones de puntuación del motor de búsqueda). Y este proceso no se puede hacer en la GetDocsFromDb()función.

Si es necesario, es posible cambiar la segunda lista a otra estructura ( Dictionary<long, T>por ejemplo), pero preferiría no cambiarla.

¿Hay alguna forma simple y eficiente de hacer esta "orden según algunas ID" con LINQ?


¿está seguro de que todo docIdocurre exactamente una vez docs, qué propiedad contendrá Ido se requerirá un selector Func<T, long>?
— Jodrell

¿La primera lista representa una "lista maestra"? En otras palabras, ¿la segunda lista será un subconjunto que representa una parte (o la totalidad) de la primera lista?
— code4life

Respuestas:


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

@Kaf es por eso que también voté, se basa en saber que se llama a la propiedad de ID de documento Id. No se especifica en la pregunta.
— Jodrell

3
@ BorjaLópez, una nota rápida. Menciona eficiencia en su pregunta. IndexOfes perfectamente aceptable para su ejemplo y agradable y simple. Si tuviera muchos datos, mi respuesta podría ser más adecuada. stackoverflow.com/questions/3663014/…
— Jodrell

2
@DenysDenysenko Fantástico. Muchas gracias; Exactamente lo que estaba buscando.
— silkfire

3
no funciona si tiene elementos en documentos que no tienen identificadores en la lista de pedidos
— Dan Hunex

44
Bastante ineficiente: se llama a IndexOf para cada elemento de la colección de origen y OrderBy tiene que ordenar los elementos. La solución de @Jodrell es mucho más rápida.
— sdds

25

Como no especificas 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];
    }
}

Es una extensión genérica para lo que quieres.

Podrías usar la extensión como esta quizás,

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

Una versión más segura podría ser

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

que funcionará si sourceno se comprime exactamente con order.


Usé esta solución y funcionó. Solo eso, tuve que hacer el método estático y la clase estática.
— Vinmm

5

La respuesta de Jodrell es la mejor, pero en realidad se volvió a implementar System.Linq.Enumerable.Join. Join también usa Lookup y sigue ordenando la fuente.

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

Esa es la respuesta que estamos buscando
— Orace

2
Esto solo prueba que Join es demasiado difícil de entender ya que todos acordaron que reescribirlo fue más fácil.
— PRMan

-3

Un enfoque simple es comprimir con la secuencia de pedido:

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

¿Por qué ordenar después de un zip?
— Jodrell

Porque Zipcombina cada índice (en una Tupla) con el documento en la misma posición en la lista correspondiente. Luego, OrderBy clasifica las Tuplas por la parte del índice y luego la selección excava nuestros documentos de la lista ahora ordenada.
— Albin Sunnanbo

pero, el resultado de GetDocsFromDb no está ordenado, por lo que creará Tuplas donde Item1no está relacionado Item2.
— Jodrell

1
Creo que esto producirá resultados incorrectos desde que ordené realizar la identificación de uppon y no el índice.
— Michael Logutov
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.