Я хочу удалить дубликаты из списка, не меняя порядок уникальных элементов в списке.
Джон Скит и другие предложили использовать следующее:
list = list.Distinct().ToList();
Справка:
Гарантируется ли, что порядок уникальных элементов будет таким же, как и раньше? Если да, дайте ссылку, подтверждающую это, поскольку я ничего не нашел в документации.
c#
list
duplicates
Нитеш
источник
источник
Ответы:
Это не гарантировано, но это наиболее очевидная реализация. Было бы трудно реализовать потоковым способом (то есть таким, чтобы он возвращал результаты как можно скорее, прочитав как можно меньше), не возвращая их по порядку.
Возможно, вы захотите прочитать мое сообщение в блоге о реализации Distinct () в Edulinq .
Обратите внимание, что даже если бы это было гарантировано для LINQ to Objects (что лично я думаю, что так и должно быть), это ничего не значило бы для других поставщиков LINQ, таких как LINQ to SQL.
Уровень гарантий, предоставляемых в LINQ to Objects, иногда немного непоследователен, ИМО. Некоторые оптимизации задокументированы, другие нет. Черт возьми, часть документации совершенно неверна .
источник
В .NET Framework 3.5 дизассемблирование CIL реализации Linq-to-Objects
Distinct()
показывает, что порядок элементов сохраняется, однако это не документированное поведение.Я провел небольшое расследование с Reflector. После дизассемблирования System.Core.dll, Version = 3.5.0.0 вы можете увидеть, что Distinct () - это метод расширения, который выглядит так:
public static class Emunmerable { public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source) { if (source == null) throw new ArgumentNullException("source"); return DistinctIterator<TSource>(source, null); } }
Итак, интересен DistinctIterator, который реализует IEnumerable и IEnumerator. Вот упрощенная (удаленные goto и ярлыки) реализация этого IEnumerator:
private sealed class DistinctIterator<TSource> : IEnumerable<TSource>, IEnumerable, IEnumerator<TSource>, IEnumerator, IDisposable { private bool _enumeratingStarted; private IEnumerator<TSource> _sourceListEnumerator; public IEnumerable<TSource> _source; private HashSet<TSource> _hashSet; private TSource _current; private bool MoveNext() { if (!_enumeratingStarted) { _sourceListEnumerator = _source.GetEnumerator(); _hashSet = new HashSet<TSource>(); _enumeratingStarted = true; } while(_sourceListEnumerator.MoveNext()) { TSource element = _sourceListEnumerator.Current; if (!_hashSet.Add(element)) continue; _current = element; return true; } return false; } void IEnumerator.Reset() { throw new NotSupportedException(); } TSource IEnumerator<TSource>.Current { get { return _current; } } object IEnumerator.Current { get { return _current; } } }
Как видите, перечисление идет в порядке, заданном исходным перечислением (список, по которому мы вызываем
Distinct
).Hashset
используется только для определения того, вернули ли мы такой элемент или нет. Если нет - возвращаем, иначе продолжаем перечисление по источнику.Таким образом, гарантируется, что
Distinct()
элементы будут возвращены точно в том же порядке , который предоставляется коллекцией, к которой был применен Distinct.источник
Согласно документации последовательность не упорядочена.
источник
Да , Enumerable.Distinct сохраняет порядок. Если предположить, что метод является ленивым, «выдает разные значения, как только они видны», он следует автоматически. Подумай об этом.
В исходных .NET Reference подтверждает. Он возвращает подпоследовательность, первый элемент в каждом классе эквивалентности.
foreach (TSource element in source) if (set.Add(element)) yield return element;
Реализация .NET Core аналогична.
К сожалению, документация для Enumerable.Distinct запуталась в этом вопросе:
Я могу только представить, что они имеют в виду «последовательность результатов не отсортирована». Вы можете реализовать Distinct путем предварительной сортировки, а затем сравнения каждого элемента с предыдущим, но это не будет ленивым, как определено выше.
источник
dbQuery.OrderBy(...).Distinct().ToList()
не возвращался список в порядке, заданном порядком по предикату - удаление Distinct (которое оказалось избыточным) исправило ошибку в моем случаеПо умолчанию при использовании Distinct оператор linq использует метод Equals, но вы можете использовать свой собственный
IEqualityComparer<T>
объект, чтобы указать, когда два объекта равны, с реализацией пользовательской логикиGetHashCode
иEquals
методом. Помните, что:GetHashCode
не следует использовать тяжелое сравнение процессоров (например, использовать только некоторые очевидные базовые проверки) и использовать его в качестве первого, чтобы указать, действительно ли два объекта отличаются (если возвращен другой хеш-код) или потенциально одинаковы (тот же хеш-код). В этом последнем случае, когда два объекта имеют один и тот же хэш-код, структура будет проверять, используя метод Equals в качестве окончательного решения о равенстве данных объектов.После того, как вы есть ,
MyType
и черезMyTypeEqualityComparer
классы следуют код не обеспечивает последовательность сохранять порядок:var cmp = new MyTypeEqualityComparer(); var lst = new List<MyType>(); // add some to lst var q = lst.Distinct(cmp);
В следующей научной библиотеке я реализовал метод расширения, чтобы гарантировать, что набор Vector3D поддерживает порядок при использовании определенного метода расширения.
DistinctKeepOrder
:соответствующий код:
/// <summary> /// support class for DistinctKeepOrder extension /// </summary> public class Vector3DWithOrder { public int Order { get; private set; } public Vector3D Vector { get; private set; } public Vector3DWithOrder(Vector3D v, int order) { Vector = v; Order = order; } } public class Vector3DWithOrderEqualityComparer : IEqualityComparer<Vector3DWithOrder> { Vector3DEqualityComparer cmp; public Vector3DWithOrderEqualityComparer(Vector3DEqualityComparer _cmp) { cmp = _cmp; } public bool Equals(Vector3DWithOrder x, Vector3DWithOrder y) { return cmp.Equals(x.Vector, y.Vector); } public int GetHashCode(Vector3DWithOrder obj) { return cmp.GetHashCode(obj.Vector); } }
Вкратце
Vector3DWithOrder
инкапсулируйте тип и целое число порядка, аVector3DWithOrderEqualityComparer
инкапсулирует компаратор исходного типа.и это помощник метода для обеспечения порядка
/// <summary> /// retrieve distinct of given vector set ensuring to maintain given order /// </summary> public static IEnumerable<Vector3D> DistinctKeepOrder(this IEnumerable<Vector3D> vectors, Vector3DEqualityComparer cmp) { var ocmp = new Vector3DWithOrderEqualityComparer(cmp); return vectors .Select((w, i) => new Vector3DWithOrder(w, i)) .Distinct(ocmp) .OrderBy(w => w.Order) .Select(w => w.Vector); }
Примечание : дальнейшие исследования могут позволить найти более общий (использование интерфейсов) и оптимизированный способ (без инкапсуляции объекта).
источник
Это сильно зависит от вашего linq-провайдера. В Linq2Objects вы можете использовать внутренний исходный код для
Distinct
, что заставляет предположить, что исходный порядок сохраняется.Однако для других провайдеров, которые разрешают, например, некоторый вид SQL, это не обязательно так, поскольку
ORDER BY
-statement обычно идет после любого агрегирования (например,Distinct
). Итак, если ваш код такой:это переводится в SQL примерно так:
Это, очевидно, сначала группирует ваши данные, а затем сортирует их. Теперь вы застряли на собственной логике СУБД о том, как это выполнить. В некоторых СУБД это даже не разрешено. Представьте себе следующие данные:
mycol anothercol 1 2 1 1 1 3 2 1 2 3
при выполнении
myArr.OrderBy(x => x.anothercol).GroupBy(x => x.mycol)
предполагаем следующий результат:mycol anothercol 1 1 2 1
Но СУБД может агрегировать столбец anothercol таким образом, чтобы всегда использовалось значение первой строки, в результате чего получались следующие данные:
mycol anothercol 1 2 2 1
что после заказа приведет к такому:
mycol anothercol 2 1 1 2
Это похоже на следующее:
SELECT mycol, First(anothercol) from mytable group by mycol order by anothercol;
что является полностью обратным порядком, чем вы ожидали.
Вы видите, что план выполнения может варьироваться в зависимости от того, что является основным поставщиком. Вот почему в документации нет никаких гарантий.
источник