user469652
user469652

Reputation: 51211

Change the name of a key in dictionary

How do I change the key of an entry in a Python dictionary?

Upvotes: 630

Views: 781378

Answers (25)

rich neadle
rich neadle

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

Zuzu Corneliu
Zuzu Corneliu

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

Muhammad Hashim Khan
Muhammad Hashim Khan

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

Michael Higgins
Michael Higgins

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

Ajster1989
Ajster1989

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

gseattle
gseattle

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)

Info on dictionary pop()

What's the one-liner for the loop above?

Upvotes: 2

Akshay Vilas Patil
Akshay Vilas Patil

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

Phil M. Pappas
Phil M. Pappas

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

Kailey
Kailey

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

L. Quastana
L. Quastana

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

Andr&#233; Sionek
Andr&#233; Sionek

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

JSowa
JSowa

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

Anar Guliyev
Anar Guliyev

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

Ward
Ward

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

Ksreenivas Karanam
Ksreenivas Karanam

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

Pranjal Singi
Pranjal Singi

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

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

Hatem
Hatem

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

moinudin
moinudin

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

MicahT
MicahT

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

martineau
martineau

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

Tauquir
Tauquir

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

kevpie
kevpie

Reputation: 26088

pop'n'fresh

>>>a = {1:2, 3:4}
>>>a[5] = a.pop(1)
>>>a
{3: 4, 5: 2}
>>> 

Upvotes: 52

AndiDog
AndiDog

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

DGH
DGH

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

Related Questions