5

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?

Kamil Budziewski
  • 22,699
  • 14
  • 85
  • 105
zack_falcon
  • 4,186
  • 20
  • 62
  • 108
  • If you're using anything older than 3.0, you won't have access to the Distinct extension method. – Justin Niessner Jan 13 '12 at 14:42
  • What is your expected result, and what result are you seeing instead? – jrummell Jan 13 '12 at 14:43
  • Just .NET 2.0. Unfortunately. Sorry I forgot to mention that. – zack_falcon Jan 13 '12 at 14:54
  • list of what? Most likely you are applying distinct on an object that doesn't distinguish equality... You can solve this by using IEquatable or IEqualityComparer http://stackoverflow.com/questions/1365748/distinct-not-working-with-linq-to-objects – Jaider Nov 30 '12 at 21:29

5 Answers5

12

.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]);
}
Cᴏʀʏ
  • 105,112
  • 20
  • 162
  • 194
  • @ThomasLevesque: Thanks, I had that originally but second guessed myself. – Cᴏʀʏ Jan 13 '12 at 14:45
  • and reference to system.core.dll – Ilia G Jan 13 '12 at 14:45
  • Oh man. So do I just download the latest version of .NET? Or will that mess something up my Visual Studio 2005? – zack_falcon Jan 13 '12 at 14:54
  • @zack_falcon: It won't mess up VS2005, but you're going to have to upgrade to VS2008. You can download Microsoft .NET Framework 3.5 SP1 from MSDN. – Cᴏʀʏ Jan 13 '12 at 15:04
  • Well, there it goes. That's what I'm afraid of. What other collection classes would you suggest? I can think up of a For Loop solution, but it might not be efficient. – zack_falcon Jan 13 '12 at 15:10
  • `.Distinct()` from LINQ has to iterate over your elements anyway -- writing your own for-loop isn't going to be any less efficient. See my edit to the post for an example. – Cᴏʀʏ Jan 13 '12 at 15:11
  • The non LINQ approach is actually faster since you iterate your collection and your dictionary once each instead of iterating over your collection again and again. – Nuffin Jan 13 '12 at 15:26
  • Zack, you could also upgrade to SharpDeveop4.1, which has a somewhat nicer price. – Jon Hanna Jan 13 '12 at 15:55
2

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));
Jon Hanna
  • 110,372
  • 10
  • 146
  • 251
0

Which version of the .NET Framework are you using? The minimum framework version that includes this method is .NET 3.5.

If you're using .NET 3.5 or later, do you have a using System.Linq; statement in the code file? If not, that is probably the reason why the method does not appear to be accessible. The Distinct method is actually an extension method defined in the Enumerable class, which is in the System.Linq namespace.

Daniel Pratt
  • 12,007
  • 2
  • 44
  • 61
0

You have to use at least C# 3.0 and .NET 3.5, and remember about adding using System.Linq;

psur
  • 4,400
  • 26
  • 36
0

As with your existing solution, you will require .NET 3.5 or greater for this to work, but here it is anyway;

var query = list.GroupBy(x => x).OrderByDescending(x => x.Count()).Take(3);

foreach (var result in query)
{
    Console.WriteLine("\"{0}\" occurs {1} time(s).", result.Key, result.Count());
}
Chris McAtackney
  • 5,192
  • 8
  • 45
  • 69