group_byメソッドで同じ値をまとめる

TaskモデルとLabelモデルが多対多のアソシエーションが組まれており、

あるユーザーが使っているLabelの全種類&各Labelの使用回数を取り出したい事例がありました。

いきなり取り出そうとすると「どのメソッドを使えばいいんだ?!」と混乱したので、解決するまでの手順をメモしておきます。

 

 

簡単な値の例で考えてみる

ある配列から、重複した要素をまとめて配列にし、その要素の数と値を取り出せれば良いはずなので、一旦数字に置き換えてみます。

 

以下のような重複した要素が入り混じっている配列があったとします。

arry = [1, 1, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 7]

この配列の中から以下の2点を取り出します。

・arryの要素の種類 → 1, 2, 3, 4, 5, 6, 7

・各要素の種類ごとの数 → 1は3個、2は2個、3は3個、4は3個、5は2個、6は1個、7は1個

 

 

①重複した要素でまとめる

group_byメソッドを使って配列の中の重複した要素をまとめます。

arry.group_by{ | i | i }

=> {1=>[1, 1, 1], 2=>[2, 2], 3=>[3, 3, 3], 4=>[4, 4, 4], 5=>[5, 5], 6=>[6], 7=>[7]}

 

②要素の種類を取り出す

group_byメソッドでまとめたキーを取り出します。

arry.group_by{ | i | i }.keys

=> [1, 2, 3, 4, 5, 6, 7]

 

③各要素の種類ごとの数を取り出す

group_byメソッドでまとめたバリューの数にmapメソッドで置き換えます。

arry.group_by{ |i|i }.values.map{ | ar | ar.size }

=> [3, 2, 3, 3, 2, 1, 1]

 

 

実際にやりたかった処理

①userを取り出します

irb(main):001:0> user = User.find(1)
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "テスト太郎", email: "test1@test.com", created_at: "2019-02-08 09:47:05", updated_at: "2019-02-23 11:53:40", password_digest: "$2a$10$n2Ajef0ZplywBIYS2B/yb.OAYQUADxmlS7MImKu.V.L...", admin: false>

 

②配列を生成します

  irb(main):002:0> @lavels = []
=> []

 

③userのタスクに紐づいているラベルを@lavelsに全て入れます

