Skip to content

Instantly share code, notes, and snippets.

@abelards
Created March 7, 2022 10:53
Show Gist options
  • Save abelards/3f07392ed583dbd03fce6ecb0cd935ef to your computer and use it in GitHub Desktop.
Save abelards/3f07392ed583dbd03fce6ecb0cd935ef to your computer and use it in GitHub Desktop.

Revisions

  1. abelards created this gist Mar 7, 2022.
    48 changes: 48 additions & 0 deletions tally.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,48 @@
    # ENGLISH ABOVE, FRENCH BELOW -- VERSION FRANCAISE CI DESSOUS


    # You want to tally something in Rails and you like Ruby so you've written
    Object.pluck(:field).tally
    # and the results look OK so you don't look further
    {'ruby' => 999, 'python' => 300, 'java' => 42, 'C' => 1}
    # But you're actually asking the database to do only SELECT objects.field, fetch 1342 records, and counting in Ruby
    # Databases are quite cool and fast, and you'd rather do
    "SELECT objects.field, COUNT(objects.field) AS objects_count FROM objects GROUP BY objects.field"
    # followed by some ordering if you'd like
    "ORDER BY objects_count DESC"
    # Of course, don't write SQL, just write Ruby and Arel!
    Object.group(:field).calculate(:count, :all)
    Object.group(:field).count

    # But if you ever need to add this to a more complicated request, here's how you can do:
    table = Object
    key = table.arel_table[:field]

    request = table.reorder('objects_count DESC').select(key, key.count.as('objects_count')).group(key)
    request.map{|record| [record.field, record.objects_count]}

    ---

    # Vous avez envie de compter des Objet par le nombre d'occurrence de champ et vous aimez Ruby donc vous faites
    Objet.pluck(:champ).tally
    # et vous avez un résultat OK :
    {'ruby' => 999, 'python' => 300, 'java' => 42, 'C' => 1}
    # Sauf que ça fait SELECT objets.champ en SQL, renvoie 1342 records et fait du comptage en Ruby.
    # Et les bases de données sont très fortes pour ça, laissons-leur le travail avec
    "SELECT objets.champ, COUNT(objets.champ) AS count FROM objets GROUP BY objets.champ"
    # et peut-être même
    "ORDER BY count DESC"
    # Et arel vous permet de faire ça très bien :
    Objet.group(:champ).calculate(:count, :all)
    Objet.group(:champ).count

    # mais si vous deviez l'intégrer dans une requête bien plus complexe, voici ce que vous pouvez faire :
    table = Objet
    key = table.arel_table[:champ]

    request = table.reorder('objects_count DESC').select(key, key.count.as('objects_count')).group(key)
    request.map{|record| [record.champ, record.objects_count]}

    # Voilà, je pense que pas mal de gens ont ça (et souvent même sans tally mais avec des hash et des += 1)