在字符串数组中查找出现最频繁的字符串(基于C#)。

5

我有一个问题。有一个字符串

string [5] names = { "John", "Sam", "Harry", "Sam", "John" }

我需要找到数组中最常见的元素。我尝试使用以下方法:
string MostCommon = names.GroupBy(v => v)
    .OrderByDescending(g => g.Count())
    .First()
    .Key;

很遗憾,它只能找到一个元素,例如 MostCommon = John,但在这种情况下,我不仅需要 John,还需要 Sam。我该怎么办?也许在这种情况下不需要使用LINQ?


1
.First() 是你的问题。去掉 .First(),你将会得到不止一个结果,但是你将无法知道那个单一的 LINQ 语句具体计数是多少。 - dckuehn
可能是返回列表中最大重复项的重复问题。 - neverendingqs
3
不要,那个副本会造成OP想要避免的情况,只选取最大值里的第一个。它没有处理OP提到的情况,即可能有多个成员具有最大计数的情况。 - MakePeaceGreatAgain
4个回答

11

First 显然只会选择你的序列中第一个元素。但是你需要所有元素数量相等的组。所以先选择每个组的名称和数字,然后在之后排序。最后选择所有与第一组元素数量相同的组。

var groups = names.GroupBy(x => x)
    .Select(x => new { x.Key, Count = x.Count() })
    .OrderByDescending(x => x.Count);
int max = groups.First().Count;
var mostCommons = groups.Where(x => x.Count == max);

编辑:在上一个语句中,您也可以使用TakeWhile而不是Where,这将避免对groups列表中的最后元素进行不必要的比较,并在找到第一个组具有少于第一个组的元素时立即停止:

var mostCommons = groups.TakeWhile(x => x.Count == groups.First().Count);

这是一种非常低效的方式,它会执行很多次“groups”查询(涉及分组和排序)。至少将groups.First().Count放入最后一个查询之外的变量中,以使“groups”查询仅执行两次。尽管这仍然比Amit Hasan的方法差(不包括可能的非LINQ解决方案),但至少可以考虑不那么糟糕。 - Ivan Stoev
关于TakeWhileWhere更有优势的观点非常好。为了未来读者的利益,我认为您应该考虑添加一个使用TakeWhile的片段,甚至完全删除Where代码,因为TakeWhile是更好的选择。 - Lance U. Matthews
@BACON 已完成。 - MakePeaceGreatAgain
@IvanStoev 已按建议完成,但我怀疑是否有一种方法可以避免两次执行组语句,即使在您引用的答案中,“namegroup”也被迭代了两次。我们可以使用ToList强制立即评估,但到目前为止,这两个解决方案应该执行类似。我使用非LINQ方法进行了测试,它也将需要对原始序列进行两次迭代。 - MakePeaceGreatAgain
@HimBromBeere 正确。重点是不要执行N次 :) 使用Max + Where的方法更好,因为即使进行2次传递,它也是O(N)。而OrderByDescending + First是O(N * lg(N)),正如您所知。在这两种情况下,GroupBy操作都是O(N),因此我不计算它。 - Ivan Stoev

5
这可以按照以下步骤完成 -
 var nameGroup = names.GroupBy(x => x);
 var maxCount = nameGroup.Max(g => g.Count());
 var mostCommons = nameGroup.Where(x => x.Count() == maxCount).Select(x => x.Key).ToArray();

4

根据您找到的最常见名称的数量,将第一个LINQ与另一个类似的LINQ组合。

string MostCommon = names.GroupBy(v => v)
    .OrderByDescending(g => g.Count())
    .First();

int count = names.Where(x => x == MostCommon).Count();

var mostCommonList = names.GroupBy(v => v)
    .Where(g => g.Count() == count);

由于最后一行不存在.Key属性(以及在分配mostCommonList时的额外=),因此无法编译。不能假定只有一个“最常见的名称”,因此结果需要是IEnumerable<string>而不是string。因此,需要删除.Key(或者,更少用地,替换为.Select(v => v))。 - Lance U. Matthews
@BACON 复制/粘贴错误太多了,感谢你指出来。我已经修复了=并删除了两个.Key语句。我认为答案应该没有问题,因为.Where将返回一个可枚举对象,所以不需要.Select() - dckuehn

0
//With Dictionary
//This is more useful if you are looking to interview big companies otherwise use the 
 Linq option which is short and handy

public static int MaxOccurrenceOfWord(string[] words)
    {
        var counts = new Dictionary<string, int>();
        int occurrences = 0;
        foreach (var word in words)
        {
            int count;
            counts.TryGetValue(word, out count);
            count++;
             //Automatically replaces the entry if it exists;
            //no need to use 'Contains'
            counts[word] = count;
        }

        string mostCommonWord = null;
        foreach (var pair in counts)
        {
            if (pair.Value > occurrences)
            {
                occurrences = pair.Value;
                mostCommonWord = pair.Key;
            }
        }
        Console.WriteLine("The most common number is {0} and it appears {1} times",
            mostCommonWord, occurrences);

        return occurrences;

    }

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