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


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

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

PC環境

  • Mac OS Mojave 10.14.1
  • Ruby 2.4.0

何がしたいのか

ゲスエンジニアのとださんが先日rubyの問題をnoteで出題していました。

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

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

ライブドアのお天気サービスの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を値としたハッシュを作成してしまえば手間が省けるのではないかと考えました。

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

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

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"}

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

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

まず

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 の情報をハッシュに変換します。

次に

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が埋まっているかわからないので、

xml_hash["rss"]["channel"]["source"]["pref"]

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

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

現状の問題点

実行速度が遅い気がする。

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

まとめ

今回の実装のポイントは

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

でした。

参考記事