質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby on Rails 4

Ruby on Rails4はRubyによって書かれたオープンソースのウェブフレームワークです。 Ruby on Railsは「設定より規約」の原則に従っており、効率的に作業を行うために再開発を行う必要をなくしてくれます。

Q&A

解決済

1回答

2010閲覧

【Ruby on Rails】ハッシュの配列を作成したい

merryken

総合スコア30

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby on Rails 4

Ruby on Rails4はRubyによって書かれたオープンソースのウェブフレームワークです。 Ruby on Railsは「設定より規約」の原則に従っており、効率的に作業を行うために再開発を行う必要をなくしてくれます。

0グッド

0クリップ

投稿2017/05/22 07:00

モデルをいいね数で並び替え、その順位を含んだハッシュの配列を作成したいです。

作成したい配列は下記となります。

[{:post_id=>"1",:rank=>"1",:like_count=>"10"},{:post_id=>"2",:rank=>"2",:like_count=>"5"}, {:post_id=>"3",:rank=>"2",:like_count=>"5"},{:post_id=>"4",:rank=>"4",:like_count=>"1"}]

Postモデルはrankカラムを持っていなく、:like_countの数で並び替えた時の順位をrankキーとして上記のような配列を作成したいです。
※順位付けの際、同値(同率)も考慮したいです。

何卒よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

Postがlike_countカラムを持っているとすると
Post.order(like_count: :desc).as_json
keyをsymbolにしたいときは
Post.order(like_count: :desc).as_json.map(&:symbolize_keys)
とやればいいですかね。
とりあえずas_jsonがキモです。

すみませんうろ覚えで調べてました。

ruby

1Post.joins("inner join posts as self_posts").group("posts.id").order("posts.like_count").select("sum(self_posts.like_count>posts.like_count)+1 AS rank,posts.like_count,posts.id").as_json

これでどうでしょう。
解説は自己結合(自分を含めた全てのレコードと結合レコード数はn*nになります)
して、自分より小さいlike_countを数え上げて1を足しています。

正直数が増えてくると(数十万件レベル)すごく重いです。

重くなる場合はRedisを使って実装することをオススメします。

投稿2017/05/22 07:29

編集2017/05/22 09:02
moke

総合スコア2241

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

merryken

2017/05/22 07:38

ご回答ありがとうございます。as_jsonでハッシュの配列が作成できるのはわかったのですが、:like_countの数で並び替えた時の順位をrankキーとして含めた配列を作成したいのですがどのように書くといいでしょうか?よろしくお願いします。
moke

2017/05/22 09:12 編集

すみません加筆しました。 あと前の質問https://teratail.com/questions/74286 に対する回答は何が問題か書いてくれないと 他に、回答がつかないと思いますよ
merryken

2017/05/22 09:32

ありがとうございます! 2点質問なのですが、上記コードに関連付けがある場合はどのように書くといいでしょうか? (@user.postsのような場合です) Redisに関してですが、どのようにするといいでしょうか(関連付けがあるため、数は抑えられると思います。問題なければいいのですが)
moke

2017/05/22 09:42

Redisは試してみてうまくいかなかったら新しく質問をたててみてください。 これでも、それなりの規模まで動きます。 関連付けがある場合joins(:user)とかを別につけてくださいその際セレクトに含めるのを忘れないでください
merryken

2017/05/22 11:04

mokeさんのコードで関連付けがない場合にはうまくいきました!ありがとうございます! 関連付けに関してですが、joins(:user)をそのままつけるとうまくいかず下記のようにしてみました。しかし、rankの部分がうまくいきません。どのようにするとmoke様のコードのようにうまくrankが計算されますでしょうか? @rank = @user.posts.joins("inner join posts as self_posts").group("posts.id").order("posts.likes_count").select("sum(self_posts.likes_count>posts.likes_count)+1 AS rank,posts.likes_count,posts.id").as_json
moke

2017/05/22 11:48 編集

うまくいかなかったらエラーメッセージなりを出すといいとおもいます。belongs_toがないだけとか これだと特定のユーザーのランキングしかでませんけどそれで良いのなら
merryken

2017/05/22 11:49

エラーはありません。rankが同値(同率)を考慮したものにならないということです。 具体的には下記の配列の通りです。 [{"id"=>1, "like_count"=>0, "rank"=>5}, {"id"=>2, "like_count"=>1, "rank"=>4}, {"id"=>3, "like_count"=>2, "rank"=>1}]
moke

2017/05/22 19:26

joins(:user)をしていない結果と比較してくれないと。user_idに値が入ってるいないデータがあるだけでは?
merryken

2017/05/22 23:54

ご回答ありがとうございます。 joins(:user)をしていない結果が上記の配列となります。 つまり、 @rank = @user.posts.joins("inner join posts as self_posts").group("posts.id").order("posts.likes_count").select("sum(self_posts.likes_count>posts.likes_count)+1 AS rank,posts.likes_count,posts.id").as_json で取得できたものが下記となります。 [{"id"=>1, "like_count"=>0, "rank"=>5}, {"id"=>2, "like_count"=>1, "rank"=>4}, {"id"=>3, "like_count"=>2, "rank"=>1}] 現在、下記のような環境でテストしております。 全ての投稿数:5 ユーザーAの投稿数:3件 結合レコード数はn*nとのことでしたので、上記コードで9件になるものと思っていましたが15件となっていますが関係ありますでしょうか?的外れでしたら申し訳ございません。 ユーザーAが投稿した3件の投稿の中でrankを含めたハッシュの配列を作成したいのですができますでしょうか。 何卒よろしくお願いします。
moke

2017/05/23 00:09 編集

1,4,5位、上記の結果で正しい挙動です。(ランキングからAさんの結果だけ抽出と言ったら普通こうでしょう。) >>ユーザーAが投稿した3件の投稿の中でrankを含めたハッシュの配列を作成したいのですができますでしょうか。 それが美しく書けないのですよ とりあえず。 where(self_posts: {user_id: @user.id})を追加して @rank = @user.posts.joins("inner join posts as self_posts").group("posts.id").order("posts.likes_count").where(self_posts: {user_id: @user.id}).select("sum(self_posts.likes_count>posts.likes_count)+1 AS rank,posts.likes_count,posts.id").as_json としてみればいいことは理論が理解できていればわかると思います。
merryken

2017/05/23 00:27

ありがとうございます。できました! as_jsonの存在は大変勉強になりました。 この度は誠にありがとうございました。 また、説明不足でお手数おかけし申し訳ございません。
moke

2017/05/23 00:32 編集

おお、よかった >>自己結合して、自分より小さいlike_countを数え上げて1を足しています。 の方も勉強になっていると嬉しいです。
merryken

2017/05/23 00:51 編集

もちろん、勉強になりました! フォロー機能を実装した時も自己結合がよくわかっていなかったので…もう少しググって掘り下げてみます。 moke様にはいつもお世話になっているばかりで、今回も本当にありがとうございました。 また何かありましたらよろしくお願いします!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問