Skip to content

Instantly share code, notes, and snippets.

@knowtheory
Last active December 13, 2015 17:29
Show Gist options
  • Select an option

  • Save knowtheory/4948333 to your computer and use it in GitHub Desktop.

Select an option

Save knowtheory/4948333 to your computer and use it in GitHub Desktop.
To run this, download the gist, and from the commandline type `ruby [path/to/the/gist.rb]` You should make sure you have Ruby and nokogiri installed on your computer first. N.B. just realized that the download gist link saves a zipped file that you will have to unzip to get to the xml_transformation.rb file.
<state name="Alabama">
<city name="Abbeville" number="1"/>
<city name="Adamsville" number="1"/>
<city name="Addison" number="1"/>
<city name="Akron" number="2"/>
</state>
require 'nokogiri'
# note that the states are wrapped inside <doc> tags.
# nokogiri prefers dealing with just one top level node.
text = <<-XML
<doc>
<state>
<name>Alabama</name>
<city>Abbeville</city>
<number>1</number>
</state>
<state>
<name>Alabama</name>
<city>Adamsville</city>
<number>1</number>
</state>
<state>
<name>Alabama</name>
<city>Addison</city>
<number>1</number>
</state>
<state>
<name>Alabama</name>
<city>Akron</city>
<number>2</number>
</state>
</doc>
XML
# parse the text into XML
xml = Nokogiri::XML(text)
# this is where we're storing the states
states = {}
# find all of the state nodes, and loop over them
xml.css('state').each do |state|
# get all of the pieces of text we care about.
state_name = state.css('name').text
city_name = state.css('city').text
number = state.css('number').text
# ensure that each state_name is stored as a hash in `states`
# this uses ruby's conditional operator. it'd be equivalent to saying
# states[state_name] = states[state_name] || {}
# or
# states[state_name] = {} unless states[state_name]
states[state_name] ||= {}
states[state_name][city_name] = number # assign each city it's number.
end
# If you wanted to work with the states in ruby, you could just stop here.
# The code below is just to output some XML.
# In general, if you've parsed some XML and extracted the data you want
# it's often easier just to work with the data you've extracted
# rather than stuffing it back into XML format.
# loop over all the states we have extracted
output = states.map do |state_name, cities|
# generate a list of xml strings for each city
# and merge them all together into one string.
city_string = cities.map do |city_name, number|
" <city name=\"#{city_name}\" number=\"#{number}\"/>"
end.join("\n")
# stick the merged xml we made for the cities into
# a state node.
"<state name=\"#{state_name}\">\n#{city_string}\n</state>"
end
# print the output.
puts output
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment