Сортування списку з інших ідентифікаторів списку


150

У мене є список з такими ідентифікаторами, як цей:

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

Morover, у мене є ще один перелік <T>елементів, які представлені описаними вище ідентифікаторами.

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

Мені потрібно зберігати однаковий порядок в обох колекціях, щоб елементи, що List<T>знаходяться в цьому місці, знаходилися в тому ж самому положенні, що і в першому (через причини оцінки пошукових систем). І цей процес неможливо виконати у GetDocsFromDb()функції.

Якщо необхідно, можна змінити другий список на якусь іншу структуру ( Dictionary<long, T>наприклад), але я вважаю за краще не змінювати його.

Чи є якийсь простий та ефективний спосіб зробити це "висвячення залежно від деяких ідентифікаторів" за допомогою LINQ?


Ви впевнені, що кожен docIdтрапляється точно один раз docs, яке властивість буде містити Idабо потрібен буде вибір Func<T, long>?
Джодрелл

Чи представляє перший список "головний список"? Іншими словами, чи буде другий список підмножиною, що представляє частину (або сукупність) першого списку?
code4life

Відповіді:


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

@Kaf, чому я теж відмовив, покладається на те, щоб знати властивість ідентифікатора документа Id. Його не зазначено у питанні.
Джодрелл

3
@ BorjaLópez, швидка примітка. У своєму запитанні ви згадуєте про ефективність. IndexOfцілком прийнятний для вашого прикладу і приємний і простий. Якби у вас було багато даних, моя відповідь може бути більш підходящою. stackoverflow.com/questions/3663014/…
Jodrell

2
@DenysDenysenko Fantastic. Дуже дякую; саме те, що я шукав.
шовковий вогонь

3
не працює, якщо у вас є елементи в документах, які не мають ідентифікаторів у списку замовлень
Dan Hunex

4
Досить неефективно - IndexOf викликається для кожного елемента в колекції джерел, і OrderBy повинен замовляти елементи. Рішення від @Jodrell набагато швидше.
sdds

25

Оскільки ви не вказуєте 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];
    }
}

Це загальне розширення для того, що ви хочете.

Ви можете використати таке розширення, можливо,

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

Більш безпечною може бути версія

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

яка працюватиме, якщо sourceне блискавки точно order.


Я використав це рішення, і воно спрацювало. Просто це, я повинен був зробити метод статичним, а клас - статичним.
vinmm

5

Відповідь Джодрела найкраща, але насправді він повторно реалізується System.Linq.Enumerable.Join. Join також використовує Lookup і підтримує впорядкування джерела.

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

Таку відповідь ми шукаємо
Орація

2
Це просто доводить, що Приєднання занадто важко зрозуміти, оскільки всі погодилися, що переписати це було простіше.
PRMan

-3

Один простий підхід - це блискавка з послідовністю замовлення:

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

чому замовляти після поштового індексу?
Джодрелл

Тому що Zipпоєднує кожен індекс (в кортеж) з документом на тій же позиції у відповідному списку. Тоді OrderBy сортує кортежі за індексною частиною, а потім вибір викопує наші лише документи з упорядкованого списку.
Альбін Суннанбо

але результат GetDocsFromDb не впорядкований, тому ви створюватимете Tuples там, де Item1це не пов’язано Item2.
Джодрелл

1
Я думаю, що це призведе до невірних результатів, оскільки замовлення виконує ідентифікатор uppon, а не індекс.
Михайло Логутов
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.