Classifique uma lista de outros IDs de lista


150

Eu tenho uma lista com alguns identificadores como este:

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

Além disso, tenho outra lista de <T>itens, representados pelos IDs descritos acima.

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

Preciso manter a mesma ordem nas duas coleções, para que os itens List<T>estejam na mesma posição que na primeira (devido a razões de pontuação do mecanismo de pesquisa). E esse processo não pode ser feito na GetDocsFromDb()função.

Se necessário, é possível alterar a segunda lista para outra estrutura ( Dictionary<long, T>por exemplo), mas eu prefiro não alterá-la.

Existe alguma maneira simples e eficiente de fazer essa "ordenação dependendo de alguns IDs" com o LINQ?


você tem certeza de que tudo docIdocorre exatamente uma vez docs, qual propriedade manterá o Idseletor ou será Func<T, long>necessário?
precisa

A primeira lista representa uma "lista principal"? Por outras palavras, a segunda lista será um subconjunto que representa uma parte (ou a totalidade) da primeira lista?
code4life

Respostas:


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

@Kaf é por isso que votei também, depende de saber que a propriedade ID do documento é chamada Id. Não está especificado na pergunta.
Jodrell

3
@ BorjaLópez, uma nota rápida. Você mencionou eficiência em sua pergunta. IndexOfé perfeitamente aceitável para o seu exemplo e agradável e simples. Se você tivesse muitos dados, minha resposta poderia ser mais adequada. stackoverflow.com/questions/3663014/…
Jodrell 17/07

2
@DenysDenysenko Fantastic. Muito obrigado; Exatamente o que eu estava procurando.
Silkfire

3
não funciona se você tiver elementos em documentos que não têm ids na lista de ordenação
Dan Hunex

4
Bastante ineficiente - IndexOf está sendo chamado para cada elemento na coleção de origem e OrderBy precisa ordenar os elementos. A solução da @Jodrell é muito mais rápida.
Sdds 29/11

25

Como você não especifica 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];
    }
}

É uma extensão genérica para o que você deseja.

Talvez você possa usar a extensão assim,

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

Uma versão mais segura pode 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á se sourcenão for exatamente o mesmo order.


Eu usei esta solução e funcionou. Apenas isso, eu tive que tornar o método estático e a classe estática.
vinmm

5

A resposta de Jodrell é a melhor, mas na verdade ele foi reimplementado System.Linq.Enumerable.Join. A associação também usa a Pesquisa e mantém a ordem de origem.

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

Essa é a resposta que estamos procurando
Orace 27/01

2
Isso apenas prova que o Join é muito difícil de entender, pois todos concordaram que reescrevê-lo era mais fácil.
PRMan 16/04

-3

Uma abordagem simples é compactar com a sequência de pedidos:

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

por que encomendar depois de um zip?
precisa

Porque Zipcombina cada índice (em uma Tupla) com o documento na mesma posição na lista correspondente. Em seguida, o OrderBy classifica as Tuplas pela parte do índice e, em seguida, o select escava nossos documentos apenas da lista ordenada agora.
Albin Sunnanbo 7/13

mas o resultado de GetDocsFromDb não é ordenado, portanto você criará Tuplas onde Item1não estiver relacionado Item2.
precisa

1
Eu acho que isso produzirá resultados incorretos desde que você solicitou a realização de uppon id e não o índice.
Michael Logutov 31/03
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.