2012-08-23 9 views
5

Ich versuche, die Zahl der Nutzer von Altersbereich zur Liste:Gruppe Benutzer nach Altersgruppe in rubin

Range : #Users 
10-14 : 16 
15-21 : 120 
22-29 : 312 
30-40 : 12131 
41-70 : 612 
71-120 : 20 

Ich dachte an ein statisches Array von Hashes erstellen:

AGE_RANGES = [ 
    {label:"10 - 14", min:10, max:14}, 
    {label:"15 - 21", min:15, max:21}, 
    {label:"22 - 29", min:22, max:29}, 
    {label:"30 - 40", min:30, max:40}, 
    {label:"41 - 70", min:41, max:70}, 
    {label:"71 - 120", min:71, max:120} 
] 

und verwende es dann für meinen Suchfilter sowie für meine Suchanfrage. Aber ich kann mir keinen Weg vorstellen, die beste Leistung zu erzielen.

Meine Methode in meinem Modell nur Gruppen nach Alter:

def self.group_by_ageRange(minAge, maxAge) 

    query = User.group("users.age") 
       .where("users.age BETWEEN minAge and maxAge ") 
       .select("users.age, 
         count(*) as number_of_users") 

end 

Irgendwelche Vorschläge?

+1

http://stackoverflow.com/questions/232387/in-sql-how-can-you-group-by-in-ranges – InternetSeriousBusiness

Antwort

7

Sie wollen einige SQL erstellen, die wie folgt aussieht:

select count(*), 
     case 
      when age between 10 and 14 then '10 - 14' 
      when age between 15 and 21 then '15 - 21' 
      -- ... 
     end as age_range 
from users 
where age between 10 and 120 
group by age_range 

In Active Bedingungen, das wäre:

# First build the big ugly CASE, we can also figure out the 
# overall max and min ages along the way. 
min = nil 
max = nil 
cases = AGE_RANGES.map do |r| 
    min = [r[:min], min || r[:min]].min 
    max = [r[:max], max || r[:max]].max 
    "when age between #{r[:min]} and #{r[:max]} then '#{r[:min]} - #{r[:max]}'" 
end 

# Then away we go... 
age_ranges = Users.select("count(*) as n, case #{cases.join(' ')} end as age_range") 
        .where(:age => min .. max) 
        .group('age_range') 
        .all 

, dass Sie mit einer Reihe von Objekten in age_ranges verlassen und diejenigen, Objekte haben n und age_range Methoden. Wenn Sie einen Hash aus, dass dann:

age_ranges = Hash[age_ranges.map { |r| [r.age_range, r.n] }] 

Das wird nicht Bereiche umfassen, die keine Menschen in ihnen natürlich haben; Ich überlasse das als Übung für den Leser.

+0

dies löste mein Problem, danke. Ich habe jetzt anscheinend einen Rails Bug im Controller ... Wenn ich 'logger.debug (" items: # {@ ageRange_items.inspect} ") hinzufüge, geht das alles gut ... Wenn nicht, setzt es einfach 'cases.join ('')' als 'age_range', was zu einem natürlichen Fehler führt, der besagt, dass 'column age_range nicht existiert' – MrWater

+0

@itsalltime: Das ist schwer zu diagnostizieren, ohne den endgültigen Code zu sehen. –

+0

hier geht es ... es ist ein bisschen komplexer als die Frage, die ich gepostet habe, da es Chain-Joins hat, aber es sollte dieses Verhalten nicht haben 'query = User.joins (shops: {receipts: {tag:: user} }) .select ("case # {cases.join ('')} end als alge_range, count (*) as number_of_users, Summe (total) als total") .where ("users.id =: user_id" , user_id: user) .where ("users_tags.age" => min .. max) .group ("age_range") ' – MrWater

0

Ich finde die angenommene Antwort ein wenig dicht. Schnell, aber schwer zu verstehen und zu schreiben. Heute habe ich eine langsamere, aber einfachere Lösung gefunden. Da wir Altersgruppen nach Bereichen gruppieren, können wir davon ausgehen, dass wir keine values over 125

haben. Das heißt, wenn Sie einen Ruby-Filter für eine gruppierte und gezählte Ergebnismenge verwenden, werden Sie nicht über mehr als 125 Elemente iterieren. Dies wird langsamer sein als eine sql-basierte Gruppe/Anzahl, aber es war schnell genug für meine Zwecke, während ich immer noch auf die DB für die meisten schweren Arbeiten angewiesen bin. Das Iterieren über einen Hash mit weniger als 125 Elementen scheint keine große Sache zu sein. Vor allem, wenn die Schlüssel-Wert-Paare sind ints wie folgt aus:

{ 
    0 => 0, 
    1 => 1, 
    3 => 5, 
    25 => 3, 
    99 => 3 
} 

Hier ist der psudo-Code:

users = User 
    .where(age: (min..max)) 
    .group(:age) 
    .count(:age) 
group = Hash.new(0) 
users.each{|age, count| 
     case 
     when age <= 10 
     group['under 10'] += count 
     when age <= 25 
     group['11-25'] += count 
     when age <= 40 
     group['26-40'] += count 
     else 
     group['41+'] += count 
     end 
} 

Hinweis: diese Lösung die Anzahl der Nutzer in einem bestimmten Bereich zur Verfügung stellt.