Как получить дубликаты элементов из списка с помощью LINQ?
у меня есть List<string> как:
List<String> list = new List<String>{"6","1","2","4","6","5","1"};
мне нужно получить дубликаты элементов в списке в новый список. Теперь я использую вложенный for петли для этого.
в результате list содержит {"6","1"}.
есть идея сделать это с помощью LINQ или лямбда-выражения?
9 ответов:
var duplicates = lst.GroupBy(s => s) .SelectMany(grp => grp.Skip(1));обратите внимание, что это вернет все дубликаты, так что если вы хотите знать, какие элементы повторяются в списке источников, вы можете использовать
Distinctк полученной последовательности или использовать решение, данное Марком Байерсом.
вот один из способов сделать это:
List<String> duplicates = lst.GroupBy(x => x) .Where(g => g.Count() > 1) .Select(g => g.Key) .ToList();The
GroupByгруппирует элементы, которые являются одинаковыми вместе, иWhereотфильтровывает те, которые появляются только один раз, оставляя вам только дубликаты.
вот еще вариант:
var list = new List<string> { "6", "1", "2", "4", "6", "5", "1" }; var set = new HashSet<string>(); var duplicates = list.Where(x => !set.Add(x));
Я знаю, что это не ответ на исходный вопрос, но вы можете найти себе здесь с этой проблемой.
Если вы хотите, чтобы все повторяющиеся элементы в ваши результаты, следующие работы.
var duplicates = list .GroupBy( x => x ) // group matching items .Where( g => g.Skip(1).Any() ) // where the group contains more than one item .SelectMany( g => g ); // re-expand the groups with more than one itemв моей ситуации мне нужны все дубликаты, чтобы я мог пометить их в пользовательском интерфейсе как ошибки.
Я написал этот метод расширения, основанный на ответе @Lee на OP.Примечание, использовался параметр по умолчанию (требуется C# 4.0). Однако перегруженного вызова метода в C# 3.0 будет достаточно.
/// <summary> /// Method that returns all the duplicates (distinct) in the collection. /// </summary> /// <typeparam name="T">The type of the collection.</typeparam> /// <param name="source">The source collection to detect for duplicates</param> /// <param name="distinct">Specify <b>true</b> to only return distinct elements.</param> /// <returns>A distinct list of duplicates found in the source collection.</returns> /// <remarks>This is an extension method to IEnumerable<T></remarks> public static IEnumerable<T> Duplicates<T> (this IEnumerable<T> source, bool distinct = true) { if (source == null) { throw new ArgumentNullException("source"); } // select the elements that are repeated IEnumerable<T> result = source.GroupBy(a => a).SelectMany(a => a.Skip(1)); // distinct? if (distinct == true) { // deferred execution helps us here result = result.Distinct(); } return result; }
List<String> list = new List<String> { "6", "1", "2", "4", "6", "5", "1" }; var q = from s in list group s by s into g where g.Count() > 1 select g.First(); foreach (var item in q) { Console.WriteLine(item); }
надеюсь, что это поможет
int[] listOfItems = new[] { 4, 2, 3, 1, 6, 4, 3 }; var duplicates = listOfItems .GroupBy(i => i) .Where(g => g.Count() > 1) .Select(g => g.Key); foreach (var d in duplicates) Console.WriteLine(d);
Я пытался решить то же самое со списком объектов и возникли проблемы, потому что я пытался перепаковать список групп в исходный список. Поэтому я придумал цикл через группы, чтобы переупаковать исходный список с элементами, которые имеют дубликаты.
public List<MediaFileInfo> GetDuplicatePictures() { List<MediaFileInfo> dupes = new List<MediaFileInfo>(); var grpDupes = from f in _fileRepo group f by f.Length into grps where grps.Count() >1 select grps; foreach (var item in grpDupes) { foreach (var thing in item) { dupes.Add(thing); } } return dupes; }
все упомянутые решения до сих пор выполняют GroupBy. Даже если мне нужен только первый дубликат, все элементы коллекций перечисляются хотя бы один раз.
следующая функция расширения прекращает перечисление, как только был найден дубликат. Он продолжается, если запрашивается следующий дубликат.
Как всегда в LINQ есть две версии, одна с IEqualityComparer и одна без него.
public static IEnumerable<TSource> ExtractDuplicates(this IEnumerable<TSource> source) { return source.ExtractDuplicates(null); } public static IEnumerable<TSource> ExtractDuplicates(this IEnumerable<TSource source, IEqualityComparer<TSource> comparer); { if (source == null) throw new ArgumentNullException(nameof(source)); if (comparer == null) comparer = EqualityCompare<TSource>.Default; HashSet<TSource> foundElements = new HashSet<TSource>(comparer); foreach (TSource sourceItem in source) { if (!foundElements.Contains(sourceItem)) { // we've not seen this sourceItem before. Add to the foundElements foundElements.Add(sourceItem); } else { // we've seen this item before. It is a duplicate! yield return sourceItem; } } }использование:
IEnumerable<MyClass> myObjects = ... // check if has duplicates: bool hasDuplicates = myObjects.ExtractDuplicates().Any(); // or find the first three duplicates: IEnumerable<MyClass> first3Duplicates = myObjects.ExtractDuplicates().Take(3) // or find the first 5 duplicates that have a Name = "MyName" IEnumerable<MyClass> myNameDuplicates = myObjects.ExtractDuplicates() .Where(duplicate => duplicate.Name == "MyName") .Take(5);для всех эти операторы linq коллекция анализируется только до тех пор, пока не будут найдены запрошенные элементы. Остальная часть последовательности не интерпретируется.
IMHO это повышение эффективности, чтобы рассмотреть.
Comments