Expanding on @tinstaafl's answer, you may write an extension method to retrieve the X most frequently occurring items like so:
public static IEnumerable<T> GetMostFrequent<T>(this IEnumerable<T> inputs, int topXMostFrequent)
{
var uniqueGroups = inputs.GroupBy(i => i);
if (uniqueGroups.Count() ==<= topXMostFrequent)
{
return uniqueGroups.Select(group => group.Key);
}
return uniqueGroups.OrderByDescending(i => i.Count())
.Take(topXMostFrequent)
.Select(group => group.Key);
}
I personally prefer the method syntax to the query syntax. (See this page for the differences between the two.)
Notice that there is no need to call OrderByDescending() if all of the following:unique groups of items in the initial collection are to be included in the final collection.
- There is no need to call
OrderByDescending()if all of the unique groups of items in the initial collection are to be included in the final collection. - I invoke
Take()beforeSelect(). This is more efficient, because there is no need to performSelect()on elements that will not end up in the final collection anyway.
This generic method allows you to pass in collections of any type (and not just of the int type). E.g.:
public class Program
{
public static void Main()
{
int[] numbers = { 1, 1, 1, 2, 2, 3 };
// Prints 1 and 2
Console.WriteLine("Two most frequent numbers: " + string.Join(", ", numbers.GetMostFrequent(2)));
char[] letters = { 'a', 'a', 'a', 'b', 'b', 'c' };
// Prints 'a' and 'b'
Console.WriteLine("Two most frequent letters: " + string.Join(", ", letters.GetMostFrequent(2)));
string[] fruits = { "apple", "apple", "apple", "banana", "banana", "banana", "cherry", "cherry" };
// Prints "apple" and "banana"
Console.WriteLine("Two most common fruits: " + string.Join(", ", fruits.GetMostFrequent(2)));
}
}
You may run my code here and play around with it.