Reputation: 51211
How do I change the key of an entry in a Python dictionary?
Upvotes: 630
Views: 781378
Reputation: 34
Replace the names of some keys in a dictionary. Maintain the original order of the key,value pairs in the dictionary without creating a new dictionary:
In this example, we replace the names of two of the three keys in dictionary d. We want to replace key name 'x' with 'a', and key name 'y' with 'b'. We want to maintain the original order of the dictionary, without creating a new dictionary.
NOTE: we cannot iterate over dictionary d, because we are making changes to dictionary d, so instead we will iterate over d.copy()
ONE WAY: Get key,value. Delete key,value pair. Add new_key,value pair. value stays the same (no change).
# Original dictionary:
d = {'x': 1, 'y': 2, 'z': 3}
# Conversion dictionary:
d1 = {'x': 'a', 'y': 'b'}
# Run through all the key,value pairs in copy of the
# dictionary d.
for key,value in d.copy().items():
if key in d1.keys():
# We get the name of the new_key value from the dictionary d1 with key.
new_key = d1[key]
else:
# No change to the name of the key
new_key = key
# If there is no change to the name of the key, we must
# delete the old key,value pair before creating
# the new key,value pair.
del d[key]
# The new key,value pair goes to the end of the dictionary.
d[new_key] = value
Output: d = {'a': 1, 'b': 2, 'z': 3}
ANOTHER WAY: Use dictionary.pop(key) to create new key,value pair and to delete the old key,value pair. ''' d = {'x': 1, 'y': 2, 'z': 3} d1 = {'x': 'a', 'y': 'b'}
for key in d.copy().keys():
if key in d1.keys():
new_key = d1[key]
d[new_key] = d.pop(key)
else:
# We need to do this if we want to keep
#the order of the dictionary.
d[key] = d.pop(key)
Output: d = {'a': 1, 'b': 2, 'z': 3}
Upvotes: 0
Reputation: 1703
For the keeping of order case (the other one is trivial, remove old and add new one) efficiently, avoiding the ordered-dictionary needing reconstruction (at least partially), I've put together a class (OrderedDictX) that extends OrderedDict and allows you to do key changes efficiently, i.e. in O(1) complexity. The implementation can also be adjusted for the now-ordered built-in dict class.
It uses 2 extra dictionaries to remap the changed keys ("external" - i.e. as they appear externally to the user) to the ones in the underlying OrderedDict ("internal") - the dictionaries will only hold keys that were changed so as long as no key changing is done they will be empty.
Performance measurements:
import timeit
import random
# Efficiency tests
from collections import MutableMapping
class OrderedDictRaymond(dict, MutableMapping):
def __init__(self, *args, **kwds):
if len(args) > 1:
raise TypeError('expected at 1 argument, got %d', len(args))
if not hasattr(self, '_keys'):
self._keys = []
self.update(*args, **kwds)
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
dict.__delitem__(self, key)
def clear(self):
del self._keys[:]
dict.clear(self)
def __setitem__(self, key, value):
if key not in self:
self._keys.append(key)
dict.__setitem__(self, key, value)
def __delitem__(self, key):
dict.__delitem__(self, key)
self._keys.remove(key)
def __iter__(self):
return iter(self._keys)
def __reversed__(self):
return reversed(self._keys)
def popitem(self):
if not self:
raise KeyError
key = self._keys.pop()
value = dict.pop(self, key)
return key, value
def __reduce__(self):
items = [[k, self[k]] for k in self]
inst_dict = vars(self).copy()
inst_dict.pop('_keys', None)
return (self.__class__, (items,), inst_dict)
setdefault = MutableMapping.setdefault
update = MutableMapping.update
pop = MutableMapping.pop
keys = MutableMapping.keys
values = MutableMapping.values
items = MutableMapping.items
def __repr__(self):
pairs = ', '.join(map('%r: %r'.__mod__, self.items()))
return '%s({%s})' % (self.__class__.__name__, pairs)
def copy(self):
return self.__class__(self)
@classmethod
def fromkeys(cls, iterable, value=None):
d = cls()
for key in iterable:
d[key] = value
return d
class obj_container:
def __init__(self, obj) -> None:
self.obj = obj
def change_key_splice(container, k_old, k_new):
od = container.obj
container.obj = OrderedDict((k_new if k == k_old else k, v) for k, v in od.items())
def change_key_raymond(container, k_old, k_new):
od = container.obj
od.rename(k_old, k_new)
def change_key_odx(container, k_old, k_new):
odx = container.obj
odx.change_key(k_old, k_new)
NUM_ITEMS = 20000
od_splice = OrderedDict([(x, x) for x in range(NUM_ITEMS)])
od_raymond = OrderedDictRaymond(od_splice.items())
odx = OrderedDictX(od_splice.items())
od_splice, od_raymond, odx = [obj_container(d) for d in [od_splice, od_raymond, odx]]
assert odx.obj == od_splice.obj
assert odx.obj == od_raymond.obj
# Pick randomly half of the keys to change
keys_to_change = random.sample(range(NUM_ITEMS), NUM_ITEMS//2)
print(f'OrderedDictX: {timeit.timeit(lambda: [change_key_odx(odx, k, k+NUM_ITEMS) for k in keys_to_change], number=1)}')
print(f'OrderedDictRaymond: {timeit.timeit(lambda: [change_key_raymond(od_raymond, k, k+NUM_ITEMS) for k in keys_to_change], number=1)}')
print(f'Splice: {timeit.timeit(lambda: [change_key_splice(od_splice, k, k+NUM_ITEMS) for k in keys_to_change], number=1)}')
assert odx.obj == od_splice.obj
assert odx.obj == od_raymond.obj
And results:
OrderedDictX: 0.06587849999999995
OrderedDictRaymond: 1.1131364
Splice: 1165.2614647
As expected, the splicing method is extremely slow (didn't expect it to be that much slower either though) and uses a lot of memory, and the O(N) Raymond solution is also slower, 17X times in this example.
Of course, this solution being O(1), compared to the O(N) OrderedDictRaymond the time difference becomes much more apparent as the dictionary size increases, e.g. for 5 times more elements (100000), the O(N) is now 100X slower:
NUM_ITEMS = 100000
OrderedDictX: 0.3636919999999999
OrderedDictRaymond: 36.3963971
Here's the code, please comment if you see issues or have improvements to propose as this might still be error-prone.
from collections import OrderedDict
class OrderedDictX(OrderedDict):
def __init__(self, *args, **kwargs):
# Mappings from new->old (ext2int), old->new (int2ext).
# Only the keys that are changed (internal key doesn't match what the user sees) are contained.
self._keys_ext2int = {}
self._keys_int2ext = {}
self.update(*args, **kwargs)
def change_key(self, k_old, k_new):
# Validate that the old key is part of the dict
if not self.__contains__(k_old):
raise Exception(f'Cannot rename key {k_old} to {k_new}: {k_old} not existing in dict')
# Return if no changing is actually to be done
if len(dict.fromkeys([k_old, k_new])) == 1:
return
# Validate that the new key would not conflict with another one
if self.__contains__(k_new):
raise Exception(f'Cannot rename key {k_old} to {k_new}: {k_new} already in dict')
# Preprocessing if key was already changed before
if k_old in self._keys_ext2int:
# Revert old change temporarily
k_old_int = self._keys_ext2int[k_old]
del self._keys_ext2int[k_old]
k_old = k_old_int
# Completely revert key-change and return if new key matches the internal key
if len(dict.fromkeys([k_old, k_new])) == 1:
del self._keys_int2ext[k_old]
return
# Finalize key change
self._keys_ext2int[k_new] = k_old
self._keys_int2ext[k_old] = k_new
def change_keys_multiple(self, rename_map: dict):
# For chaining changes: k1->k2, k2->k3, ..., kn->k_last
# we change backwards when k_last is not present;
# when k_last is k1 instead (circularity) we do almost the same
# except that we first change kn to a non-present key k_non_p,
# then after all the changes we can do the final change k_non_p->k1
already_changed, k_non_p = {}, None
for k_old, k_new in rename_map.items():
# If key was already changed due to chaining changes, we may advance
if k_old in already_changed:
continue
# If change is not chaining, directly change it
if k_new not in self:
self.change_key(k_old, k_new)
else:
# Change is chaining, find chain of changes
change_chain, is_circular = OrderedDict([(k_old, k_new)]), False
while True:
k_tail = next(iter(reversed(change_chain.values())))
# Chain ends if the tail key is no longer present or we detect circularity
if k_tail not in self:
break
if k_tail == k_old:
is_circular = True
break
# Continue chain
k_tail_new = rename_map[k_tail]
change_chain[k_tail] = k_tail_new
# Check for chain validity
if k_tail_new != k_old and k_tail_new in change_chain:
change_chain_str = ', '.join([f'{k1}->{k2}' for k1, k2 in change_chain.items()])
raise Exception(f'Invalid key-change chain detected: {change_chain_str}')
# NOP change (k->k) if length of chain is 1
if len(change_chain) == 1:
continue
# Circularity trick: first we change the last key to a generated non-present one
if is_circular:
if k_non_p is None:
k_non_p = self._get_non_present_key()
change_chain[next(iter(reversed(change_chain)))] = k_non_p
# Apply change chain in reverse
for k_old_chain, k_new_chain in reversed(change_chain.items()):
self.change_key(k_old_chain, k_new_chain)
already_changed[k_old_chain] = None
# Circularity trick: do the final rename
if is_circular:
self.change_key(k_non_p, k_old)
def _get_non_present_key(self):
i = len(self)
while i in self:
i -= 1
return i
def __contains__(self, k):
return (super().__contains__(k) and k not in self._keys_int2ext) or k in self._keys_ext2int
def __getitem__(self, k):
if not self.__contains__(k):
# Intentionally raise KeyError in ext2int
return self._keys_ext2int[k]
return super().__getitem__(self._keys_ext2int.get(k, k))
def __setitem__(self, k, v):
if k in self._keys_ext2int:
return super().__setitem__(self._keys_ext2int[k], v)
# If the key exists in the internal state but was renamed to a k_ext,
# employ this trick: make it such that it appears as if k_ext has also been renamed to k
if k in self._keys_int2ext:
k_ext = self._keys_int2ext[k]
self._keys_ext2int[k] = k_ext
k = k_ext
return super().__setitem__(k, v)
def __delitem__(self, k):
if not self.__contains__(k):
# Intentionally raise KeyError in ext2int
del self._keys_ext2int[k]
if k in self._keys_ext2int:
k_int = self._keys_ext2int[k]
del self._keys_ext2int[k]
del self._keys_int2ext[k_int]
k = k_int
return super().__delitem__(k)
def __iter__(self):
return self.keys()
def __reversed__(self):
for k in reversed(super().keys()):
yield self._keys_int2ext.get(k, k)
def __eq__(self, other: object) -> bool:
if not isinstance(other, dict):
return False
if len(self) != len(other):
return False
for (k, v), (k_other, v_other) in zip(self.items(), other.items()):
if k != k_other or v != v_other:
return False
return True
def get(self, k, default=None):
return self.__getitem__(k) if self.__contains__(k) else default
def popitem(self, last=True) -> tuple:
if not last:
k = next(iter(super().keys()))
else:
k = next(reversed(super().keys()))
v = super().__getitem__(k)
k = self._keys_int2ext.get(k, k)
self.__delitem__(k)
return k, v
class OrderedDictXKeysView:
def __init__(self, odx: 'OrderedDictX', orig_keys):
self._int2ext = odx._keys_int2ext
self._it = iter(orig_keys)
self._it_rev = reversed(orig_keys)
def __iter__(self):
return self
def __next__(self):
k = self._it.__next__()
return self._int2ext.get(k, k)
def __reversed__(self):
for k in self._it_rev:
yield self._int2ext.get(k, k)
class OrderedDictXItemsView:
def __init__(self, odx: 'OrderedDictX', orig_items):
self._int2ext = odx._keys_int2ext
self._it = iter(orig_items)
self._it_rev = reversed(orig_items)
def __iter__(self):
return self
def __next__(self):
k, v = self._it.__next__()
return self._int2ext.get(k, k), v
def __reversed__(self):
for k, v in self._it_rev:
yield self._int2ext.get(k, k), v
def keys(self):
return self.OrderedDictXKeysView(self, super().keys())
def items(self):
return self.OrderedDictXItemsView(self, super().items())
def copy(self):
return OrderedDictX(self.items())
# FIXME: move this to pytest
if __name__ == '__main__':
MAX = 25
items = [(i+1, i+1) for i in range(MAX)]
keys = [i[0] for i in items]
d = OrderedDictX(items)
# keys() before change
print(list(d.items()))
assert list(d.keys()) == keys
# __contains__ before change
assert 1 in d
# __getitem__ before change
assert d[1] == 1
# __setitem__ before change
d[1] = 100
assert d[1] == 100
d[1] = 1
assert d[1] == 1
# __delitem__ before change
assert MAX in d
del d[MAX]
assert MAX not in d
d[MAX] = MAX
assert MAX in d
print('== Tests before key change finished ==')
# change_key and __contains__
assert MAX-1 in d
assert MAX*2 not in d
d.change_key(MAX-1, MAX*2)
assert MAX-1 not in d
assert MAX*2 in d
# items() and keys()
items[MAX-2] = (MAX*2, MAX-1)
keys[MAX-2] = MAX*2
assert list(d.items()) == items
assert list(d.keys()) == keys
print(list(d.items()))
# __getitem__
assert d[MAX*2] == MAX-1
# __setitem__
d[MAX*2] = MAX*3
items[MAX-2] = (MAX*2, MAX*3)
keys[MAX-2] = MAX*2
assert list(d.items()) == items
assert list(d.keys()) == keys
# __delitem__
del d[MAX]
items = items[:-1]
keys = keys[:-1]
assert list(d.items()) == items
assert list(d.keys()) == keys
d[MAX] = MAX
items.append((MAX, MAX))
keys.append(MAX)
# __iter__
assert list(d) == keys
# __reversed__
print(list(reversed(d.items())))
assert list(reversed(d)) == list(reversed(keys))
assert list(reversed(d.keys())) == list(reversed(keys))
assert list(reversed(d.items())) == list(reversed(items))
# pop_item()
assert d.popitem() == (MAX, MAX)
assert d.popitem() == (MAX*2, MAX*3)
items = items[:-2]
keys = keys[:-2]
assert list(d.items()) == items
assert list(d.keys()) == keys
# update()
d.update({1: 1000, MAX-2: MAX*4})
items[0] = (1, 1000)
items[MAX-3] = (MAX-2, MAX*4)
assert list(d.items()) == items
assert list(d.keys()) == keys
# move_to_end()
d.move_to_end(1)
items = items[1:] + [items[0]]
keys = keys[1:] + [keys[0]]
assert list(d.items()) == items
assert list(d.keys()) == keys
assert list(d) == keys
# __eq__
d.change_key(1, 2000)
other_d = OrderedDictX(d.items())
assert d == other_d
assert other_d == d
import timeit
import random
# Efficiency tests
from collections import MutableMapping
class OrderedDictRaymond(dict, MutableMapping):
def __init__(self, *args, **kwds):
if len(args) > 1:
raise TypeError('expected at 1 argument, got %d', len(args))
if not hasattr(self, '_keys'):
self._keys = []
self.update(*args, **kwds)
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
dict.__delitem__(self, key)
def clear(self):
del self._keys[:]
dict.clear(self)
def __setitem__(self, key, value):
if key not in self:
self._keys.append(key)
dict.__setitem__(self, key, value)
def __delitem__(self, key):
dict.__delitem__(self, key)
self._keys.remove(key)
def __iter__(self):
return iter(self._keys)
def __reversed__(self):
return reversed(self._keys)
def popitem(self):
if not self:
raise KeyError
key = self._keys.pop()
value = dict.pop(self, key)
return key, value
def __reduce__(self):
items = [[k, self[k]] for k in self]
inst_dict = vars(self).copy()
inst_dict.pop('_keys', None)
return (self.__class__, (items,), inst_dict)
setdefault = MutableMapping.setdefault
update = MutableMapping.update
pop = MutableMapping.pop
keys = MutableMapping.keys
values = MutableMapping.values
items = MutableMapping.items
def __repr__(self):
pairs = ', '.join(map('%r: %r'.__mod__, self.items()))
return '%s({%s})' % (self.__class__.__name__, pairs)
def copy(self):
return self.__class__(self)
@classmethod
def fromkeys(cls, iterable, value=None):
d = cls()
for key in iterable:
d[key] = value
return d
class obj_container:
def __init__(self, obj) -> None:
self.obj = obj
def change_key_splice(container, k_old, k_new):
od = container.obj
container.obj = OrderedDict((k_new if k == k_old else k, v) for k, v in od.items())
def change_key_raymond(container, k_old, k_new):
od = container.obj
od.rename(k_old, k_new)
def change_key_odx(container, k_old, k_new):
odx = container.obj
odx.change_key(k_old, k_new)
NUM_ITEMS = 100000
od_splice = OrderedDict([(x, x) for x in range(NUM_ITEMS)])
od_raymond = OrderedDictRaymond(od_splice.items())
odx = OrderedDictX(od_splice.items())
od_splice, od_raymond, odx = [obj_container(d) for d in [od_splice, od_raymond, odx]]
assert odx.obj == od_splice.obj
assert odx.obj == od_raymond.obj
# Pick randomly half of the keys to change
keys_to_change = random.sample(range(NUM_ITEMS), NUM_ITEMS//2)
print(f'OrderedDictX: {timeit.timeit(lambda: [change_key_odx(odx, k, k+NUM_ITEMS) for k in keys_to_change], number=1)}')
#print(f'OrderedDictRaymond: {timeit.timeit(lambda: [change_key_raymond(od_raymond, k, k+NUM_ITEMS) for k in keys_to_change], number=1)}')
#print(f'Splice: {timeit.timeit(lambda: [change_key_splice(od_splice, k, k+NUM_ITEMS) for k in keys_to_change], number=1)}')
# assert odx.obj == od_splice.obj
# assert odx.obj == od_raymond.obj
keys_odx = list(odx.obj)
keys_raymond = list(od_raymond.obj)
keys_splice = list(od_splice.obj)
print('== items() ==')
print(f'OrderedDictX: {timeit.timeit(lambda: [(k, v) for k, v in odx.obj.items()], number=1)}')
print(f'OrderedDictRaymond: {timeit.timeit(lambda: [(k, v) for k, v in od_raymond.obj.items()], number=1)}')
print(f'OrderedDict: {timeit.timeit(lambda: [(k, v) for k, v in od_splice.obj.items()], number=1)}')
print('== keys() ==')
print(f'OrderedDictX: {timeit.timeit(lambda: [k for k in odx.obj.keys()], number=1)}')
print(f'OrderedDictRaymond: {timeit.timeit(lambda: [k for k in od_raymond.obj.keys()], number=1)}')
print(f'OrderedDict: {timeit.timeit(lambda: [k for k in od_splice.obj.keys()], number=1)}')
print('== list() ==')
print(f'OrderedDictX: {timeit.timeit(lambda: list(odx.obj), number=1)}')
print(f'OrderedDictRaymond: {timeit.timeit(lambda: list(od_raymond.obj), number=1)}')
print(f'OrderedDict: {timeit.timeit(lambda: list(od_splice.obj), number=1)}')
print('== in ==')
print(f'OrderedDictX: {timeit.timeit(lambda: [k in odx.obj for k in keys_odx], number=1)}')
print(f'OrderedDictRaymond: {timeit.timeit(lambda: [k in od_raymond.obj for k in keys_raymond], number=1)}')
print(f'OrderedDict: {timeit.timeit(lambda: [k in od_splice.obj for k in keys_splice], number=1)}')
print('== get ==')
print(f'OrderedDictX: {timeit.timeit(lambda: [odx.obj[k] for k in keys_odx], number=1)}')
print(f'OrderedDictRaymond: {timeit.timeit(lambda: [od_raymond.obj[k] for k in keys_raymond], number=1)}')
print(f'OrderedDict: {timeit.timeit(lambda: [od_splice.obj[k] for k in keys_splice], number=1)}')
Upvotes: 0
Reputation: 21
I have keys stored in a list (col_names)
Using this list I have updated all my keys present in data_dict
data_dict = { key:value for key, value in zip(col_names,list(data_dict.values())) }
Upvotes: 0
Reputation: 381
You can use iff/else dictionary comprehension. This method allows you to replace an arbitrary number of keys in one line AND does not require you to change all of them.
key_map_dict = {'a':'apple','c':'cat'}
d = {'a':1,'b':2,'c':3}
d = {(key_map_dict[k] if k in key_map_dict else k):v for (k,v) in d.items() }
Returns {'apple':1,'b':2,'cat':3}
Upvotes: 13
Reputation: 317
I just had to help my wife do something like those for a python class, so I made this code to show her how to do it. Just like the title says, it only replaces a key name. It's very rare that you have to replace just a key name, and keep the order of the dictionary intact but figured I'd share anyway since this post is what Goggle returns when you search for it even though it's a very old thread.
Code:
dictionary = {
"cat": "meow",
"dog": "woof",
"cow": "ding ding ding",
"goat": "beh"
}
def countKeys(dictionary):
num = 0
for key, value in dictionary.items():
num += 1
return num
def keyPosition(dictionary, search):
num = 0
for key, value in dictionary.items():
if key == search:
return num
num += 1
def replaceKey(dictionary, position, newKey):
num = 0
updatedDictionary = {}
for key, value in dictionary.items():
if num == position:
updatedDictionary.update({newKey: value})
else:
updatedDictionary.update({key: value})
num += 1
return updatedDictionary
for x in dictionary:
print("A", x, "goes", dictionary[x])
numKeys = countKeys(dictionary)
print("There are", numKeys, "animals in this list.\n")
print("Woops, that's not what a cow says...")
keyPos = keyPosition(dictionary, "cow")
print("Cow is in the", keyPos, "position, lets put a fox there instead...\n")
dictionary = replaceKey(dictionary, keyPos, "fox")
for x in dictionary:
print("A", x, "goes", dictionary[x])
Output:
A cat goes meow
A dog goes woof
A cow goes ding ding ding
A goat goes beh
There are 4 animals in this list.
Woops, that's not what a cow says...
Cow is in the 2 position, lets put a fox there instead...
A cat goes meow
A dog goes woof
A fox goes ding ding ding
A goat goes beh
Upvotes: 1
Reputation: 1022
Replacing spaces in dict keys with underscores, I use this simple route ...
for k in dictionary.copy():
if ' ' in k:
dictionary[ k.replace(' ', '_') ] = dictionary.pop(k, 'e r r')
Or just dictionary.pop(k)
Note 'e r r', which can be any string, would become the new value if the key is not in the dictionary to be able to replace it, which can't happen here. The argument is optional, in other similar code where KeyError might be hit, that added arg avoids it and yet can create a new key with that 'e r r' or whatever you set it to as the value.
.copy() avoids ... dictionary changed size during iteration.
.keys() not needed, k is each key, k stands for key in my head.
(I'm using v3.7)
What's the one-liner for the loop above?
Upvotes: 2
Reputation: 144
With pandas you can have something like this,
from pandas import DataFrame
df = DataFrame([{"fruit":"apple", "colour":"red"}])
df.rename(columns = {'fruit':'fruit_name'}, inplace = True)
df.to_dict('records')[0]
>>> {'fruit_name': 'apple', 'colour': 'red'}
Upvotes: 0
Reputation: 1
I wrote this function below where you can change the name of a current key name to a new one.
def change_dictionary_key_name(dict_object, old_name, new_name):
'''
[PARAMETERS]:
dict_object (dict): The object of the dictionary to perform the change
old_name (string): The original name of the key to be changed
new_name (string): The new name of the key
[RETURNS]:
final_obj: The dictionary with the updated key names
Take the dictionary and convert its keys to a list.
Update the list with the new value and then convert the list of the new keys to
a new dictionary
'''
keys_list = list(dict_object.keys())
for i in range(len(keys_list)):
if (keys_list[i] == old_name):
keys_list[i] = new_name
final_obj = dict(zip(keys_list, list(dict_object.values())))
return final_obj
Assuming a JSON you can call it and rename it by the following line:
data = json.load(json_file)
for item in data:
item = change_dictionary_key_name(item, old_key_name, new_key_name)
Conversion from list to dictionary keys has been found here:
https://www.geeksforgeeks.org/python-ways-to-change-keys-in-dictionary/
Upvotes: 0
Reputation: 321
Be aware of the position of pop:
Put the key you want to delete after pop()
orig_dict['AAAAA'] = orig_dict.pop('A')
orig_dict = {'A': 1, 'B' : 5, 'C' : 10, 'D' : 15}
# printing initial
print ("original: ", orig_dict)
# changing keys of dictionary
orig_dict['AAAAA'] = orig_dict.pop('A')
# printing final result
print ("Changed: ", str(orig_dict))
Upvotes: 1
Reputation: 1336
An example of complete solution
Declare a json file which contains mapping you want
{
"old_key_name": "new_key_name",
"old_key_name_2": "new_key_name_2",
}
Load it
with open("<filepath>") as json_file:
format_dict = json.load(json_file)
Create this function to format a dict with your mapping
def format_output(dict_to_format,format_dict):
for row in dict_to_format:
if row in format_dict.keys() and row != format_dict[row]:
dict_to_format[format_dict[row]] = dict_to_format.pop(row)
return dict_to_format
Upvotes: 0
Reputation: 186
This will lowercase all your dict keys. Even if you have nested dict or lists. You can do something similar to apply other transformations.
def lowercase_keys(obj):
if isinstance(obj, dict):
obj = {key.lower(): value for key, value in obj.items()}
for key, value in obj.items():
if isinstance(value, list):
for idx, item in enumerate(value):
value[idx] = lowercase_keys(item)
obj[key] = lowercase_keys(value)
return obj
json_str = {"FOO": "BAR", "BAR": 123, "EMB_LIST": [{"FOO": "bar", "Bar": 123}, {"FOO": "bar", "Bar": 123}], "EMB_DICT": {"FOO": "BAR", "BAR": 123, "EMB_LIST": [{"FOO": "bar", "Bar": 123}, {"FOO": "bar", "Bar": 123}]}}
lowercase_keys(json_str)
Out[0]: {'foo': 'BAR',
'bar': 123,
'emb_list': [{'foo': 'bar', 'bar': 123}, {'foo': 'bar', 'bar': 123}],
'emb_dict': {'foo': 'BAR',
'bar': 123,
'emb_list': [{'foo': 'bar', 'bar': 123}, {'foo': 'bar', 'bar': 123}]}}
Upvotes: 2
Reputation: 10572
Method if anyone wants to replace all occurrences of the key in a multi-level dictionary.
Function checks if the dictionary has a specific key and then iterates over sub-dictionaries and invokes the function recursively:
def update_keys(old_key,new_key,d):
if isinstance(d,dict):
if old_key in d:
d[new_key] = d[old_key]
del d[old_key]
for key in d:
updateKey(old_key,new_key,d[key])
update_keys('old','new',dictionary)
Upvotes: 0
Reputation: 51
this function gets a dict, and another dict specifying how to rename keys; it returns a new dict, with renamed keys:
def rekey(inp_dict, keys_replace):
return {keys_replace.get(k, k): v for k, v in inp_dict.items()}
test:
def test_rekey():
assert rekey({'a': 1, "b": 2, "c": 3}, {"b": "beta"}) == {'a': 1, "beta": 2, "c": 3}
Upvotes: 5
Reputation: 2852
In python 2.7 and higher, you can use dictionary comprehension: This is an example I encountered while reading a CSV using a DictReader. The user had suffixed all the column names with ':'
ori_dict = {'key1:' : 1, 'key2:' : 2, 'key3:' : 3}
to get rid of the trailing ':' in the keys:
corrected_dict = { k.replace(':', ''): v for k, v in ori_dict.items() }
Upvotes: 44
Reputation: 209
d = {1:2,3:4}
suppose that we want to change the keys to the list elements p=['a' , 'b']. the following code will do:
d=dict(zip(p,list(d.values())))
and we get
{'a': 2, 'b': 4}
Upvotes: 20
Reputation: 156
To convert all the keys in the dictionary
Suppose this is your dictionary:
>>> sample = {'person-id': '3', 'person-name': 'Bob'}
To convert all the dashes to underscores in the sample dictionary key:
>>> sample = {key.replace('-', '_'): sample.pop(key) for key in sample.keys()}
>>> sample
>>> {'person_id': '3', 'person_name': 'Bob'}
Upvotes: 5
Reputation: 567
In case of changing all the keys at once. Here I am stemming the keys.
a = {'making' : 1, 'jumping' : 2, 'climbing' : 1, 'running' : 2}
b = {ps.stem(w) : a[w] for w in a.keys()}
print(b)
>>> {'climb': 1, 'jump': 2, 'make': 1, 'run': 2} #output
Upvotes: 4
Reputation: 71
If you have a complex dict, it means there is a dict or list within the dict:
myDict = {1:"one",2:{3:"three",4:"four"}}
myDict[2][5] = myDict[2].pop(4)
print myDict
Output
{1: 'one', 2: {3: 'three', 5: 'four'}}
Upvotes: 7
Reputation: 138317
Easily done in 2 steps:
dictionary[new_key] = dictionary[old_key]
del dictionary[old_key]
Or in 1 step:
dictionary[new_key] = dictionary.pop(old_key)
which will raise KeyError
if dictionary[old_key]
is undefined. Note that this will delete dictionary[old_key]
.
>>> dictionary = { 1: 'one', 2:'two', 3:'three' }
>>> dictionary['ONE'] = dictionary.pop(1)
>>> dictionary
{2: 'two', 3: 'three', 'ONE': 'one'}
>>> dictionary['ONE'] = dictionary.pop(1)
Traceback (most recent call last):
File "<input>", line 1, in <module>
KeyError: 1
Upvotes: 1171
Reputation: 383
I haven't seen this exact answer:
dict['key'] = value
You can even do this to object attributes. Make them into a dictionary by doing this:
dict = vars(obj)
Then you can manipulate the object attributes like you would a dictionary:
dict['attribute'] = value
Upvotes: -4
Reputation: 123413
Since keys are what dictionaries use to lookup values, you can't really change them. The closest thing you can do is to save the value associated with the old key, delete it, then add a new entry with the replacement key and the saved value. Several of the other answers illustrate different ways this can be accomplished.
Upvotes: 16
Reputation: 6913
if you want to change all the keys:
d = {'x':1, 'y':2, 'z':3}
d1 = {'x':'a', 'y':'b', 'z':'c'}
In [10]: dict((d1[key], value) for (key, value) in d.items())
Out[10]: {'a': 1, 'b': 2, 'c': 3}
if you want to change single key: You can go with any of the above suggestion.
Upvotes: 114
Reputation: 26088
pop'n'fresh
>>>a = {1:2, 3:4}
>>>a[5] = a.pop(1)
>>>a
{3: 4, 5: 2}
>>>
Upvotes: 52
Reputation: 70108
No direct way to do this, but you can delete-then-assign
d = {1:2,3:4}
d[newKey] = d[1]
del d[1]
or do mass key changes:
d = dict((changeKey(k), v) for k, v in d.items())
Upvotes: 8
Reputation: 11539
You can associate the same value with many keys, or just remove a key and re-add a new key with the same value.
For example, if you have keys->values:
red->1
blue->2
green->4
there's no reason you can't add purple->2
or remove red->1
and add orange->1
Upvotes: 0