Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting the number of times an item is repeated in C#

Tags:

c#

list

linq

I'm working on a program where the user has to input some sort of string in, and the program will store it in a List or an Array, then count how many times the item was repeated.

The three items that are repeated the most are then displayed in descending order of number of repetitions (1st has 10 repeats, 2nd has 9, 3rd has 8)

It sounded simple. Since I have no idea how many people will input a string in, I used a list, then followed this example:

foreach (string value in list.Distinct())  
{  
    System.Diagnostics.Debug.WriteLine("\"{0}\" occurs {1} time(s).", value, list.Count(v => v == value));  
}

But for some reason, .Distinct() does not appear after my list name. Did I do something wrong? Does this have something to do with my C#, which is NOT a C# 3.0? The example never mentioned anything about adding another reference, or the like.

Is there any other way I can do this?

like image 335
zack_falcon Avatar asked Jan 13 '12 14:01

zack_falcon


2 Answers

.Distinct() is a LINQ extension method. You need .NET 3.5+ to use it.

With that said, you don't need LINQ to do what you want. You could easily use other collection classes and a bit of arithmetic to get your results.

// Create a dictionary to hold key-value pairs of words and counts
IDictionary<string, int> counts = new Dictionary<string, int>();

// Iterate over each word in your list
foreach (string value in list)
{
    // Add the word as a key if it's not already in the dictionary, and
    // initialize the count for that word to 1, otherwise just increment
    // the count for an existing word
    if (!counts.ContainsKey(value))
        counts.Add(value, 1);
    else
        counts[value]++; 
}

// Loop through the dictionary results to print the results
foreach (string value in counts.Keys)
{
    System.Diagnostics.Debug
        .WriteLine("\"{0}\" occurs {1} time(s).", value, counts[value]);
}
like image 81
Cᴏʀʏ Avatar answered Sep 28 '22 03:09

Cᴏʀʏ


If you don't have C#3.0, then you don't have extension methods.

If you don't have .NET3.5 then you don't have any of the Linq extension methods to call as statics.

You can add your own for quite a few of these pieces of functionality:

public static IEnumerable<T> Distinct(IEnumerable<T> src, IEqualityComparer<T> eCmp)
{
  Dictionary<T, bool> fakeHashSet = new Dictionary<T, bool>(eCmp);
  //When I coded for 2.0 I had my own custom HashSet<T>, but that's overkill here
  bool dummy;
  foreach(T item in src)
  {
    if(!fakeHashSet.TryGetValue(item, out dummy))
    {
      fakeHashSet.Add(item, true);
      yield return item;
    }
  }
}
public static IEnumerable<T> Distinct(IEnumerable<T> src)
{
  return Distinct(src, EqualityComparer<T>.Default);
}
public delegate TResult Func<T, TResult>(T arg);//we don't even have this :(
public static int Count(IEnumerable<T> src, Func<T, bool> predicate)
{
  int c = 0;
  foreach(T item in src)
    if(predicate(item))
      ++c;
  return c;
}

Because we don't have extension syntax, or lamdbas, we have to call them like:

foreach (string value in Distinct(list))  
{  
    System.Diagnostics.Debug.WriteLine("\"{0}\" occurs {1} time(s).", value, Count(list, delegate(string v){return v == value;}));  
}

In all, we can implement much of Linq-to-objects with C#2.0, and many of us did, but it's nowhere near as friendly, and of course we can't map to other query providers.

In this case though, you'd be faster just doing the count directly:

Dictonary<string, int> counts = new Dictionary<string, int>();
foreach(string value in list)
{
  if(counts.ContainsKey(value))
    counts[value]++;
  else
    counts[value] = 1;
}
foreach(KeyValuePair<string, int> kvp in counts)
  System.Diagnostics.Debug.WriteLine("\"{0}\" occurs {1} time(s).", kvp.Key, kvp.Value));
like image 20
Jon Hanna Avatar answered Sep 28 '22 05:09

Jon Hanna