J'ai un List<string>
qui a des mots dupliqués. Je dois trouver tous les mots qui sont des doublons.
Toute astuce pour les obtenir tous?
Dans .NET Framework 3.5 et versions ultérieures, vous pouvez utiliser Enumerable.GroupBy
qui retourne un énumérable d'énumérables de clés dupliquées, puis filtre tous les énumérables ayant un nombre de <= 1, puis sélectionne leurs clés pour revenir à un seul énumérable:
var duplicateKeys = list.GroupBy(x => x)
.Where(group => group.Count() > 1)
.Select(group => group.Key);
Si vous utilisez LINQ, vous pouvez utiliser la requête suivante:
var duplicateItems = from x in list
group x by x into grouped
where grouped.Count() > 1
select grouped.Key;
ou, si vous le préférez sans le sucre syntaxique:
var duplicateItems = list.GroupBy(x => x).Where(x => x.Count() > 1).Select(x => x.Key);
Cela regroupe tous les éléments identiques, puis les filtre uniquement aux groupes de plusieurs éléments. Enfin, il sélectionne uniquement la clé dans ces groupes, car vous n’avez pas besoin du décompte.
Si vous préférez ne pas utiliser LINQ, vous pouvez utiliser cette méthode d'extension:
public void SomeMethod {
var duplicateItems = list.GetDuplicates();
…
}
public static IEnumerable<T> GetDuplicates<T>(this IEnumerable<T> source) {
HashSet<T> itemsSeen = new HashSet<T>();
HashSet<T> itemsYielded = new HashSet<T>();
foreach (T item in source) {
if (!itemsSeen.Add(item)) {
if (itemsYielded.Add(item)) {
yield return item;
}
}
}
}
Cela permet de garder trace des éléments qu’il a vus et cédés. S'il n'a pas encore vu d'élément, il l'ajoute à la liste des éléments visibles, sinon il l'ignore. S'il n'a pas déjà cédé un élément, il le cède, sinon il l'ignore.
et sans le LINQ:
string[] ss = {"1","1","1"};
var myList = new List<string>();
var duplicates = new List<string>();
foreach (var s in ss)
{
if (!myList.Contains(s))
myList.Add(s);
else
duplicates.Add(s);
}
// show list without duplicates
foreach (var s in myList)
Console.WriteLine(s);
// show duplicates list
foreach (var s in duplicates)
Console.WriteLine(s);
Si vous recherchez une méthode plus générique:
public static List<U> FindDuplicates<T, U>(this List<T> list, Func<T, U> keySelector)
{
return list.GroupBy(keySelector)
.Where(group => group.Count() > 1)
.Select(group => group.Key).ToList();
}
EDIT: Voici un exemple:
public class Person {
public string Name {get;set;}
public int Age {get;set;}
}
List<Person> list = new List<Person>() { new Person() { Name = "John", Age = 22 }, new Person() { Name = "John", Age = 30 }, new Person() { Name = "Jack", Age = 30 } };
var duplicateNames = list.FindDuplicates(p => p.Name);
var duplicateAges = list.FindDuplicates(p => p.Age);
foreach(var dupName in duplicateNames) {
Console.WriteLine(dupName); // Will print out John
}
foreach(var dupAge in duplicateAges) {
Console.WriteLine(dupAge); // Will print out 30
}
Utiliser LINQ, bien sûr. Le code ci-dessous vous donnerait un dictionnaire d’élément item en tant que chaîne, ainsi que le nombre de chaque élément de votre liste de sources.
var item2ItemCount = list.GroupBy(item => item).ToDictionary(x=>x.Key,x=>x.Count());
Pour ce que ça vaut, voici mon chemin:
List<string> list = new List<string>(new string[] { "cat", "Dog", "parrot", "dog", "parrot", "goat", "parrot", "horse", "goat" });
Dictionary<string, int> wordCount = new Dictionary<string, int>();
//count them all:
list.ForEach(Word =>
{
string key = Word.ToLower();
if (!wordCount.ContainsKey(key))
wordCount.Add(key, 0);
wordCount[key]++;
});
//remove words appearing only once:
wordCount.Keys.ToList().FindAll(Word => wordCount[Word] == 1).ForEach(key => wordCount.Remove(key));
Console.WriteLine(string.Format("Found {0} duplicates in the list:", wordCount.Count));
wordCount.Keys.ToList().ForEach(key => Console.WriteLine(string.Format("{0} appears {1} times", key, wordCount[key])));
Je suppose que chaque chaîne de votre liste contient plusieurs mots, laissez-moi savoir si c'est inexact.
List<string> list = File.RealAllLines("foobar.txt").ToList();
var words = from line in list
from Word in line.Split(new[] { ' ', ';', ',', '.', ':', '(', ')' }, StringSplitOptions.RemoveEmptyEntries)
select Word;
var duplicateWords = from w in words
group w by w.ToLower() into g
where g.Count() > 1
select new
{
Word = g.Key,
Count = g.Count()
}
J'utilise une méthode comme celle pour vérifier les entrées en double dans une chaîne:
public static IEnumerable<string> CheckForDuplicated(IEnumerable<string> listString)
{
List<string> duplicateKeys = new List<string>();
List<string> notDuplicateKeys = new List<string>();
foreach (var text in listString)
{
if (notDuplicateKeys.Contains(text))
{
duplicateKeys.Add(text);
}
else
{
notDuplicateKeys.Add(text);
}
}
return duplicateKeys;
}
Ce n’est peut-être pas le moyen le plus court ou le plus élégant, mais je pense que cela est très lisible.
lblrepeated.Text = "";
string value = txtInput.Text;
char[] arr = value.ToCharArray();
char[] crr=new char[1];
int count1 = 0;
for (int i = 0; i < arr.Length; i++)
{
int count = 0;
char letter=arr[i];
for (int j = 0; j < arr.Length; j++)
{
char letter3 = arr[j];
if (letter == letter3)
{
count++;
}
}
if (count1 < count)
{
Array.Resize<char>(ref crr,0);
int count2 = 0;
for(int l = 0;l < crr.Length;l++)
{
if (crr[l] == letter)
count2++;
}
if (count2 == 0)
{
Array.Resize<char>(ref crr, crr.Length + 1);
crr[crr.Length-1] = letter;
}
count1 = count;
}
else if (count1 == count)
{
int count2 = 0;
for (int l = 0; l < crr.Length; l++)
{
if (crr[l] == letter)
count2++;
}
if (count2 == 0)
{
Array.Resize<char>(ref crr, crr.Length + 1);
crr[crr.Length - 1] = letter;
}
count1 = count;
}
}
for (int k = 0; k < crr.Length; k++)
lblrepeated.Text = lblrepeated.Text + crr[k] + count1.ToString();