How do you duplicate a Dictionary in a way that changes to the duplicate do not effect the original?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By AniMerrill

So I have a situation where I’m opening and parsing a JSON file into a dictionary object, and the JSON file was kind of a hacky way to get some events working for a UI system (basically same layout with some buttons, the JSON data outlines events with what events the buttons call after, etc.). It works great except I had a problem when I started adding variables that could potentially change the event before it’s called. Here’s a simplified example:

JSON:

{
   "vars" : {
      "key_collected" : false
   }
   "events" : {
      "some_event" : { ... }
      "some_event_2" : { ... }
      "event_that_needs_key" : {
         "sprite_info" : { ... }
         "flavor_text" : "There's a locked door..."
         "buttons" : [
            { "text" : "Open Door", "event" : "opening_door_event" }
            { "text" : "Leave", "event" : "leaving_event" }
         ]
      }
   }
}

And so from here I found out how to change the values for key_collected and other variables just to make sure they could be changed and used as I expected, and so my idea was that upon the trigger that calls the event_that_needs_key was to check for key_collected (which worked) and if true it just runs the event as normal but if false then pop_front() off of the button array from a duplicate() of this event and run that instead. What would happen is that the check did work, but if you went to the event before getting the key and then go back the button got permanently deleted until the program restarted and it read from the JSON file again.

For some reason I got the duplicate() function to work on a different structure elsewhere in the program (it was a node dictionary for A* style pathfinding) but it didn’t work for this one, so I hadn’t run into the problem of deep copies etc. before in Godot (although I was aware of the problem from a long time ago in C++ classes). I tried what some answers I found suggested where you do a for keys in dictionary loop but it seemed to have the same problem when I tried to pop_front() off that button array.

I eventually realized I left the JSON file open (because I’m a bad programmer lol) so I fudged it by recopying that one specific function from the file data and doing essentially the same thing, but I would kind of like to know what the proper procedure is here. Up until I had this problem I thought the whole point of the duplicate() function was to make a separate “deep” copy of a dictionary that would transfer data into a separate dictionary reference.

:bust_in_silhouette: Reply From: hilfazer

Duplicate() copies each element of a dictionary but it does not call duplicate() (deep copy) on them. You have other dicts as well as arrays in your dictionary - they are being shallow copied.
You may need to call duplicate() on each dict element that needs it.

If speed is not a concern you can use:

var dict2 = str2var( var2str(dict1) )

Ahh that makes sense. I think the other thing that was hanging me up is I didn’t realize that Arrays ALSO have a duplicate function and will otherwise shallow copy. For future readers, this is how I managed to fix it (and apologies for confusion between a key to unlock a door and a key in a dictionary):

for key in json_events["event_that_needs_key"]:
	if(typeof(json_events["event_that_needs_key"][key]) == TYPE_ARRAY ||
		typeof(json_events["event_that_needs_key"][key]) == TYPE_DICTIONARY):
			temp[key] = json_events["event_that_needs_key"][key].duplicate()
	else:
		temp[key] = json_events["event_that_needs_key"][key]

Also I was not aware that there was a function that convert strings and variables back and forth, that one also worked for me. Out of curiosity though, just because I’ve never really bothered looking into the engine proper, would it really be a performance decrease over copying a dictionary this small anyway? My game is not performance intensive, it’s a simple turn based type of thing, but I guess for future readers or my own reference do you or anyone else have an opinion on that?

Thank you very much for your help though, that suggestion really fixed it up.

AniMerrill | 2018-03-30 17:34