- ベストアンサー
Rubyでハッシュのハッシュのソート方法を知りたい!
- Rubyでハッシュのハッシュをソートする方法を教えてください。
- ユーザごとに案件ごとの必要工数をハッシュとして持たせ、全工数が多いユーザ順にソートしたいです。
- 具体的には、h1 = {"user1"=>{"a"=>10, "b"=>20, "c"=>30"}, "user2"=>{"d"=>5, "e"=>8}, "user3"=>{"f"=>10, "g"=>5, "h"=>10} } のようなハッシュのハッシュをソートしたいです。
- みんなの回答 (6)
- 専門家の回答
質問者が選んだベストアンサー
追記された状況を見る限り、JobやUserといった構造体クラスやテーブルクラスを書きわけておくことで取り回しが楽になりそうな気もしますが・・・今回の本題からははずれちゃいますね。 本題の中で残っている疑問点は、多分 Enumerable#inject と Hash.[] ですよね Enumerable#inject は有名なメソッドなので、少し検索すればいっぱい解説が出てくると思います。 自分はとりあえず公式リファレンスのURLだけ貼っておきます。 http://doc.ruby-lang.org/ja/1.8.7/class/Enumerable.html http://doc.ruby-lang.org/ja/1.9.2/class/Enumerable.html inject(sym) -> object inject(init, sym) -> object の部分ですね。 ぱっと見わかりづらいメソッドだとは思うので、いくつか書いてみて動作確認することをお勧めします。 RubyのHashは、1.9系から順番の保持を保証するようになりました。(OrderedHash) Hash.[] を使うことでソートが崩れて、なおかつinjectのシンボル記法が使えるということであれば、多分Ruby1.8.7をお使いですよね? もしRuby1.9系で#eachの順番だけ維持されればいい(indexアクセス等不要)なら、最終形がHashでも動きます。 Ruby1.8.7から乗り換えるわけにはいかないということであれば、多重配列で我慢するかテーブルクラスを書くことになるのかなと思います。
その他の回答 (5)
- koko_u_u
- ベストアンサー率18% (216/1139)
ANo5 氏が言われているように、User をあらわすクラスなどを定義するべきです。 自分のやりたいことをできる限りストレートにコード化することが重要です。 User クラスは name メソッドと task メソッド、total_cost メソッドくらいがあればよいでしょう。 外部システムから得られたユーザ情報を User インスタンスの配列として得れば、あとは total_cost に応じて、ソートするのは簡単です。 表示するときのために to_s メソッドを用意するのもよいでしょう。
お礼
質問の背景にあった問題について考えていただき大変勉強になりました。 今後の設計の際によく考えるようにしたいと思います。 回答ありがとうございました。
- MillenniuM
- ベストアンサー率58% (42/72)
RubyのHashは順番を保存するので勘違いする人もいますけど、 データ構造としての連想配列ではキー列挙の順序は追加の順序と一般には異なります。 まあ、それはそれとして、#2さんのを少し改変して、 Hash[h1.sort_by{|k, v| -v.values.inject(:+)}] 変更点: o Hash.[]を使って最終的にHashを生成 o 符号反転してソートすることで降順ソートを少し短く、意図を明確に
お礼
回答ありがとうございます。 こちらについても「(:+)」の部分の使い方がまだよくわかっておらず、もう少し探してみます。 あと、私の環境のせいかもしれませんが、Hash[]でくくる前はソートされているようなのですが、Hash[]でくくるとソート前の状態に戻ってしまうようでした。もう少し調べてみます。
- koko_u_u
- ベストアンサー率18% (216/1139)
そもそも Hash について、そのキーの登場順は考えるべきではないと思います。 ソートした結果は配列として得るようにインターフェースを考え直したほうがよいかと。 つまり、利用者が {"user1"=>{"a"=>10, "b"=>20, "c"=>30"}, "user3"=>{"f"=>10, "g"=>5, "h"=>10}, "user2"=>{"d"=>5, "e"=>8} } と {"user1"=>{"a"=>10, "b"=>20, "c"=>30"}, "user2"=>{"d"=>5, "e"=>8}, "user3"=>{"f"=>10, "g"=>5, "h"=>10} } を区別する必要が生ずるのは避けるべきだと思います。
お礼
ご指摘の通りです。自分でももっとよい設計があるのではないか?と思っています。 背景を補足します。 あるシステム内に、ユーザ名、案件、必要工数を含むテーブルが複数あり、それらは何かでソートされているわけではないのですが、一つずつすべてのテーブルを取り出すことができるようになっています。ここまでは既存のシステムなので変更できません。 このシステムからRubyで全テーブルを取り出しながら、最終的には、ユーザごとに抱えている案件の合計時間が多い順に表示させたい、というのがやりたいことです。 その前段として、テーブルを取り出し、ユーザ名ごとにハッシュを作っていこうと思ったのが例としてあげたハッシュになります。その後ソートしようとしたところで行き詰まり、質問させていただきました。
- sholmes
- ベストアンサー率81% (89/109)
ありゃ・・・すみません、injectの使い方間違ってました。 さっきのだと簡単にバグが出ますね。 修正版等 http://ideone.com/MhIAT http://ideone.com/RrRJ6
お礼
回答ありがとうございます。 実際に試してみて、期待通りの結果になることを確認できました。 injectというのは使ったことがなかったので勉強になりました。 「:-」の部分がちょっとわかっておらず、降順を示すのだろうことは想像できるのですが、使い方の説明が見つけられていないのでもう少し探してみます。
- sholmes
- ベストアンサー率81% (89/109)
"c"=>30" のところ、30の後のダブルクォーテーションは誤記ですよね? 取り敢えず、そのまま書くとこんな感じでしょうか http://ideone.com/MjaPO
お礼
申し訳ありません。30の後のダブルクォーテーションは誤記でした。 早々の回答ありがとうございました。
お礼
さらなる回答ありがとうございました。 いただいたURLでsymの意味もわかりました。「:-」は降順ということではなく、値を0から引いていって、マイナスの数字なので結果的に絶対値の大きいものからソートされるということですね。 また、ご指摘の通りRubyは1.8.7でした。やはり環境に違いがあったということでそれについても理解できました。