Is there a way to rename a dictionary key, without reassigning its value to a new name and removing the old name key; and without iterating through dict key/value?
In case of OrderedDict, do the same, while keeping that key’s position.
python – Rename a dictionary key
The Question :
- what exactly do you mean by “without reassigning its value to a new name and removing the old name key”? the way i see it, that is the definition of renaming a key, and all of the answers below reassign the value and remove the old key name. you have yet to accept an answer, so perhaps these haven’t accomplished what you’re seeking?
- You really need to specify version number(s). As of Python 3.7, the language spec now guarantees that dicts follow insertion order. That also makes OrderedDict mostly obsolete (unless a) you want code that also backports to 2.x or 3.6- or b) you care about the issues listed in Will OrderedDict become redundant in Python 3.7?). And back in 3.6, dictionary insertion order was guaranteed by the CPython implementation (but not the language spec).
- @smci In Python 3.7 dicts follow insertion order, however there are still different from
OrderedDict
. dicts ignore order when they are being compared for equality, whereasOrderedDict
take order into account when being compared. I know you linked to something that explains it, but I thought your comment might have misled those who may not have read that link. - @smci I disagree with your opinion that differences between
dict
andOrderedDict
can be glossed over and thatOrderedDict
is now obsolete. I think also debating this opinion is out-of-topic for this question. The question you linked to is a better place to talk about it. Interestingly, the only upvoted answer on the question you linked to agrees that the differences matter and thatOrderedDict
is not obsolete. If you have a different answer, go post it there and let the community vote. stackoverflow.com/q/50872498/247696 - Does this answer your question? Change the name of a key in dictionary
The Answer 1
For a regular dict, you can use:
mydict[k_new] = mydict.pop(k_old)
This will move the item to the end of the dict, unless k_new
was already existing in which case it will overwrite the value in-place.
For a Python 3.7+ dict where you additionally want to preserve the ordering, the simplest is to rebuild an entirely new instance. For example, renaming key 2
to 'two'
:
>>> d = {0:0, 1:1, 2:2, 3:3} >>> {"two" if k == 2 else k:v for k,v in d.items()} {0: 0, 1: 1, 'two': 2, 3: 3}
The same is true for an OrderedDict
, where you can’t use dict comprehension syntax, but you can use a generator expression:
OrderedDict((k_new if k == k_old else k, v) for k, v in od.items())
Modifying the key itself, as the question asks for, is impractical because keys are hashable which usually implies they’re immutable and can’t be modified.
The Answer 2
Using a check for newkey!=oldkey
, this way you can do:
if newkey!=oldkey: dictionary[newkey] = dictionary[oldkey] del dictionary[oldkey]
The Answer 3
In case of renaming all dictionary keys:
target_dict = {'k1':'v1', 'k2':'v2', 'k3':'v3'} new_keys = ['k4','k5','k6'] for key,n_key in zip(target_dict.keys(), new_keys): target_dict[n_key] = target_dict.pop(key)
The Answer 4
You can use this OrderedDict recipe
written by Raymond Hettinger and modify it to add a rename
method, but this is going to be a O(N) in complexity:
def rename(self,key,new_key): ind = self._keys.index(key) #get the index of old key, O(N) operation self._keys[ind] = new_key #replace old key with new key in self._keys self[new_key] = self[key] #add the new key, this is added at the end of self._keys self._keys.pop(-1) #pop the last item in self._keys
Example:
dic = OrderedDict((("a",1),("b",2),("c",3))) print dic dic.rename("a","foo") dic.rename("b","bar") dic["d"] = 5 dic.rename("d","spam") for k,v in dic.items(): print k,v
output:
OrderedDict({'a': 1, 'b': 2, 'c': 3}) foo 1 bar 2 c 3 spam 5
The Answer 5
A few people before me mentioned the .pop
trick to delete and create a key in a one-liner.
I personally find the more explicit implementation more readable:
d = {'a': 1, 'b': 2} v = d['b'] del d['b'] d['c'] = v
The code above returns {'a': 1, 'c': 2}
The Answer 6
Other answers are pretty good.But in python3.6, regular dict also has order. So it’s hard to keep key’s position in normal case.
def rename(old_dict,old_name,new_name): new_dict = {} for key,value in zip(old_dict.keys(),old_dict.values()): new_key = key if key != old_name else new_name new_dict[new_key] = old_dict[key] return new_dict
The Answer 7
In Python 3.6 (onwards?) I would go for the following one-liner
test = {'a': 1, 'old': 2, 'c': 3} old_k = 'old' new_k = 'new' new_v = 4 # optional print(dict((new_k, new_v) if k == old_k else (k, v) for k, v in test.items()))
which produces
{'a': 1, 'new': 4, 'c': 3}
May be worth noting that without the print
statement the ipython console/jupyter notebook present the dictionary in an order of their choosing…
The Answer 8
I have combined some answers from the above thread and come up with the solution below. Although it is simple it can be used as a building block for making more complex key updates from a dictionary.
test_dict = {'a': 1, 'b': 2, 'c': 3} print(test_dict) # {'a': 1, 'b': 2, 'c': 3} prefix = 'up' def dict_key_update(json_file): new_keys = [] old_keys = [] for i,(key,value) in enumerate(json_file.items()): old_keys.append(key) new_keys.append(str(prefix) + key) # i have updated by adding a prefix to the # key for old_key, new_key in zip(old_keys,new_keys): print('old {}, new {}'.format(old_key, new_key)) if new_key!=old_key: json_file[new_key] = json_file.pop(old_key) return json_file test_dict = dict_key_update(test_dict) print(test_dict) # {'upa': 1, 'upb': 2, 'upc': 3}
The Answer 9
I am using @wim ‘s answer above, with dict.pop() when renaming keys, but I found a gotcha. Cycling through the dict to change the keys, without separating the list of old keys completely from the dict instance, resulted in cycling new, changed keys into the loop, and missing some existing keys.
To start with, I did it this way:
for current_key in my_dict: new_key = current_key.replace(':','_') fixed_metadata[new_key] = fixed_metadata.pop(current_key)
I found that cycling through the dict in this way, the dictionary kept finding keys even when it shouldn’t, i.e., the new keys, the ones I had changed! I needed to separate the instances completely from each other to (a) avoid finding my own changed keys in the for loop, and (b) find some keys that were not being found within the loop for some reason.
I am doing this now:
current_keys = list(my_dict.keys()) for current_key in current_keys: and so on...
Converting the my_dict.keys() to a list was necessary to get free of the reference to the changing dict. Just using my_dict.keys() kept me tied to the original instance, with the strange side effects.
The Answer 10
In case someone wants to rename all the keys at once providing a list with the new names:
def rename_keys(dict_, new_keys): """ new_keys: type List(), must match length of dict_ """ # dict_ = {oldK: value} # d1={oldK:newK,} maps old keys to the new ones: d1 = dict( zip( list(dict_.keys()), new_keys) ) # d1{oldK} == new_key return {d1[oldK]: value for oldK, value in dict_.items()}
The Answer 11
@helloswift123 I like your function. Here is a modification to rename multiple keys in a single call:
def rename(d, keymap): """ :param d: old dict :type d: dict :param keymap: [{:keys from-keys :values to-keys} keymap] :returns: new dict :rtype: dict """ new_dict = {} for key, value in zip(d.keys(), d.values()): new_key = keymap.get(key, key) new_dict[new_key] = d[key] return new_dict
The Answer 12
Suppose you want to rename key k3 to k4:
temp_dict = {'k1':'v1', 'k2':'v2', 'k3':'v3'} temp_dict['k4']= temp_dict.pop('k3')
The Answer 13
I came up with this function which does not mutate the original dictionary. This function also supports list of dictionaries too.
import functools from typing import Union, Dict, List def rename_dict_keys( data: Union[Dict, List[Dict]], old_key: str, new_key: str ): """ This function renames dictionary keys :param data: :param old_key: :param new_key: :return: Union[Dict, List[Dict]] """ if isinstance(data, dict): res = {k: v for k, v in data.items() if k != old_key} try: res[new_key] = data[old_key] except KeyError: raise KeyError( "cannot rename key as old key '%s' is not present in data" % old_key ) return res elif isinstance(data, list): return list( map( functools.partial( rename_dict_keys, old_key=old_key, new_key=new_key ), data, ) ) raise ValueError("expected type List[Dict] or Dict got '%s' for data" % type(data))