RubyでXMLから欲しい情報のハッシュを作成

# RubyでXMLから欲しい情報のハッシュを作成

もし、もっと良い方法があればコメントください！！

## PC環境
* Mac OS Mojave 10.14.1
* Ruby 2.4.0

## 何がしたいのか

[ゲスエンジニアのとださん](https://twitter.com/cohki0305)が先日rubyの問題をnoteで出題していました。

上記noteは以下のurlを参照ください。
https://note.mu/cohki0305/n/n339931e3f705

Rubyで天気予報のプログラムを作成するのですが、

ライブドアの[お天気サービス](http://weather.livedoor.com/weather_hacks/webservice)のAPIを用いるとき、

http://weather.livedoor.com/forecast/rss/primary_area.xml　 にて

あらかじめ場所の```city id```を知っていないといけません。

調べたい場所が変わるごとに```city id```を設定し直すのは面倒です。

だったら、

http://weather.livedoor.com/forecast/rss/primary_area.xml の情報から

場所をキー、```city id```を値としたハッシュを作成してしまえば手間が省けるのではないかと考えました。

![hash.png](https://qiita-image-store.s3.amazonaws.com/0/335866/35bd1eab-ad10-6c1c-aba4-dcb17c3262a2.png)


## 実際のハッシュ作成プログラム

実際に作成したプログラムです。

```ruby
require 'open-uri'
require "rexml/document"
require 'active_support'
require 'active_support/core_ext'

xml_url = open("http://weather.livedoor.com/forecast/rss/primary_area.xml").read.toutf8
xml_doc = REXML::Document.new(xml_url)
xml_hash = Hash.from_xml(xml_doc.to_s)
city_id_hash = {}
xml_hash["rss"]["channel"]["source"]["pref"].each do |key|
  count = 0
  #ハッシュ作成
  while key["city"][count] != nil do
    city_id_hash[key["city"][count]["title"]] = key["city"][count]["id"] 
    count += 1
  end
end

#確認用
puts city_id_hash
```

実行結果は次の通りです。

```
{"稚内"=>"011000", "旭川"=>"012010", "留萌"=>"012020", "網走"=>"013010", "北見"=>"013020", "紋別"=>"013030", "根室"=>"014010", "釧路"=>"014020", "帯広"=>"014030", "室蘭"=>"015010", "浦河"=>"015020", "札幌"=>"016010", "岩見沢"=>"016020", "倶知安"=>"016030", "函館"=>"017010", "江差"=>"017020", "青森"=>"020010", "むつ"=>"020020", "八戸"=>"020030", "盛岡"=>"030010", "宮古"=>"030020", "大船渡"=>"030030", "仙台"=>"040010", "白石"=>"040020", "秋田"=>"050010", "横手"=>"050020", "山形"=>"060010", "米沢"=>"060020", "酒田"=>"060030", "新庄"=>"060040", "福島"=>"070010", "小名浜"=>"070020", "若松"=>"070030", "水戸"=>"080010", "土浦"=>"080020", "宇都宮"=>"090010", "大田原"=>"090020", "前橋"=>"100010", "みなかみ"=>"100020", "さいたま"=>"110010", "熊谷"=>"110020", "秩父"=>"110030", "千葉"=>"120010", "銚子"=>"120020", "館山"=>"120030", "東京"=>"130010", "大島"=>"130020", "八丈島"=>"130030", "父島"=>"130040", "横浜"=>"140010", "小田原"=>"140020", "新潟"=>"150010", "長岡"=>"150020", "高田"=>"150030", "相川"=>"150040", "富山"=>"160010", "伏木"=>"160020", "金沢"=>"170010", "輪島"=>"170020", "福井"=>"180010", "敦賀"=>"180020", "甲府"=>"190010", "河口湖"=>"190020", "長野"=>"200010", "松本"=>"200020", "飯田"=>"200030", "岐阜"=>"210010", "高山"=>"210020", "静岡"=>"220010", "網代"=>"220020", "三島"=>"220030", "浜松"=>"220040", "名古屋"=>"230010", "豊橋"=>"230020", "津"=>"240010", "尾鷲"=>"240020", "大津"=>"250010", "彦根"=>"250020", "京都"=>"260010", "舞鶴"=>"260020", "神戸"=>"280010", "豊岡"=>"280020", "奈良"=>"290010", "風屋"=>"290020", "和歌山"=>"300010", "潮岬"=>"300020", "鳥取"=>"310010", "米子"=>"310020", "松江"=>"320010", "浜田"=>"320020", "西郷"=>"320030", "岡山"=>"330010", "津山"=>"330020", "広島"=>"340010", "庄原"=>"340020", "下関"=>"350010", "山口"=>"350020", "柳井"=>"350030", "萩"=>"350040", "徳島"=>"360010", "日和佐"=>"360020", "松山"=>"380010", "新居浜"=>"380020", "宇和島"=>"380030", "高知"=>"390010", "室戸岬"=>"390020", "清水"=>"390030", "福岡"=>"400010", "八幡"=>"400020", "飯塚"=>"400030", "久留米"=>"400040", "佐賀"=>"410010", "伊万里"=>"410020", "長崎"=>"420010", "佐世保"=>"420020", "厳原"=>"420030", "福江"=>"420040", "熊本"=>"430010", "阿蘇乙姫"=>"430020", "牛深"=>"430030", "人吉"=>"430040", "大分"=>"440010", "中津"=>"440020", "日田"=>"440030", "佐伯"=>"440040", "宮崎"=>"450010", "延岡"=>"450020", "都城"=>"450030", "高千穂"=>"450040", "鹿児島"=>"460010", "鹿屋"=>"460020", "種子島"=>"460030", "名瀬"=>"460040", "那覇"=>"471010", "名護"=>"471020", "久米島"=>"471030", "南大東"=>"472000", "宮古島"=>"473000", "石垣島"=>"474010", "与那国島"=>"474020"}
```

なかなかの力技のような気はしますが、とりあえずはできました。

### 作成に至るまでの試行錯誤
まず

```ruby
xml_url = open("http://weather.livedoor.com/forecast/rss/primary_area.xml").read.toutf8
xml_doc = REXML::Document.new(xml_url)
xml_hash = Hash.from_xml(xml_doc.to_s)
```

で http://weather.livedoor.com/forecast/rss/primary_area.xml の情報をハッシュに変換します。

次に

```ruby
city_id_hash = {}
xml_hash["rss"]["channel"]["source"]["pref"].each do |key|
  count = 0
  #ハッシュ作成
  while key["city"][count] != nil do
    city_id_hash[key["city"][count]["title"]] = key["city"][count]["id"] 
    count += 1
  end
end
```

で地名とそれに対応する```city id```だけを抜き出してハッシュを作成しています。
作成中にはどのキーに地名や```city id```が埋まっているかわからないので、

```ruby
xml_hash["rss"]["channel"]["source"]["pref"]
```
に関しては、```xml_hash.keys```や```xml_hash["rss"].keys```を何回も繰り返して地道に見つけていきました笑

while文の条件、```key["city"][count] != nil```はなぜこのようにしているかというと、
都道府県ごとに所有している地名が異なるからです。
この実装については実際に```puts key["city"]```をループ中に挿入して動作を確認していただくと理解できると思います。ぜひ考えてみてください。

### 現状の問題点
実行速度が遅い気がする。

もっと良い方法があるのならばぜひコメントください！


## まとめ
今回の実装のポイントは

1.　xmlをhashとして読み込む
2.　**読み込んだhashのデータ構造を調査する**
3.　欲しい情報を抜き出し、求めていたハッシュを完成させる

でした。

## 参考記事

* 『kzfm’s trial and error　本腰いれてRubyやPHPを覚えていくブログ。』
    - XMLをParseする (Ruby)
    - http://kzfm-s.hateblo.jp/entry/2015/06/07/014735