9

Given the following hash structure I would like to walk the structure and make a modification to all of the values with a key of "link":

{"page_id":"12345", "link_data":{"message":"test message", "link":"https://www.example.com", "caption":"https://www.example.com", "child_attachments":[{"link":"http://www.example.com", "name":"test", "description":"test", "picture":"https://fbcdn-creative-a.akamaihd.net/hads-ak-xap1/t45.1600-4/10736595_6021553533580_1924611765_n.png"}, {"link":"http://www.example.com", "name":"test", "description":"test", "picture":"https://fbcdn-creative-a.akamaihd.net/hads-ak-xaf1/t45.1600-4/10736681_6021625991180_305087686_n.png"}, {"link":"http://www.example.com", "name":"test", "description":"test 2", "picture":"https://fbcdn-creative-a.akamaihd.net/hads-ak-xfp1/t45.1600-4/10736569_6020761399780_1700219102_n.png"}]}}

The approach that I've been playing with feels a bit wrong to me in that Im checking all of the values to see if they have a pattern matching what should be a URL and then modifying it at that point:

  def find_all_values_for(key)
    result = []
    result << self[key]
    self.values.each do |hash_value|
      if hash_value.to_s =~ URI::regexp # if the value looks like a URL then change it
        # update the url
     end
    end
  end

So the exact end result of the transformation should be the same hash with the URL's modified. What I actually want to do is to add tracking parameters to each of the URL's in the hash.

I've toyed with the idea of converting the hash to a string and performing some string replacement on it too but that doesnt seem like it would be the cleanest way of doing such a thing.

Cheers

cgallagher
  • 235
  • 2
  • 10
  • 2
    I didn't understand your question. What are you exactly trying to do? – Surya Nov 05 '14 at 17:44
  • I have updated the question with a bit more detail, hope it helps. – cgallagher Nov 05 '14 at 17:47
  • It would be helpful if you would edit to: 1) give your hash a name (e.g., `hash = {...}`) so that readers can reference it in their answers; 2) shorten and simplify the hash to the minimum required for your question (the terms need not be related to your application); 3) reformat the hash to that horizontal scrolling is not needed to read it (by putting it on multiple lines); 4) show the desired output for the given hash; and 5) clearly state your question. – Cary Swoveland Nov 05 '14 at 17:54

2 Answers2

17

Something like this perhaps?

def update_links(hash)
  hash.each do |k, v|
    if k == "link" && v.is_a?(String)
      # update link here
      v.replace "a modification"
    elsif v.is_a?(Hash)
      update_links v
    elsif v.is_a?(Array)
      v.flatten.each { |x| update_links(x) if x.is_a?(Hash) }
    end
  end
  hash
end
August
  • 12,410
  • 3
  • 35
  • 51
0

The accepted answer is simply not going to work in ruby 2.5+. You cannot modify a hash in place. You will get a frozen string error:

`replace': can't modify frozen String: "..." (FrozenError)

Instead, you can create a new hash with modified key/value pairs:

 def update_links(hash)
    hash.reduce({}) do |acc, (key,value)|
      if value.is_a?(Hash)
        acc[key.underscore] = update_links(value)
      else
        acc[key.underscore] = value
      end
      acc
    end
  end

In this simple demo, it underscores the key without modifying it in-place.

Daniel Viglione
  • 8,014
  • 9
  • 67
  • 101