views:

197

answers:

10

I have a Person class, with Name and AreaID properties.

public class Person
{
   public string Name;
   public int AreaID;

   // snip
}

I have a List<Person> with the potential for hundreds of Person objects in the list. e.g., 100 Persons with AreaID = 1 and 100 Persons with AreaID = 2

I want to return distinct list of AreaID's and how many Persons have that AreaID.

For example, AreaID = 1 Persons = 100 AreaID = 2 Persons = 100

+5  A: 

Looks like you want to group by area ID then:

var groups = from person in persons
             group 1 by person.AreaID into area
             select new { AreaID = area.Key, Persons = area.Count() };

I'm using "group 1" to indicate that I really don't care about the data within each group - only the count and the key.

This is inefficient in that it has to buffer all the results for the sake of grouping - you make well be able to use Reactive LINQ in .NET 4.0 to do this more efficiently, or you could certainly use Push LINQ if you wanted to. Then again, for relatively small datasets it probably doesn't matter :)

Jon Skeet
Good point on the group 1 by. Does this have much of an effect on the actual performance, though, given that it's using person.AreaID to group anyways?
Reed Copsey
@Reed: It means that if you're streaming the Person from disk, it becomes eligible for garbage collection earlier. Not an issue in most cases though.
Jon Skeet
+6  A: 

Use the GroupBy method.

var list = ...list of Persons...

var areas = list.GroupBy( p => p.AreaID )
                .Select( g => new {
                    AreaID = g.Key,
                    Count = g.Count()
                 });
tvanfosson
If you're grouping by AreaID, won't the key be the AreaID already, so you want `g.Key` instead of `g.Key.AreaID`?
Jon Skeet
Ah, yes -- I was repeating a pattern that I recently used where I had a compound key and needed to use a property of the key. I'll fix it.
tvanfosson
A: 
return list.GroupBy(p => p.AreaID)
    .Select(g => new { AreaID = g.Key, People = g.Count() });
Yuriy Faktorovich
A: 

Instead of distinct, use GroupBy, or the more succinct LINQ statement:

var results = from p in PersonList
              group p by p.AreaID into g
              select new { AreaID=g.Key, Count=g.Count() };

foreach(var item in results)
    Console.WriteLine("There were {0} items in Area {1}", item.Count, item.AreaID);
Reed Copsey
What is "n" in the "group n" part?
Jon Skeet
Thanks - it was a bad typo :)
Reed Copsey
A: 

you could use list.GroupBy(x => x.AreaID);

brysseldorf
A: 

You can try this:

var groups = from person in list
       group person by person.AreaID into areaGroup
       select new {
        AreaID = areaGroup.Key,
        Count = areaGroup.Count()
       };
scmccart
A: 
        var people = new List<Person>();

        var q = from p in people
                group p by p.AreaId into g
                select new { Id = g.Key, Total = g.Count() };


        people.Add(new Person { AreaId = 1, Name = "Alex" });
        people.Add(new Person { AreaId = 1, Name = "Alex" });
        people.Add(new Person { AreaId = 2, Name = "Alex" });
        people.Add(new Person { AreaId = 3, Name = "Alex" });
        people.Add(new Person { AreaId = 3, Name = "Alex" });
        people.Add(new Person { AreaId = 4, Name = "Alex" });
        people.Add(new Person { AreaId = 2, Name = "Alex" });
        people.Add(new Person { AreaId = 4, Name = "Alex" });
        people.Add(new Person { AreaId = 1, Name = "Alex" });

        foreach (var item in q)
        {
            Console.WriteLine("AreaId: {0}, Total: {1}",item.Id,item.Total);
        }
BFree
A: 

Something like this, perhaps ?

            List<Person> persons = new List<Person> ();
            persons.Add (new Person (1, "test1"));
            persons.Add (new Person (1, "test2"));
            persons.Add (new Person (2, "test3"));

            var results = 
                persons.GroupBy (p => p.AreaId);

            foreach( var r in results )
            {
                Console.WriteLine (String.Format ("Area Id: {0} - Number of members: {1}", r.Key, r.Count ()));
            }

            Console.ReadLine ();
Frederik Gheysels
ok, i am a bit late
Frederik Gheysels
A: 

ToLookup() will do what you want.

leppie
A: 

Surprisingly nobody advised to override Equals and GetHashCode. If you do so you can do folowing:

 List<Person> unique = personList.Distinct();

Or even

 List<Person> areaGroup = personList.GroupBy(p => p.AreaID);
 List<Person> area1Count = personList.Where(p => p.AreaID == 1).Count();

This gives you more flexibility, - no need in useless anonymous class.

Vasiliy Borovyak