Збереження порядку з LINQ


361

Я використовую LINQ для вказівки щодо об'єктів впорядкованому масиві. Які операції не слід робити, щоб переконатися, що порядок масиву не змінився?

Відповіді:


645

Я вивчив методи System.Linq.Eumerable , відкинувши будь-які результати, що принесли не-численні результати. Я перевірив зауваження кожного, щоб визначити, як порядок результату відрізнятиметься від порядку джерела.

Зберігає замовлення абсолютно. Ви можете зіставити вихідний елемент за індексом до результату

  • Як безліч
  • У ролях
  • Concat
  • Виберіть
  • ToArray
  • ToList

Зберігає орден. Елементи фільтруються або додаються, але не переупорядковуються.

  • Виразний
  • За винятком
  • Перетинатися
  • OfType
  • Доплата (нова в .net 4.7.1)
  • Пропустити
  • Пропустити
  • Брати
  • Візьміть, поки
  • Де
  • Zip (новий у .net 4)

Знищує замовлення - ми не знаємо, в якому порядку очікувати результатів.

  • ToD Dictionary
  • ToLookup

Повторно визначає порядок - використовуйте їх, щоб змінити порядок результату

  • Сортувати за
  • OrderByDescending
  • Зворотний
  • ТодіБо
  • ПотімByDescending

Переосмислює замовлення за деякими правилами.

  • GroupBy - об’єкти IGrouping виводяться в порядку, заснованому на порядку елементів у джерелі, які створили перший ключ кожної IGrouping. Елементи в групуванні виводяться в тому порядку, в якому вони відображаються в джерелі.
  • GroupJoin - GroupJoin зберігає порядок елементів зовнішніх, а для кожного елемента зовнішніх - порядок відповідних елементів із внутрішніх.
  • Об’єднання - зберігає порядок елементів зовнішніх, а для кожного з цих елементів - порядок відповідності елементів внутрішніх.
  • SelectMany - для кожного елемента джерела викликається селектор і повертається послідовність значень.
  • Союз - Коли об'єкт, повернутий цим методом, перераховується, Союз перераховує перше і друге в тому порядку і отримує кожен елемент, який уже не був виведений.

