Сохранение заказа с помощью LINQ

361

Я использую инструкции LINQ to Objects для упорядоченного массива. Какие операции не следует делать, чтобы убедиться, что порядок массива не изменился?

Матье Дурут
источник

Ответы:

645

Я исследовал методы System.Linq.Enumerable , отбрасывая все, которые возвращали не IEnumerable результаты. Я проверил замечания каждого из них, чтобы определить, как порядок результата будет отличаться от порядка источника.

Сохраняет порядок абсолютно. Вы можете сопоставить исходный элемент по индексу с результирующим элементом

  • AsEnumerable
  • В ролях
  • Concat
  • Выбрать
  • ToArray
  • К списку

Сохраняет заказ. Элементы фильтруются или добавляются, но не переупорядочиваются.

  • отчетливый
  • Кроме
  • Пересечь
  • OfType
  • Prepend (новое в .net 4.7.1)
  • Пропускать
  • SkipWhile
  • принимать
  • TakeWhile
  • куда
  • Zip (новый в .net 4)

Уничтожает порядок - мы не знаем, в каком порядке следует ожидать результатов.

  • ToDictionary
  • ToLookup

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

  • Сортировать по
  • OrderByDescending
  • Обеспечить регресс
  • ThenBy
  • ThenByDescending

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

  • GroupBy - объекты IGrouping выдаются в порядке, основанном на порядке элементов в источнике, который создал первый ключ каждой IGrouping. Элементы в группировке приводятся в порядке их появления в источнике.
  • GroupJoin - GroupJoin сохраняет порядок элементов external, а для каждого элемента external - порядок соответствия элементов из inner.
  • Join - сохраняет порядок элементов external, а для каждого из этих элементов порядок совпадающих элементов inner.
  • SelectMany - для каждого элемента источника вызывается селектор и возвращается последовательность значений.
  • Объединение - при перечислении объекта, возвращаемого этим методом, объединение перечисляет первое и второе в этом порядке и возвращает каждый элемент, который еще не был получен.

Изменить: я переместил Distinct в сохранении порядка на основе этой реализации .

    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
На самом деле, я думаю, что Distinct сохраняет первоначальный (первый найденный) порядок - поэтому {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 to Objects. В Linq to Sql, Different () помещает ключевое слово Different в сгенерированный sql, и упорядочение из sql не гарантируется. Мне было бы интересно увидеть реализацию различных для linq объектов, которая не сохраняет порядок и является более эффективной, чем та, которая сохраняет порядок. Например, вы можете использовать весь ввод и поместить его в хэш-набор, а затем получить значения, перечислив хэш-набор (убыточный порядок), но это еще хуже. Так что да, я не против презрев документации каждый сейчас и потом :)
дан
4
Может быть, документация (для Distinctметода) просто означает «не отсортировано», а не «в непредсказуемом порядке». Я бы сказал, Distinctотносится к категории фильтрации выше, как и Where.
Джеппе Стиг Нильсен
34

Вы на самом деле говорите о SQL или о массивах? Иными словами, вы используете LINQ to SQL или LINQ to Objects?

Операторы LINQ to Objects фактически не изменяют свой исходный источник данных - они создают последовательности, которые эффективно поддерживаются источником данных. Единственными операциями, которые изменяют порядок, являются OrderBy / OrderByDescending / ThenBy / ThenByDescending - и даже тогда они стабильны для одинаково упорядоченных элементов. Конечно, многие операции отфильтруют некоторые элементы, но возвращаемые элементы будут в том же порядке.

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

Джон Скит
источник
так как OrderBy является стабильной сортировкой, то: seq.OrderBy (_ => _.Key) будет располагать элементы в том же порядке, что и seq.GroupBy (_ => _.Key) .SelectMany (_ => _ ). Это верно?
DMG
1
@ DMG: нет, не будет. Только GroupByзатем SelectManyдадут результаты , сгруппированные по ключу, но не в порядке возрастания ключа ... он даст их в том порядке , в котором ключи первоначально имели место.
Джон Скит
Вы говорите, что LINQ to SQL не сохраняет порядок?
симбионт
@symbiont: Во многих операций SQL есть это не хорошо определены для того , чтобы начать с. По сути, я пытаюсь давать обещания только о вещах, которые я могу гарантировать, таких как LINQ to Objects.
Джон Скит
@JonSkeet Если я использую OrderBy, это гарантирует, что 'n' объекты с одинаковым ключом сохранят свою первоначальную последовательность, за исключением того, что они все вместе. т.е. list<x> {a b c d e f g}если в c, d, e все ключи имеют одинаковый ключ, то результирующая последовательность будет содержать c, d, e рядом друг с другом И в порядке c, d, e. Я не могу найти категорический ответ на основе MS.
Паулюрий
7

Если вы работаете с массивом, похоже, что вы используете LINQ-to-Objects, а не SQL; Можешь подтвердить? Большинство операций LINQ ничего не упорядочивают (выходные данные будут в том же порядке, что и входные), поэтому не применяйте другую сортировку (OrderBy [Descending] / ThenBy [Descending]).

[править: как выразился Джон более четко; LINQ обычно создает новую последовательность, оставляя исходные данные в покое]

Обратите внимание, что Dictionary<,>вставка данных в (ToDictionary) будет шифровать данные, так как словарь не учитывает какой-либо конкретный порядок сортировки.

Но наиболее распространенные вещи (выбрать, где, пропустить, взять) должны быть в порядке.

Марк Гравелл
источник
Если я не ошибаюсь, ToDictionary()просто не дает никаких обещаний о порядке, но на практике поддерживает порядок ввода (пока вы не удалите что-либо из него). Я не говорю, чтобы полагаться на это, но «карабкаться» кажется неточным.
Тимо
4

Я нашел отличный ответ в аналогичном вопросе, который ссылается на официальную документацию. Процитирую это:

Для Enumerableметодов (LINQ к объектам, который относится к List<T>), вы можете полагаться на порядок элементов , возвращаемых Select, Whereили GroupBy. Это не относится к вещам, которые по своей природе неупорядочены, как ToDictionaryили Distinct.

Из документации Enumerable.GroupBy :

Эти IGrouping<TKey, TElement>объекты получены в порядке , основанном на порядок элементов в источнике , которые произвели первый ключ каждого IGrouping<TKey, TElement>. Элементы в группировке приводятся в порядке их появления source.

Это не обязательно верно для IQueryableметодов расширения (другие поставщики LINQ).

Источник: Поддерживают ли перечисляемые методы LINQ относительный порядок элементов?

Кертис Яллоп
источник
2

Любое 'group by' или 'order by' может изменить порядок.

leppie
источник
0

Вопрос здесь конкретно относится к LINQ-to-Objects.

Если вы используете LINQ-to-SQL вместо этого, там нет порядка, если вы не навязываете что-то вроде:

mysqlresult.OrderBy(e=>e.SomeColumn)

Если вы не сделаете это с LINQ-to-SQL, то порядок результатов может отличаться для последующих запросов, даже для тех же данных, что может вызвать прерывистую ошибку.

Эндрю Пэйт
источник