Add a sort method to Dictionary and HashMap
Implements this proposal and closes https://github.com/godotengine/godot-proposals/issues/7060
This PR adds a method to Dictionary, and internally in HashMap, to sort in-place by key. This can be used to ensure dictionaries with the same contents produce equivalent results when getting the keys, getting the values, and converting to a string. This is also useful when wanting a JSON representation consistent with what is in memory, and useful for storing on a database that requires dictionaries to be sorted.
func _ready():
var dict = {
"key4": "value4",
"key2": "value2",
"key1": "value1",
"key3": "value3",
}
dict.sort()
print(dict) # { "key1": "value1", "key2": "value2", "key3": "value3", "key4": "value4" }
I used insertion sort because we want this operation to be fast for the common case where the input is already sorted or nearly sorted. The worst case is O(n²) but the best case is O(n).
I am planning to use this method in The Mirror, and while I could just implement in GDScript or keep it to our engine fork, this seems like it would be broadly useful to have in upstream core.
Perhaps we also need an insert() method? Right now there is no way to move a key in the dictionary other than to remove it and all the keys after it and then reinsert them all.
# Probably fast (to find before_key by hash).
insert_before_key(key: Variant, value: Variant, before_key: Variant)
# Probably slow (double linked list).
insert_at_index(key: Variant, value: Variant, at_index: int)
@dalexeev Yeah that makes sense. It might also be sensible to have an insert without any specified before_key or at_index that would assume the Dictionary was sorted and just insert a new value.
@dalexeev
Perhaps we also need an insert() method?
This would be great and might be useful for my RC client too.
It might also be sensible to have an insert without any specified before_key or at_index that would assume the Dictionary was sorted and just insert a new value.
What difference would it make to Dictionary's operator []? I guess though that just having a method equivalent for it might be useful by itself, insert() would also pair up nicely with get() ig.
Dictionaries use insertion order for determinism, so for the vast majority of cases, you don't need this. If you really wanted something like this, you can do a function that takes the keys, sorts them and rebuilds the dictionary, but it seems like a very corner case functionality. I am more on the side of leaving it out.
Dictionaries are already auto-sorted when you serialize them to string. While it's supper inefficient, you should be able to do str_to_var(var_to_str(dict)) to sort a Dictionary.
@reduz I understand the determinism of having keys in the inserted order, but I need this for a different kind of determinism:
- Identical data should be stored in a consistent order, so I want to sort it to ensure this.
- Inserting in a different order and then sorting should result in the same final data.
- Serializing to a JSON file reading back should keep the data consistent.
- Saving to a database and reading it back should keep the data consistent.
I really don't think that this is an unreasonable use case. Perhaps Godot hasn't had this before because hobbyist projects either haven't done much advanced operations on Dictionaries or have used hacks like str_to_var(var_to_str(dict)).
Thanks!