Je vais avoir un List<string>
comme:
List<String> list = new List<String>{"6","1","2","4","6","5","1"};
Je dois obtenir les éléments en double de la liste dans une nouvelle liste. J'utilise maintenant une boucle imbriquée for
pour le faire.
La list
résultante contiendra {"6","1"}
.
Y at-il une idée de faire cela en utilisant LINQ ou expressions lambda ?
var duplicates = lst.GroupBy(s => s)
.SelectMany(grp => grp.Skip(1));
Notez que ceci retournera tous les doublons. Ainsi, si vous voulez seulement savoir quels éléments sont dupliqués dans la liste des sources, vous pouvez appliquer Distinct
à la séquence résultante ou utiliser la solution fournie par Mark Byers.
Voici une façon de le faire:
List<String> duplicates = lst.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(g => g.Key)
.ToList();
La GroupBy
regroupe les éléments identiques, et la Where
filtre ceux qui n'apparaissent qu'une seule fois, en vous laissant uniquement les doublons.
Voici une autre option:
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));
Je sais que ce n'est pas la réponse à la question initiale, mais vous pouvez vous retrouver ici avec ce problème.
Si vous souhaitez que tous les éléments en double dans vos résultats, les opérations suivantes fonctionnent.
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
Dans ma situation, j'ai besoin de tous les doublons pour pouvoir les marquer dans l'interface utilisateur comme des erreurs.
J'ai écrit cette méthode d'extension basée sur la réponse de @ Lee au PO. Remarque , un paramètre par défaut a été utilisé (nécessitant C # 4.0). Cependant, un appel de méthode surchargé en C # 3.0 suffirait.
/// <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;
}
J'espère que cela aidera
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);
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);
}
J'essayais de résoudre le même problème avec une liste d'objets et je rencontrais des problèmes parce que j'essayais de reconditionner la liste des groupes dans la liste d'origine. Je suis donc venu en boucle à travers les groupes pour remballer la liste originale avec des éléments qui ont des doublons.
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;
}
Toutes les solutions mentionnées jusqu'à présent effectuent un GroupBy. Même si je n'ai besoin que du premier doublon, tous les éléments des collections sont énumérés au moins une fois.
La fonction d'extension suivante arrête d'énumérer dès qu'un duplicata a été trouvé. Cela continue si un prochain duplicata est demandé.
Comme toujours dans LINQ, il existe deux versions, l’une avec IEqualityComparer et l’autre sans elle.
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;
}
}
}
Usage:
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);
Pour toutes ces instructions linq, la collection est uniquement analysée jusqu'à ce que les éléments demandés soient trouvés. Le reste de la séquence n'est pas interprété.
IMHO c'est un gain d'efficacité à prendre en compte.