Reputation: 1395
I want to create statistics that will show the age group of my registered Users.
Example:
18-24 - 150 registered users
25-34 - 3948 registered users
35-44 - 10028 registered users
45+ - 538 registered users
I have a birthday (dob) attribute in the Users table. I calculate their age from the User model:
def age
now = Time.now.utc.to_date
now.year - birthday.year - ((now.month > birthday.month || (now.month == birthday.month && now.day >= birthday.day)) ? 0 : 1)
end
The best I can do on my own is:
User.group(:birthday).count
Which outputs the total users for each date of birth (not the age).
I want to instead output groups for ages in the view. Can someone assist me? OR the alternative can be listing total users for each age. Whichever would be the best solution.
Upvotes: 2
Views: 1424
Reputation: 28245
You could do something like this in MySQL to get the sum of all 8-10 year old people:
SELECT COUNT(*) as FROM users WHERE Year(dob) BETWEEN 2004 AND 2006;
or in Rails
User.where('Year(dob) IN (?)',(2004..2006)).count
Using this code, you can do just the following to get the number of people in an age group:
groups = [[18,24],[25,35],[34,44],[45,99]]
current_year = DateTime.now.year
groups.each do |age_group|
range = Range.new(*age_group.map{|y| current_year - y}.reverse)
print age_group.join(" - ")+": "
puts User.where('Year(dob) IN (?)',range).count
end
Upvotes: 2
Reputation: 3048
create instance method to compute age of a user app/models/user.rb
:
def age
now = Time.now.utc.to_date
now.year - birthdate.year - ((now.month > birthdate.month || (now.month == birthdate.month && now.day >= birthdate.day)) ? 0 : 1)
end
Create object representing an age group (I'll go with tableless model, but it can be pure ruby object or even struct) app/models/age_group.rb
:
class AgeGroup
include ActiveModel::Model # not really necessary, but will add some AM functionality which could be nice later
attr_accessor :from, :to, :count
end
Create service object for computing age groups (Choosing service object is just my personal preference. You can create a helper or whatever you think will fit your needs the best). app/services/age_groups_service.rb
:
class AgeGroupService
# @params
# ranges - an array of age group ranges. E.g.: [[0, 18], [19, 24], [25, 34], ...]
# users - an array of users from which the age groups will be computed. Defaults to all users
def initialize(ranges = [], users = User.all.to_a)
@ranges = ranges
@users = users
@age_groups = []
end
# Count users in each age group range
# @return
# an array of age groups. E.g.: [{ from: 0, to: 18, count: 12 }, ...]
def call
@ranges.each do |range|
users = @users.select { |user| user.age >= range[0] && user.age <= range[1] }
@age_groups << AgeGroup.new(from: range[0], to: range[1], count: users.length)
end
@age_groups
end
end
Assign value returned by call to the AgeGroupsService
to an instance variable in your controller:
age_group_service = AgeGroupsService.new([[18, 24], [25, 34], [35, 44], [45, 100]])
@age_groups = age_group_service.call
Print out the results in a view:
<ul>
<% @age_groups.each do |age_group| %>
<li><%= age_group.from %> - <%= age_group.to %>: <b><%= age_group.count %></b></li>
<% end %>
</ul>
Some further ideas / notes:
AgeGroup
model and AgeGroupsService
aren't necessary. You can use PORO and helpers instead or you can even put all the code into User
model and relevant controller. Anyway I would strongly recommend against this as it will go messy and it just feels wrong. Age groups seem like something deserving it's own place.Upvotes: 2