如何从List<string>中找到所有的重复项?

126

我有一个List<string>,其中有一些单词是重复的。我需要找到所有重复的单词。

有什么技巧可以获取它们吗?


3
@nawfal,我没有看到这里提到 Linq... - rene
9个回答

225

在 .NET framework 3.5 及以上版本中,可以使用 Enumerable.GroupBy 方法返回一个重复键的枚举,再筛选出任何 Count 值小于或等于1 的枚举,然后选择它们的键以返回单个枚举:

var duplicateKeys = list.GroupBy(x => x)
                        .Where(group => group.Count() > 1)
                        .Select(group => group.Key);

3
这将按值对所有行进行分组,而不是重复项...您仍然需要通过“Count() > 1”进行过滤。此外,我理解问题的方式是,每行包含几个单词,而问答者想要重复的单词(但也许我误解了问题)。 - Thomas Levesque
34
@Thomas:是的,代码还不完整,那只是第一步。接下来,如果他只想要重复项,可以使用Where,像这样:list.GroupBy(x => x).Where(group => group.Count() > 1).Select(group => Group.Key).ToList() - Giuseppe Ottaviano
1
不需要计算所有项来检查是否有多个:.Where(group => group.Skip(1).Any()) - Russell Horwood
太酷了!它起作用了。 - NidhinSPradeep

36
如果您正在使用LINQ,您可以使用以下查询:
var duplicateItems = from x in list
                     group x by x into grouped
                     where grouped.Count() > 1
                     select grouped.Key;

或者,如果您更喜欢没有语法糖的方式:
var duplicateItems = list.GroupBy(x => x).Where(x => x.Count() > 1).Select(x => x.Key);

这将把所有相同的元素分组,然后过滤掉只有一个元素的组。最后,它只选择那些组的键,因为您不需要计数。

如果您不想使用LINQ,可以使用此扩展方法:

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;
            }
        }
    }
}

这个代码会记录它已经见过和生成的项目。如果它之前没有看到过某个项目,它会将其添加到已经看到的列表中,否则就忽略它。如果它之前没有生成过某个项目,它会生成它,否则就忽略它。


+1 for HashSet。我认为在内部(由于排序等原因),它执行更快的搜索。 - Davi Fiamenghi
1
PMSL @ 'without the syntactic sugar'. 这确实是LINQ方法语法的另一个名称。 - OpcodePete
如果您不需要跟踪“itemsSeen”,则可以简化循环如下:foreach (T item in source) { if (!itemsYielded.Add(item)) { yield return item; } } - Shawinder Sekhon

20

并且不使用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);

为什么可以不带开销声明,还要使用 var? - BenKoshy
11
var 中不存在“overhead”。 - Cody Gray

14
如果您正在寻找一种更通用的方法:
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();
    }

编辑:这里有一个例子:

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
}

请问<U>和<T,U>是什么意思?我们需要包含一些命名空间吗?还是需要用正确的对象类型替换它们? - Irshad Babar
1
T和U是方法定义中的通用类型。您可以在调用方法时替换它们,或者像我的示例一样推断出它们:list.FindDuplicates(p => p.Name): T -> 人; U -> 字符串; list.FindDuplicates(p => p.Age): T -> 人; U -> 整数; - Mauricio Ramalho

5
使用LINQ,当然。 下面的代码将为您提供一个项目字符串字典,并计算源列表中每个项目的数量。
var item2ItemCount = list.GroupBy(item => item).ToDictionary(x=>x.Key,x=>x.Count());

4

就我来说,以下是我的方法:

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])));

3
我假设你的列表中每个字符串都包含多个单词,如果这不正确,请让我知道。
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()
                     }

2

我使用以下方法检查字符串中的重复条目:

Original Answer翻译成"最初的回答"

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;
}

也许这不是最简洁或最优雅的方式,但我认为它非常易读。Original Answer翻译成"最初的回答"。

0
    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();

9
我也能要一些意大利面吗? - Th3B0Y

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接