これまで配列を多用していたのですが、
ある問題を解いていた時に配列だけでは解決できず、
ハッシュで解決できました。
その学びについてのメモ。
#前提
Aさん、 Bさん、 Cさんの書籍購入費ランキングを作成する。
Aさん、 Bさん、 Cさんという名前の 3人のエンジニアがおり、
それぞれ 1000円, 3000円, 2000円 の書籍購入費を消費。
この場合、 Bさんが 1位となり、 Cさんが 2位、そして Aさんが 3位となる。
それぞれのエンジニアの名前と、書籍の購入情報が与えられるので、
この情報から書籍購入費ランキングを作成する。
##入力
3 -> エンジニアの人数( number_of_engineers )
A B C -> エンジニアの名前( name_of_engineers )
4 -> エンジニアたちが購入した本の数( number_of_books )
A 1000 -> 本を購入したエンジニアの名前とその本の金額( name, price )
B 1000
B 2000
C 2000
##出力
B
C
A
#解き方
number_of_engineers = gets.to_i
name_of_engineers = gets.to_s.split(" ")
number_of_books = gets.to_i
name_n_price = {}
name_of_engineers.each{|name| name_n_price[name] = 0} #解説1
number_of_books.times do
name, price = gets.to_s.split(" ")
name_n_price.each_key{ |i| name_n_price[i] += price.to_i if i == name} #解説2
end
puts name_n_price.sort_by{ |name, price| -price}.to_h.keys #解説3
#解説
##解説1
事前に"name_n_price"で空のハッシュを生成しています。
name_of_engineersは現時点で以下のようになっています。
name_of_engineers = ["A", "B", "C"]
この配列の要素(A, B, C)をeachメソッドで取得し、name_n_priceの中のキーとして加えていきます。
また、この時、name_n_price[name] = 0としているので、各キーには 0というバリューが入ります。
この後、"A 1000"などが入力されていくため、初期値の設定として、上記のようにしています。
上記操作によって、"name_n_price"は以下のようになります。
name_n_price = {"A"=> 0, "B"=> 0, "C"=> 0}
##解説2
number_of_books.times do ~ end で "A 1000"など計4回の入力を行わせます。
(今回はnumber_of_books = 4)
name, price = gets.to_s.split(" ")にて以下のような配列を生成させます。
name, price = ["A", "1000"]
続いて、name_n_priceのハッシュ内の各キーに対して、値を追加していきます。
そのため、name_n_price内の各要素をeachメソッドで取得していくのですが、
今回はキーのみの取得を行っていきます。
理由はハッシュのキーに値を追加していく事を理解できていれば、
おさらいしておきます。
irb(main):011:0> h = {}
=> {}
irb(main):012:0> h[:name] = "suzuki"
=> "suzuki"
irb(main):013:0> p h
{:name=>"suzuki"}
=> {:name=>"suzuki"}
要はキー(今回で言う"name")があれば、値(今回で言う"suzuki")を指定して追加が可能です。
そのため、" name_n_price.each_key{} "とします。
他にはバリューだけ取り出すeach_valueもあります。
続いて、name_n_price = {"A"=> 0, "B"=> 0, "C"=> 0}に対して、
Aさんに該当した場合に値を追加していきたいので、if分で条件分岐を行います。
以下の通りです。
if i == name
ここでの " i "には A, B, Cが入ります。
( each_key でキーを取得しており { |i| ~ }としているため)
最後に入力された金額(price)を加えていきます。
name_n_price[i] += price.to_i
上記を分解して書くと、
name_n_price[i] = name_n_price[i] + price.to_i
代入値で書くと
name_n_price[A] = 0 + 1000
なので、
name_n_price = {"A"=> 0, "B"=> 0, "C"=> 0}
が
name_n_price = {"A"=> 1000, "B"=> 0, "C"=> 0}
になります。
これをあと三回繰り返すと結果的に
name_n_price = {"A"=> 1000, "B"=> 3000, "C"=> 2000}
になります。
##解説3
最後にname_n_priceに対して、金額が大きい人からそのキー(名前)を順番に出力します。
ハッシュ内の金額を大きい順(降順)にしたいので、sort_byを使用します。
sortもありますが、こちらはキーでの並び替えであるため、今回は使えません。
ちなみに、使うとこんな感じになります。
irb(main):017:0> h = {"z" => 1, "f" => 3, "a" => 5}
=> {"z"=>1, "f"=>3, "a"=>5}
irb(main):018:0> h.sort
=> [["a", 5], ["f", 3], ["z", 1]]
キーがアルファベット順で並び替えられています。
以下のようにする事で、値を降順にできます。
name_n_price.sort_by{ |name, price| -price}
irb(main):019:0> name_n_price = {"A"=> 1000, "B"=> 3000, "C"=> 2000}
=> {"A"=>1000, "B"=>3000, "C"=>2000}
irb(main):020:0> name_n_price.sort_by{ |name, price| -price}
=> [["B", 3000], ["C", 2000], ["A", 1000]]
これで並び替えができましたが、ハッシュから配列に要素が格納されています。
これは、sort_byやsortを実行すると、なってしまいます。
なぜかは不明です。
今回の条件ではキーだけを取得したいので、
配列をハッシュに戻して、キーを取得します。
配列をハッシュに変換するには、to_hメソッドを使用します。
name_n_price.sort_by{ |name, price| -price}.to_h
=> {"B"=>3000, "C"=>2000, "A"=>1000}
また、キーだけ欲しいので、keysメソッドを使用します。
name_n_price.sort_by{ |name, price| -price}.to_h.keys
=> ["B", "C", "A"]
putsメソッドでは改行されるので、上記をputsで出力してあげれば、
期待通りの答えになります。
以上です。