irb(main):003:0> user.tasks.each do |task|
irb(main):004:1* task.lavels.each do |lavel|
irb(main):005:2* @lavels << lavel
irb(main):006:2> end
irb(main):007:1> end
Task Load (0.7ms) SELECT "tasks".* FROM "tasks" WHERE "tasks"."user_id" = $1 [["user_id", 1]]
Lavel Load (0.7ms) SELECT "lavels".* FROM "lavels" INNER JOIN "task_lavels" ON "lavels"."id" = "task_lavels"."lavel_id" WHERE "task_lavels"."task_id" = $1 [["task_id", 22]]
Lavel Load (0.3ms) SELECT "lavels".* FROM "lavels" INNER JOIN "task_lavels" ON "lavels"."id" = "task_lavels"."lavel_id" WHERE "task_lavels"."task_id" = $1 [["task_id", 13]]
Lavel Load (0.3ms) SELECT "lavels".* FROM "lavels" INNER JOIN "task_lavels" ON "lavels"."id" = "task_lavels"."lavel_id" WHERE "task_lavels"."task_id" = $1 [["task_id", 9]]
Lavel Load (0.3ms) SELECT "lavels".* FROM "lavels" INNER JOIN "task_lavels" ON "lavels"."id" = "task_lavels"."lavel_id" WHERE "task_lavels"."task_id" = $1 [["task_id", 14]]
Lavel Load (0.2ms) SELECT "lavels".* FROM "lavels" INNER JOIN "task_lavels" ON "lavels"."id" = "task_lavels"."lavel_id" WHERE "task_lavels"."task_id" = $1 [["task_id", 16]]
Lavel Load (0.2ms) SELECT "lavels".* FROM "lavels" INNER JOIN "task_lavels" ON "lavels"."id" = "task_lavels"."lavel_id" WHERE "task_lavels"."task_id" = $1 [["task_id", 18]]
Lavel Load (0.1ms) SELECT "lavels".* FROM "lavels" INNER JOIN "task_lavels" ON "lavels"."id" = "task_lavels"."lavel_id" WHERE "task_lavels"."task_id" = $1 [["task_id", 6]]
Lavel Load (0.1ms) SELECT "lavels".* FROM "lavels" INNER JOIN "task_lavels" ON "lavels"."id" = "task_lavels"."lavel_id" WHERE "task_lavels"."task_id" = $1 [["task_id", 1]]
Lavel Load (0.1ms) SELECT "lavels".* FROM "lavels" INNER JOIN "task_lavels" ON "lavels"."id" = "task_lavels"."lavel_id" WHERE "task_lavels"."task_id" = $1 [["task_id", 4]]
Lavel Load (0.1ms) SELECT "lavels".* FROM "lavels" INNER JOIN "task_lavels" ON "lavels"."id" = "task_lavels"."lavel_id" WHERE "task_lavels"."task_id" = $1 [["task_id", 5]]
Lavel Load (0.1ms) SELECT "lavels".* FROM "lavels" INNER JOIN "task_lavels" ON "lavels"."id" = "task_lavels"."lavel_id" WHERE "task_lavels"."task_id" = $1 [["task_id", 7]]
Lavel Load (0.1ms) SELECT "lavels".* FROM "lavels" INNER JOIN "task_lavels" ON "lavels"."id" = "task_lavels"."lavel_id" WHERE "task_lavels"."task_id" = $1 [["task_id", 8]]
Lavel Load (0.1ms) SELECT "lavels".* FROM "lavels" INNER JOIN "task_lavels" ON "lavels"."id" = "task_lavels"."lavel_id" WHERE "task_lavels"."task_id" = $1 [["task_id", 17]]
Lavel Load (0.1ms) SELECT "lavels".* FROM "lavels" INNER JOIN "task_lavels" ON "lavels"."id" = "task_lavels"."lavel_id" WHERE "task_lavels"."task_id" = $1 [["task_id", 11]]
Lavel Load (0.1ms) SELECT "lavels".* FROM "lavels" INNER JOIN "task_lavels" ON "lavels"."id" = "task_lavels"."lavel_id" WHERE "task_lavels"."task_id" = $1 [["task_id", 3]]
Lavel Load (0.1ms) SELECT "lavels".* FROM "lavels" INNER JOIN "task_lavels" ON "lavels"."id" = "task_lavels"."lavel_id" WHERE "task_lavels"."task_id" = $1 [["task_id", 21]]
Lavel Load (0.1ms) SELECT "lavels".* FROM "lavels" INNER JOIN "task_lavels" ON "lavels"."id" = "task_lavels"."lavel_id" WHERE "task_lavels"."task_id" = $1 [["task_id", 12]]
=> [#<Task id: 22, title: "tttttt", description: "ttttttttt", created_at: "2019-02-20 08:37:47", updated_at: "2019-02-25 05:41:09", deadline: "2019-02-23", status: "untouched", priority: "level0", user_id: 1, group_id: nil, row_order: 0>, #<Task id: 13, title: "status", description: "statusを変更できるようにした", created_at: "2019-02-07 03:24:53", updated_at: "2019-02-25 05:44:06", deadline: "2019-02-15", status: "untouched", priority: "level0", user_id: 1, group_id: 1, row_order: 1073741824>, #<Task id: 9, title: "flash", description: "書き方変えた?!", created_at: "2019-02-03 03:01:43", updated_at: "2019-02-19 12:04:44", deadline: "2019-02-26", status: "done", priority: "level0", user_id: 1, group_id: 1, row_order: nil>, #<Task id: 14, title: "緊急度", description: "緊急度を登録できるようにする", created_at: "2019-02-08 07:46:35", updated_at: "2019-02-19 12:04:44", deadline: "2019-02-09", status: "untouched", priority: "level0", user_id: 1, group_id: 1, row_order: nil>, #<Task id: 16, title: "ラベル", description: "ラベル", created_at: "2019-02-13 03:31:05", updated_at: "2019-02-19 12:04:44", deadline: "2019-02-13", status: "untouched", priority: "level0", user_id: 1, group_id: 1, row_order: nil>, #<Task id: 18, title: "rer", description: "rer", created_at: "2019-02-13 08:20:46", updated_at: "2019-02-19 12:04:44", deadline: "2019-02-13", status: "untouched", priority: "level0", user_id: 1, group_id: 1, row_order: nil>, #<Task id: 6, title: "redirect", description: "redirect_to task_path, notice: 'TODOを作成しました'", created_at: "2019-02-02 21:52:03", updated_at: "2019-02-19 12:04:44", deadline: "2019-02-18", status: "untouched", priority: "level1", user_id: 1, group_id: 1, row_order: nil>, #<Task id: 1, title: "indexアクション", description: "indexアクションとそのビューを作成する", created_at: "2019-01-31 23:49:40", updated_at: "2019-02-19 12:04:44", deadline: "2019-02-07", status: "untouched", priority: "level0", user_id: 1, group_id: 1, row_order: nil>, #<Task id: 4, title: "slim", description: "slimで書いた", created_at: "2019-02-02 21:47:06", updated_at: "2019-02-19 12:04:44", deadline: "2019-02-20", status: "untouched", priority: "level0", user_id: 1, group_id: 1, row_order: nil>, #<Task id: 5, title: "redirect", description: "redirect_to task_path, notice: 'TODOを作成しました'", created_at: "2019-02-02 21:52:00", updated_at: "2019-02-19 12:04:44", deadline: "2019-02-12", status: "untouched", priority: "level0", user_id: 1, group_id: 1, row_order: nil>, #<Task id: 7, title: "redirect直した", description: "redirect_to tasks_path, notice: 'TODOを作成しました'", created_at: "2019-02-02 21:52:47", updated_at: "2019-02-19 12:04:44", deadline: "2019-02-19", status: "untouched", priority: "level0", user_id: 1, group_id: 1, row_order: nil>, #<Task id: 8, title: "flash", description: "flashためす", created_at: "2019-02-02 22:22:21", updated_at: "2019-02-19 12:04:44", deadline: "2019-02-10", status: "untouched", priority: "level0", user_id: 1, group_id: 1, row_order: nil>, #<Task id: 17, title: "fdas", description: "fdsa", created_at: "2019-02-13 08:18:36", updated_at: "2019-02-25 06:06:39", deadline: "2019-02-13", status: "untouched", priority: "level0", user_id: 1, group_id: 1, row_order: -1207959552>, #<Task id: 11, title: "validation", description: "presence: true", created_at: "2019-02-04 17:18:27", updated_at: "2019-02-25 08:11:15", deadline: "2019-02-21", status: "untouched", priority: "level0", user_id: 1, group_id: 1, row_order: -603979776>, #<Task id: 3, title: "edit,update", description: "edit,updateアクションを作成する", created_at: "2019-01-31 23:52:52", updated_at: "2019-02-19 12:04:44", deadline: "2019-02-14", status: "working", priority: "level0", user_id: 1, group_id: 1, row_order: nil>, #<Task id: 21, title: "ラベルラベル", description: "ラベルラベル", created_at: "2019-02-15 03:28:09", updated_at: "2019-02-20 03:37:47", deadline: "2019-02-16", status: "untouched", priority: "level0", user_id: 1, group_id: 1, row_order: nil>, #<Task id: 12, title: "datatime", description: "datatimeを試す", created_at: "2019-02-06 05:37:44", updated_at: "2019-02-20 04:08:27", deadline: "2019-02-06", status: "untouched", priority: "level2", user_id: 1, group_id: 1, row_order: nil>]

 

④@lavelsをgroup_byメソッドで同じ要素は配列にまとめ、キーを取り出します

    irb(main):009:0> @lavels.group_by{|lavel| lavel}.keys
=> [#<Lavel id: 24, lavel_name: "tre", created_at: "2019-02-20 09:35:35", updated_at: "2019-02-20 09:35:35">, #<Lavel id: 25, lavel_name: "share_tasks", created_at: "2019-02-21 06:34:51", updated_at: "2019-02-21 06:34:51">, #<Lavel id: 4, lavel_name: "swd", created_at: "2019-02-13 06:39:23", updated_at: "2019-02-13 06:39:23">, #<Lavel id: 6, lavel_name: "dsa", created_at: "2019-02-13 06:45:41", updated_at: "2019-02-13 06:45:41">, #<Lavel id: 10, lavel_name: "えええええ", created_at: "2019-02-15 03:58:25", updated_at: "2019-02-15 03:58:25">, #<Lavel id: 32, lavel_name: "chart", created_at: "2019-02-25 08:08:44", updated_at: "2019-02-25 08:08:44">, #<Lavel id: 33, lavel_name: "チャート", created_at: "2019-02-25 08:11:15", updated_at: "2019-02-25 08:11:15">, #<Lavel id: 19, lavel_name: "?!?", created_at: "2019-02-17 04:24:40", updated_at: "2019-02-17 04:24:40">, #<Lavel id: 20, lavel_name: "!!!", created_at: "2019-02-17 04:24:40", updated_at: "2019-02-17 04:24:40">, #<Lavel id: 21, lavel_name: "No1", created_at: "2019-02-17 05:14:06", updated_at: "2019-02-17 05:14:06">, #<Lavel id: 22, lavel_name: "fas", created_at: "2019-02-19 08:49:44", updated_at: "2019-02-19 08:49:44">, #<Lavel id: 23, lavel_name: "fda", created_at: "2019-02-19 08:49:44", updated_at: "2019-02-19 08:49:44">]

 

⑤各要素の個数を取り出します

  irb(main):010:0> @lavels.group_by{|lavel| lavel}.values.map{|ar| ar.size}
=> [1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1]

 

 

これでやりたかった処理ができました!!