Traversing a Hashie::Mash

While messing around with gems for Ruby, and in particular Dark Sky’s weather forecast API, Forecast.io, I came across an object type that I hadn’t used before: the Mash.

It seems like a deeply-nested thing, and I couldn’t really see an obvious way to iterate over it, so I figured I’d share the process that I used to unwrap it.

In order to figure out exactly what Forecast.io was returning in the first place, I first used the class method:

forecast = ForecastIO.forecast(latitude, longitude)
puts forecast.class

which returned:

Hashie::Mash

This is how I figured out it was a Mash in the first place. I then converted the Mash to a hash:

forecast = forecast.to_hash

and was then able to see what this resulting hash was composed of, again using the class method:

forecast["daily"].each do |key, value|
  puts "Key name: #{key}\tKey type: #{key.class}\tValue type: #{value.class}"
end

which returned:

Key name: summary Key type: String Value type: String
Key name: icon Key type: String Value type: String
Key name: data Key type: String Value type: Array

And so I was able to discover that all the forecast data that I wanted to access was kept in an array, as a value paired to the key called “data.” “Summary” contains today’s weather summary only, and “icon” contains today’s weather icon only. As we will see, those data are also stored for today’s date in the forecast, so we don’t even need to use them for the current day.

But what is each element in this array?

Turns out the data is in an array of 8 hashes – today’s weather plus the seven day forecast. Each hash represents a different day. Each hash contains everything from ozone to pressure to sunset times, and feel free to poke around in there. For now, all I’m interested in is the temperature maximum and minimum, along with the summary.

forecast = ForecastIO.forecast(latitude, longitude)
forecast = forecast.to_hash

forecast["daily"].each do |key, value|
  if key =="data"
    value.each do |n|
      date = Time.at(n['time'])
      puts "#{date.month}/#{date.day}\nHigh: #{n['temperatureMax']}F\tLow: #{n['temperatureMin']}F\n#{n['summary']}\n--------"
    end
  end
end

And that successfully pulled everything out and displayed it nice and pretty, so this was one way of traversing the Mash.

In the end, I was able to make a quick little script to pull out forecast data for a location input by a user using Forecast.io for getting the forecast and geocoder for looking up the latitude/longitude of a location:

require 'forecast_io'
require 'date'
require 'time'
require 'geocoder'

date = Date.today

puts "Enter the city and/or state you would like to get a forecast for:"
location = gets.chomp
puts "--------"

city = Geocoder.search(location)
latitude = city[0].latitude
longitude = city[0].longitude

ForecastIO.api_key = 'YOUR API KEY'

forecast = ForecastIO.forecast(latitude, longitude)
forecast = forecast.to_hash

forecast["daily"].each do |key, value|
  if key =="data"
    value.each do |n|
      date = Time.at(n['time'])
      puts "#{date.month}/#{date.day}\nHigh: #{n['temperatureMax']}F\tLow: #{n['temperatureMin']}F\n#{n['summary']}\n--------"
    end
  end
end

For example, if you input “san francisco,” you get an output that looks like this:

Enter the city and/or state you would like to get a forecast for:
san francisco
--------
7/11
High: 70.66F Low: 57.11F
Partly cloudy starting in the evening.
--------
7/12
High: 69.74F Low: 54.98F
Partly cloudy in the morning.
--------
7/13
High: 72.35F Low: 56.02F
Partly cloudy in the morning.
--------
7/14
High: 75.86F Low: 55.67F
Partly cloudy in the morning.
--------
7/15
High: 75.49F Low: 57.89F
Clear throughout the day.
--------
7/16
High: 69.27F Low: 57.4F
Clear throughout the day.
--------
7/17
High: 66.31F Low: 56.43F
Mostly cloudy until afternoon.
--------
7/18
High: 66.72F Low: 55.71F
Mostly cloudy until afternoon.
--------

Hooray!

Although I figure, there’s got to be a less roundabout-way of getting data out of a Mash (without converting them into hashes), because otherwise why would they even exist? Thus, further research is called for on my part.