Editing JSON Array contents in Ruby

2019-03-04 15:28发布

问题:

My JSON array is structured like this:

{"data":[{"Chris":[{"long":10,"lat":19}]},{"Scott":[{"long":9,"lat":18}]}]}

In the ruby program, I am wanting to be able to edit the lat and long values for the names. But I am not quite sure how to do so.

sections.each do |user_coords|
        user_coords.each do |user, coords|
            if user == Usrname then
                #Change lat and long value for Usrname
            end
        end
end

How can this be done?

回答1:

This is how to access individual elements in your JSON:

require 'json'

foo = JSON['{"data":[{"Chris":[{"long":10,"lat":19}]},{"Scott":[{"long":9,"lat":18}]}]}']
foo['data'][0]['Chris'][0]['long'] = 5
foo['data'][0]['Chris'][0]['lat'] = 7
foo # => {"data"=>[{"Chris"=>[{"long"=>5, "lat"=>7}]}, {"Scott"=>[{"long"=>9, "lat"=>18}]}]}

You can simplify the path somewhat, by using a variable as a placeholder into the object:

foo = JSON['{"data":[{"Chris":[{"long":10,"lat":19}]},{"Scott":[{"long":9,"lat":18}]}]}']
chris = foo['data'][0]['Chris'][0]
chris['long'] = 5
chris['lat'] = 7
foo # => {"data"=>[{"Chris"=>[{"long"=>5, "lat"=>7}]}, {"Scott"=>[{"long"=>9, "lat"=>18}]}]}

chris points to the "Chris" hash, which is embedded inside the foo hash. Changes to the chris hash occur inside foo.

If the hash was defined normally, it'd be more clean/clear and straightforward:

foo = JSON['{"data":{"Chris":{"long":5,"lat":7},"Scott":{"long":9,"lat":18}}}']
foo['data']['Chris']['long'] = 5
foo['data']['Chris']['lat'] = 7
foo # => {"data"=>{"Chris"=>{"long"=>5, "lat"=>7}, "Scott"=>{"long"=>9, "lat"=>18}}}

foo is more clearly defined as:

foo = {
  'data' => {
    'Chris' => {'long' => 5, 'lat' => 7},
    'Scott' => {'long' => 9, 'lat' => 18}
  }
}

Conditionally iterating over the hash to find a particular key/value pair looks like this with your hash:

require 'json'

foo = JSON['{"data":[{"Chris":[{"long":10,"lat":19}]},{"Scott":[{"long":9,"lat":18}]}]}']

user_name = 'Chris'
data = foo['data'].first
data.first.each do |key, value|
  if key == user_name
    data[user_name].first['long'] = 5
    data[user_name].first['lat'] = 6
  end
end

foo # => {"data"=>[{"Chris"=>[{"long"=>5, "lat"=>6}]}, {"Scott"=>[{"long"=>9, "lat"=>18}]}]}

Having to use first (or [0]) to get at hash elements has smell to it.

Using a hash that is defined correctly results in code that looks like:

foo = JSON['{"data":{"Chris":{"long":10,"lat":19},"Scott":{"long":9,"lat":18}}}']
foo['data'].each do |key, value| 
  if key == user_name
    value['long'] = 5
    value['lat'] = 7
  end
end
foo # => {"data"=>{"Chris"=>{"long"=>5, "lat"=>7}, "Scott"=>{"long"=>9, "lat"=>18}}}

How can I add another person called Bob with long = 10 and lat = 20

It sounds like you don't have a good grasp of manipulating/accessing hashes, or how to convert to/from JSON. You'd do well to get those basics down.

Don't start with JSON, instead, start with a Ruby hash:

require 'json'

foo = {
  "data" => {
    "Chris" => {
      "long" => 5, 
      "lat" => 7
    }, 
    "Scott" => {
      "long" => 9,
      "lat" => 18
    }
  }
}

Add to that any other hash elements you want:

bob_hash = {'Bob' => {'long' => 10, 'lat' => 20}}
foo['data'].merge!(bob_hash)

merge! adds bob_hash to foo['data']. Then, tell the hash to output its JSON representation using to_json. It's a lot easier to work with familiar Ruby structures, and let Ruby do the heavy-lifting of converting to JSON, than it is to try to do string manipulation on an existing JSON string. If you have the JSON, then parse it and convert/modify the resulting Ruby object, then output the JSON again.

puts foo.to_json
# >> {"data":{"Chris":{"long":5,"lat":7},"Scott":{"long":9,"lat":18},"Bob":{"long":10,"lat":20}}}

I'd recommend reading "How to convert JSON to a hash, search for and change a value" also, as it's a useful alternative for accessing values in the resulting hash.



回答2:

username = 'Chris'
sections.each do |user_coords|
  user_coords.each do |user, coords|
    if user == username then
      coords.each do |lat_long|
        lat_long['lat']  = 123 # Your value here...
        lat_long['long'] = 456 # Your value here...
      end
    end
  end
end
sections.to_json # => '[{"Chris":[{"long":456,"lat":123}]}...]'