views:

99

answers:

7

I have a StringCollection object with 5 words in them. 3 of them are duplicate words. I am trying to create a LINQ query that will count how many unique words are in the collection and output them to to the console. So, for example, if my StringCollection has 'House', 'Car', 'House','Dog', 'Cat', then it should output like this:

House --> 2
Car --> 1
Dog --> 1
Cat --> 1

Any ideas on how to create a LINQ query to do this?

+3  A: 
var xs = new StringCollection { "House", "Car", "House", "Dog", "Cat" };

foreach (var g in xs.Cast<string>()
                    .GroupBy(x => x, StringComparer.CurrentCultureIgnoreCase))
{
    Console.WriteLine("{0}: {1}", g.Key, g.Count());
}
dtb
A: 
foreach(var g in input.GroupBy(i => i.ToLower()).Select(i => new {Word = i.Key, Count = i.Count()})
{
  Console.WriteLine(string.Format("{0} -> {1}", g.Word, g.Count));
}
Femaref
No! Using ToLower for case-insensitive string comparison == facefalm.jpg
dtb
A: 

It should be as simple as:

Console.WriteLine(stringCollection.Distinct().Count());
Guffa
That will show the unique strings, but not the count of duplicated ones.
Ray Hayes
This will count the number of unique strings. so it would return `4`
Matthew Whited
Guffa
+6  A: 

Try the following

var res = from word in col.Cast<string>()
          group word by word into g
          select new { Word = g.Key, Count = g.Count() };
JaredPar
+1 But this doesn't ignores case.
Taylor Leese
I believe a simple change to `group word by word.ToLower()` will fix that, of course all your keys will then be in lowercase.
Matt Greer
To be fair his answer was probably written before that was added.
Matthew Whited
When I try this, I get an error. Keep in mind that I am using a System.Collections.Specialized.StringCollection object to store the strings, not a string array
icemanind
@icemanind: I edited my answer to handle both of these criteria for you...
Reed Copsey
@icemanind i changed my answer to deal with the weakly typed collection.
JaredPar
@Taylor, my answer was written before the casing requirement was added.
JaredPar
Excellent! Works perfect
icemanind
+1  A: 

Given that you are using StringCollection and want to ignore case, you'll need to use Enumerable.GroupBy with Enumerable.Cast:

var results = collection.Cast<string>.GroupBy(
        i => i,
        (word, words) => new { Word = word, Count = words.Count() },
        StringComparer.CurrentCultureIgnoreCase
    );

foreach(var wordPair in results)
     Console.WriteLine("Word: \"{0}\" - Count: {1}", wordPair.Word, wordPair.Count);
Reed Copsey
A: 
var query =    
  from s in Collection  
  group s by s.Description into g  
  select new {word = g.Key, num = g.Count()};
Donnie
+1  A: 

To build a single string value result...

var stringCollection = new[] { "House", "Car", "house", "Dog", "Cat" };
var result = stringCollection.Cast<string>().GroupBy(
                                  k => k, 
                                  StringComparer.InvariantCultureIgnoreCase)
                             .Select(v => v.Key + " -->" + v.Count())
                             .Aggregate((l,r)=>l+" " + r);
//result = "House -->2 Car -->1 Dog -->1 Cat -->1"

To put each value on a different line...

var stringCollection = new[] { "House", "Car", "house", "Dog", "Cat" };
var result = stringCollection.Cast<string>().GroupBy(
                                  k => k, 
                                  StringComparer.InvariantCultureIgnoreCase);

foreach (var value in result)
    Console.WriteLine("{0} --> {1}", value.Key, value.Count());
Matthew Whited
`.Cast<string>()` added to work with `StringCollection`
Matthew Whited