iamse7en iamse7en - 17 days ago 6
Ruby Question

Modify a nested hash of arrays and hashes

{"8933"=>
[{"id"=>419,
"game_id"=>8933,
"user_id"=>1,
"line"=>21,
"created_at"=>Mon, 21 Nov 2016 02:27:16 EST -05:00,
"updated_at"=>Mon, 21 Nov 2016 02:27:16 EST -05:00}],
"8690"=>
[{"id"=>401,
"game_id"=>8690,
"user_id"=>1,
"line"=>#<BigDecimal:7fdd42d96670,'-0.5E0',9(18)>,
"created_at"=>Wed, 16 Nov 2016 21:55:59 EST -05:00,
"updated_at"=>Wed, 16 Nov 2016 21:55:59 EST -05:00}]}


I need to retain this exact structure, only change/update the value of the first
"line"
to whatever, let's say 20. It always be this same exact spot I will need to update/modify. I tried a bunch of Hash#methods only to run into syntax errors or changing the existing structure. Thank you!

Answer

Here's a possible solution, which doesn't modify the original data and creates a new hash.

data = {
  "8933"=>
  [
    {
      "id"=>419,
      "game_id"=>8933,
      "user_id"=>1,
      "line"=>21,
      "created_at"=>"Mon, 21 Nov 2016 02:27:16 EST -05:00",
      "updated_at"=>"Mon, 21 Nov 2016 02:27:16 EST -05:00"
    }
  ],
  "8690"=>
  [
    {
      "id"=>401,
      "game_id"=>8690,
      "user_id"=>1,
      "line"=>-5,
      "created_at"=>"Wed, 16 Nov 2016 21:55:59 EST -05:00",
      "updated_at"=>"Wed, 16 Nov 2016 21:55:59 EST -05:00"
    }
  ]
}

# Use deep_copy if you use Ruby only. Use data.deep_dup if you use Rails
def deep_copy(o)
    Marshal.load(Marshal.dump(o))
end

modified_data = deep_copy(data)

modified_data.each{|n,array|
  array.each{|ids|
     ids["line"] = "whatever"
  }
} 

require 'pp'

pp modified_data
puts "-----"
pp data

# {"8933"=>
#   [{"id"=>419,
#     "game_id"=>8933,
#     "user_id"=>1,
#     "line"=>"whatever",
#     "created_at"=>"Mon, 21 Nov 2016 02:27:16 EST -05:00",
#     "updated_at"=>"Mon, 21 Nov 2016 02:27:16 EST -05:00"}],
#  "8690"=>
#   [{"id"=>401,
#     "game_id"=>8690,
#     "user_id"=>1,
#     "line"=>"whatever",
#     "created_at"=>"Wed, 16 Nov 2016 21:55:59 EST -05:00",
#     "updated_at"=>"Wed, 16 Nov 2016 21:55:59 EST -05:00"}]}
# -----
# {"8933"=>
#   [{"id"=>419,
#     "game_id"=>8933,
#     "user_id"=>1,
#     "line"=>21,
#     "created_at"=>"Mon, 21 Nov 2016 02:27:16 EST -05:00",
#     "updated_at"=>"Mon, 21 Nov 2016 02:27:16 EST -05:00"}],
#  "8690"=>
#   [{"id"=>401,
#     "game_id"=>8690,
#     "user_id"=>1,
#     "line"=>-5,
#     "created_at"=>"Wed, 16 Nov 2016 21:55:59 EST -05:00",
#     "updated_at"=>"Wed, 16 Nov 2016 21:55:59 EST -05:00"}]}

If you really just want to modify the first occurence of line, you can just do :

data.values.first.first["line"] = "whatever"