Reputation: 6547
Say I have a list of Person
class
public class Person
{
public int Age { get; set; }
public string Name { get; set; }
}
How can I group by dynamic ranges? (For example starting from the youngest person I would like to group by ranges of 5 so if the youngest person is 12 the groups would be 12-17, 18-23 ....)
How can I determine the Key
of IGrouping
interface? (Set the Key
of each group to be the ages average in that group for example)
Upvotes: 1
Views: 1467
Reputation: 106956
To get the key to group by you can create a function:
String GetAgeInterval(Int32 age, Int32 minimumAge, Int32 intervalSize) {
var group = (age - minimumAge)/intervalSize;
var startAge = group*intervalSize + minimumAge;
var endAge = startAge + intervalSize - 1;
return String.Format("{0}-{1}", startAge, endAge);
}
Assuming that the minimum age is 12 and the interval size is 5 then for ages between 12 and 16 (inclusive) the function will return the string 12-16
, for ages between 17 and 21 (inclusive) the function will return the string 17-21
etc. Or you can use an interval size of 6 to get the intervals 12-17, 18-23 etc.
You can then create the groups:
var minimumAge = persons.Min(person => person.Age);
var personsByAgeIntervals = persons
.GroupBy(person => GetAgeInterval(person.Age, minimumAge, 5));
To get the average age in each group you can do something like this:
var groups = personsByAgeIntervals.Select(
grouping => new {
AgeInterval = grouping.Key,
AverageAge = grouping.Average(person => person.Age),
Persons = grouping.ToList()
}
);
This will create a sequence of groups represented by an anonymous type with properties AgeInterval
, AverageAge
and Persons
.
Upvotes: 3
Reputation: 673
Using Linq but not IGrouping (I've never used this interface, so I didn't think helping you would be the best time to start). I added a configuration class to set the min/max age as well as a basic descriptor.
public class GroupConfiguration {
public int MinimumAge { get; set; }
public int MaximumAge { get; set; }
public string Description { get; set; }
}
I created a list of Person (people) and populated it with a few sample records.
List<Person> people = new List<Person>() {
new Person(12, "Joe"),
new Person(17, "Bob"),
new Person(21, "Sally"),
new Person(15, "Jim")
};
Then I created a list of GroupConfiguration (configurations) and populated it with 3 logical-for-me records.
List<GroupConfiguration> configurations = new List<GroupConfiguration>() {
new GroupConfiguration() {MinimumAge = 0, MaximumAge=17, Description="Minors"},
new GroupConfiguration() {MinimumAge = 18, MaximumAge=20, Description="Adult-No Alcohol"},
new GroupConfiguration() {MinimumAge = 21, MaximumAge=999, Description="Adult-Alcohol"},
};
I then load them to a dictionary, to maintain the relationship between the configuration and the results that match that configration. This uses Linq to find the records from people that match MinimumAge <= age <= MaximumAge. This would allow someone to be placed in multiple results, if there were MinimumAge and Maximum age overlaps.
Dictionary<GroupConfiguration, IEnumerable<Person>> groupingDictionary = configurations.ToDictionary(groupConfiguration => groupConfiguration, groupConfiguration
=> people.Where(x => x.Age >= groupConfiguration.MinimumAge && x.Age <= groupConfiguration.MaximumAge));
Throwing this in a console program, I validated that 3 people exist in the Minors group, 0 in the Adult-No Alcohol group, and 1 in the Adult-Alcohol group.
foreach (var kvp in groupingDictionary) {
Console.WriteLine(kvp.Key.Description + " " + kvp.Value.Count());
}
Console.ReadLine();
Upvotes: 1