Редагувати. Я перемістив "Розрізнення" на збереження порядку на основі цієї реалізації .

    private static IEnumerable<TSource> DistinctIterator<TSource>
      (IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
    {
        Set<TSource> set = new Set<TSource>(comparer);
        foreach (TSource element in source)
            if (set.Add(element)) yield return element;
    }

2
Власне, я думаю, що відмінність зберігає оригінальний (вперше знайдений) порядок - тож {1,2,1,3,1,3,4,1,5} було б {1,2,3,4,5}
Марк Гравелл

10
msdn.microsoft.com/en-us/library/bb348436.aspx Метод Distinct <(Of <(TSource>)>) (IEnumerable <(Of <(TSource>)>) "повертає не упорядковану послідовність, яка не містить дублюючих значень .
Емі Б

12
Марк: те, що ти кажеш, може бути правдою, але погано було б покладатися на таку поведінку.
Емі Б

4
@Amy B так, але це не стосується Linq до об'єктів. У Linq to Sql distit () ставить чітке ключове слово у створений sql, а впорядкування з sql не гарантується. Мені було б цікаво побачити реалізацію відмінних для linq об'єктів, які не зберігають порядок і більш ефективні, ніж ті, що зберігають порядок. Наприклад, ви можете споживати весь вхід і помістити його в хешсет, а потім отримати значення, перерахувавши хешсет (втрачаючи порядок), але це гірше. Так що так, я не проти відхиляти документацію раз у раз :)
дан

4
Можливо, документація (для Distinctметоду) просто означала сказати "несортовано", а не "в непередбачуваному порядку". Я б сказав, що Distinctналежить до вищевказаної категорії фільтрування Where.
Джеппе Стіг Нільсен

34

Ви насправді говорите про SQL чи про масиви? Інакше кажучи, ви використовуєте LINQ для SQL або LINQ для об'єктів?

Оператори LINQ для об'єктів насправді не змінюють свого початкового джерела даних - вони будують послідовності, які ефективно підтримуються джерелом даних. Єдиними операціями, які змінюють впорядкування, є OrderBy / OrderByDescending / thenBy / thenByDescending - і навіть тоді вони стабільні для однаково упорядкованих елементів. Звичайно, багато операцій будуть фільтрувати деякі елементи, але повернені елементи будуть у тому ж порядку.

Якщо ви конвертуєте в іншу структуру даних, наприклад, з ToLookup або ToDictionary, я не вважаю, що порядок зберігається в цій точці - але це все одно дещо інше. (Порядок відображення значень із тим самим ключем зберігається для пошуку, хоча, я вважаю.)


тому що OrderBy є стабільним сортом, тоді: seq.OrderBy (_ => _.Key) розмістить елементи в точно такому ж порядку, як і seq.GroupBy (_ => _.Key) .SelectMany (_ => _ ). Це правильно?
dmg

1
@dmg: Ні, не буде. Тільки GroupByпотім SelectManyдадуть результати , згруповані по ключу, але не в порядку зростання ключа ... він дасть їх у тому порядку , в якому ключі спочатку мали місце.
Джон Скіт

Ви говорите, що LINQ для SQL не зберігає порядок?
симбіонт

@symbiont: У багатьох операцій SQL є це недобре визначені для того , щоб почати с. В основному я намагаюся обіцяти лише ті речі, які я можу гарантувати - наприклад, LINQ для об'єктів.
Джон Скіт

@JonSkeet Якщо я використовую OrderBy, це гарантує, що 'n' об'єкти з одним ключем збережуть свою первісну послідовність, за винятком того, що вони всі разом. тобто: list<x> {a b c d e f g}якщо c, d, e всі мають один і той же ключ, то отримана послідовність буде містити c, d, e поруч один з одним І в порядку c, d, e. Я не можу знайти категоричну відповідь на основі МС.
Paulustrious

7

Якщо ви працюєте над масивом, це здається, що ви використовуєте LINQ до об'єктів, а не SQL; ви можете підтвердити? Більшість операцій LINQ нічого не здійснюють повторне замовлення (вихід буде в тому ж порядку, що і вхідний), тому не застосовуйте інший сорт (OrderBy [спадаючий] / потімBy [низхідний]).

[ред .: як Джон висловився чіткіше; LINQ зазвичай створює нову послідовність, залишаючи вихідні дані в спокої]

Зауважте, що натискання даних у Dictionary<,>(ToDictionary) скомпонує дані, оскільки словник не дотримується певного порядку сортування.

Але найпоширеніші речі (Select, Where, Skip, Take) мають бути добре.


Якщо я не помиляюся, ToDictionary()просто не обіцяє замовлення, але на практиці підтримує порядок введення (поки ви щось не вилучите з нього). Я не кажу, щоб покладатися на це, але "скремблінг" здається неточним.
Тимо

4

Я знайшов чудову відповідь у подібному питанні, яке посилається на офіційну документацію. Щоб його цитувати:

Для Enumerableметодів (LINQ до об'єктів, який відноситься до List<T>), ви можете покладатися на порядок елементів , що повертаються Select, Whereабо GroupBy. Це не відноситься до речей, які по своїй природі невпорядкованих як ToDictionaryабо Distinct.

З документації Enumerable.GroupBy :

Ці IGrouping<TKey, TElement>об'єкти отримані в порядку , заснованому на порядок елементів в джерелі , які збудували перший ключ кожного IGrouping<TKey, TElement>. Елементи в групуванні виводяться в порядку, в якому вони відображаються source.

Це не обов'язково стосується IQueryableметодів розширення (інших постачальників LINQ).

Джерело: Чи підтримують численні методи LINQ відносний порядок елементів?


2

Будь-яка "група за" або "упорядкувати за" можливо змінить порядок.


0

Питання тут конкретно стосується LINQ до об'єктів.

Якщо ви використовуєте LINQ-to-SQL, замість цього немає порядку, якщо ви не накладете його на щось подібне:

mysqlresult.OrderBy(e=>e.SomeColumn)

Якщо ви цього не зробите з LINQ-SQL, то порядок результатів може змінюватися між наступними запитами, навіть одних і тих же даних, що може спричинити помилку, що переривається.

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