I've got a simple sinatra webapp that pulls data from Pinboard's RSS api, parses the info, and re-displays it. There are 4 tasks I need to perform with the data:
- I need to remove all instances of a specific tag from all items (in this case,
want) - I need to create a list of all unique tags across all the items returned
- I need to dynamically remove certain tags from the list if they are already being used to filter the list
- I need to add an attribute to each item that is the host of the url associated with that item (i.e.
amazon.comfor whatever long amazon url may have been added) - I need to return a list of unique locations across all the items returned
Here is the way I am currently handling this:
items = []
tags = []
locations = []
begin
JSON.parse(data).each do |item|
# Remove the tag want from the list of tags,
# strip any extra whitespace, and add a location attribute
item['t'].each { |t| t.strip! }
item['t'].delete_if { |tag| tag == 'want' }
# Try like hell to parse the url. Assign an error string as a last resort
begin
item['l'] = /https?:\/\/(?:[-\w\d]*\.)?([-\w\d]*\.[a-zA-Z]{2,3}(?:\.[a-zA-Z]{2})?)/i.match(item['u'])[1]
rescue
item['l'] = "URL Parse error"
end
# Add the item's tags
item['t'].each do |tag|
tags << tag unless tags.include? tag or filter_tags.include? tag
end
locations << item['l'] unless locations.include? item['l']
items << item
end
rescue
end
return items, tags.sort, locations.sort
I really don't like this. It just doesn't feel clean to me, what with the creation of the empty arrays, and adding stuff in, etc. But my thought was that it's better to do a single iteration over the data returned than to do multiple loops. But now I am considering this instead:
items = JSON.parse(data)
# Remove the want tag from all items
items.each { |i| i['t'].delete_if { |t| t == 'want' }}
#generate a list of tags
tags = items.map { |i| i['t']}.flatten.uniq.sort
tags.delete_if { |t| filter_tags.include? t }
# Parse the url
re = /https?:\/\/(?:[-\w\d]*\.)?([-\w\d]*\.[a-zA-Z]{2,3}(?:\.[a-zA-Z]{2})?)/i
items.each { |i| i['l'] = re.match(i['u'])[1] }
# Generate a list of locations
locations = items.map { |i| i['l']}.flatten.uniq.sort
return items, tags, locations
It feels cleaner, and seems like it's easier to read, but I'm not sure if the nested delete_if in the first bit is going too far. I'm also not sure how I feel about iterating over the items multiple times, or chaining flatten.uniq.sort for both tags and locations