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.
- 105
- 6
- 12,398
- 3
- 19
- 15
-
7what 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? – dbliss Apr 06 '15 at 18:09
-
11You really need to specify version number(s). **[As of Python 3.7, the language spec now guarantees that dicts follow insertion order](https://docs.python.org/3/whatsnew/3.7.html)**. 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?*](https://stackoverflow.com/questions/50872498/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 Mar 04 '19 at 05:38
-
5@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, whereas `OrderedDict` 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. – Flimm Dec 05 '19 at 13:08
-
@Flimm: that's true but I can't see that it matters, this question asks two questions-in-one, and the intent of the second part been superseded. *"dicts ignore order when they are being compared for equality"* Yes because they're not supposed to compare by order. *"whereas `OrderedDict` takes order into account when being compared"* Ok but noone cares post-3.7. I claim `OrderedDict` is largely obsolete, can you articulate reasons why it isn't? e.g. [here](https://stackoverflow.com/questions/50872498/will-ordereddict-become-redundant-in-python-3-7) is not persuasive unless you need `reversed()` – smci Dec 06 '19 at 06:58
-
@Flimm: I can't find any credible arguments why OrderedDict isn't obsolete on code in 3.7+ unless it has to be compatible with pre-3.7 or 2.x. So e.g. [this](http://gandenberger.org/2018/03/10/ordered-dicts-vs-ordereddict/) isn't at all persuasive. In particular *"using an OrderedDict communicates your intention..."* is an appalling argument for completely necessary technical debt and obfuscation. People should simply switch back to dict and get on with it. That simple. – smci Dec 06 '19 at 08:11
-
2@smci I disagree with your opinion that differences between `dict` and `OrderedDict` can be glossed over and that `OrderedDict` 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 that `OrderedDict` is not obsolete. If you have a different answer, go post it there and let the community vote. https://stackoverflow.com/q/50872498/247696 – Flimm Dec 06 '19 at 08:21
-
3Duplicate of https://stackoverflow.com/q/4406501/2506522 ? – betontalpfa Aug 28 '20 at 22:10
-
2Does this answer your question? [Change the name of a key in dictionary](https://stackoverflow.com/questions/4406501/change-the-name-of-a-key-in-dictionary) – betontalpfa Aug 28 '20 at 22:10
13 Answers
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.
- 302,178
- 90
- 548
- 690
-
1For python 3.5, I think `dict.pop` is workable for an OrderedDict based on my test. – Daniel Dec 05 '17 at 13:30
-
8Nope, `OrderedDict` will preserve insertion order, which is not what the question asked about. – wim Dec 05 '17 at 16:20
-
When using the approach with rebuilding an entirely new instance (although only single key has to be changed), what would be the performance for really huge dictionaries (lot of items)? – Nerxis Jun 15 '21 at 07:46
Using a check for newkey!=oldkey, this way you can do:
if newkey!=oldkey:
dictionary[newkey] = dictionary[oldkey]
del dictionary[oldkey]
- 10,118
- 5
- 41
- 48
- 10,631
- 4
- 42
- 67
-
6this works beautifully, but does not maintain the original order because the new key gets added at the end by default (in python3). – szeitlin Oct 01 '18 at 21:14
-
5@szeitlin you shouldn't rely on `dict` order, even if python3.6+ intializes it in ordered form, previous versions don't and it's not really a feature just an afteraffect of how py3.6+ dicts were change. Use `OrderedDict` if you care about order. – Granitosaurus Nov 15 '18 at 08:05
-
4Thanks @Granitosaurus, I did not need you to explain that to me. That was not the point of my comment. – szeitlin Nov 15 '18 at 19:30
-
12@szeitlin your comment implied that the fact that it changes the dict order matters in some way, shape or form when in reality no one should rely on dictionary order so this fact is completely irrelevant – Granitosaurus Nov 16 '18 at 07:51
-
2So, the above argumentative comments were relevant in Python 3.6, but as of 3.7+, [dicts are definitely ordered](https://mail.python.org/pipermail/python-dev/2017-December/151283.html). – mattdm Jul 31 '21 at 14:45
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)
-
3Is `target_dict.keys()` guaranteed to be the same order as in definition? I thought a Python dict is unordered, and the order from a dict's keys view is unpredictable – ttimasdf Jan 14 '20 at 07:55
-
This has changed since Python 3.6 see https://docs.python.org/3/library/collections.html#collections.OrderedDict – Sebastian Feb 17 '21 at 16:32
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
- 232,417
- 55
- 437
- 487
-
1@MERose Add the Python file somewhere in your module search path and import `OrderedDict` from it. But I would recommend: Create a class that inherits from `OrderedDict` and add a `rename` method to it. – Ashwini Chaudhary Jun 09 '15 at 10:09
-
Seems like OrderedDict has since been rewritten to use a doubly-linked list, so there's probably still a way to do this, but it requires a lot more acrobatics. :-/ – szeitlin Oct 01 '18 at 21:36
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}
- 12,532
- 6
- 50
- 100
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
- 123
- 4
- 13
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...
- 1,105
- 9
- 19
Suppose you want to rename key k3 to k4:
temp_dict = {'k1':'v1', 'k2':'v2', 'k3':'v3'}
temp_dict['k4']= temp_dict.pop('k3')
- 269
- 2
- 6
-
3
-
1This will return a `TypeError: 'dict' object is not callable` If with `temp_dict('k3')` you are trying to reference the value of the `k3` key then you should use square brackets and not parentheses. However, this will just add a new key to the dictionary and won't rename an existing key, as requested. – Mewtwo Nov 11 '19 at 10:28
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()}
- 231
- 2
- 10
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.
- 589
- 1
- 7
- 15
@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
- 524
- 4
- 11
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))
- 976
- 2
- 17
- 39
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}
- 371
- 3
- 8