user3113376
user3113376

Reputation: 171

Getting Percentage of TypeOf Item in a List with LINQ

I want to be able to get the percentages of certain types of things in a list.

Say I have a list of Animals, but inside that list I have Cats, Dogs, And Snakes:

List<Animal> animals = new List<Animal>{
new Cat { Name="Cuddles", Sound=Sound.Meow, Age=3 },
new Dog { Name="Spot", Sound=Sound.Bark, Age=7 },
new Cat { Name="Karma", Sound=Sound.Meow, Age=10 },
new Snake { Name="Nagini", Sound=Sound.Hiss, Age=1 },
new Dog { Name="Sparky", Sound=Sound.Bark, Age=4},
new Cat { Name="Matty", Sound=Sound.Meow, Age=15}
};

And I want to be able to get the percentages of these animals, so a potential result set would be:

Cats: 0.50
Dogs: 0.35
Snakes: 0.15

But I'm not really sure how to get this type of result set. Especially if I just wanted to get the percentage of Cats.

I know I need to use list.OfType<Cat>() to pull the list of just cats, but that's also when I'm having trouble getting the percentage of Cats, because I cant use .Sum() or .Average().

Any ideas?

Upvotes: 1

Views: 1028

Answers (3)

Anduril
Anduril

Reputation: 1276

You are most of the way there

you can simply do:

var catPercent = list.OfType<Cat>().Count() / (float)list.Count();

Upvotes: 4

Jon Skeet
Jon Skeet

Reputation: 1500785

No, I don't think you need to be using OfType<> at all. I suspect you really want GroupBy:

var countsByType = animals.GroupBy(x => x.GetType(),
                                   (t, g) => new { Type = t, Count = g.Count() });

That will give you:

{ Type = Cat, Count = 3 },
{ Type = Dog, Count = 2 },
{ Type = Snake, Count = 1 }

You can go from there to the proportion by dividing the count by the total if you want. For example:

var countsByType = animals.GroupBy(x => x.GetType(),
                                   (t, g) => new { Type = t, Count = g.Count() })
                          .ToList();
// Could just use animals.Count, but this approach works for sequences
// which can only be enumerated once.
var totalCount = countsByType.Sum(t => t.Count);
var proportions = countsByType.Select(pair => new { 
                            pair.Type,
                            Proportion = pair.Count / (double) totalCount
                        });

Upvotes: 8

Christian
Christian

Reputation: 4375

GroupBy is just what you need. It creates groups of items that have a common key, in your case the type of the class. Using a select on that grouping will allow you to create for instance a tuple that has the percentage you would like to get.

animals.GroupBy(a => a.GetType()).Select(group => new Tuple<Type, double>(group.Key, group.Count() / animals.Count()));

Upvotes: 1

Related Questions