Add keys to a dictionary with automatically incremented values

Viewed 153

I have a dictionary, d, with values 0,1,2 for the 3 keys:

{'-11111': 0, 'hello kitty': 1, 'hello this is me': 2}

I want to add 2 new keys from a list and automatically increment their values as 3 and 4

I can get this far

newkeys = ['give a dog a bone', 'take a dog for a walk']
d.update([newkeys])

gives an updated dictionary, but with no values assigned to the new keys

{'-11111': 0,
 'hello kitty': 1,
 'hello this is me': 2,
 'give a dog a bone': 'take a dog for a walk'}

what I want is this:

{'-11111': 0,
 'hello kitty': 1,
 'hello this is me': 2,
 'give a dog a bone': 3,
 'take a dog for a walk': 4}

Is there an efficient and simple way to do this? thanks

6 Answers

Note: this will only work for Python 3.8+. Before 3.8, the dict type was not insertion-ordered, and therefore any attempt to do what OP wants would be much more of a pain in versions < 3.8.

A simple solution

Here's a simple solution which doesn't require importing any modules:

{val: idx for idx, val in enumerate([*old_dict, *new_keys])}

A quick demo:

>>> old_dict = {'-11111': 0, 'hello kitty': 1, 'hello this is me': 2}
>>> new_keys = ['give a dog a bone', 'take a dog for a walk']
>>> new_dict = {val: idx for idx, val in enumerate([*old_dict, *new_keys])}
>>> new_dict
{'-11111': 0,
 'hello kitty': 1,
 'hello this is me': 2,
 'give a dog a bone': 3,
 'take a dog for a walk': 4}

This requires building a new dictionary, and in addition to other solutions will not account for duplicate keys (e.g., if you add another "-11111" to new_keys, the old one will be erased, and your count won't be updated).

A nice bonus of using enumerate() is that it allows for starting your counter at any value by using the optional start parameter:

>>> {val: idx for idx, val in enumerate([*old_dict, *new_keys], -99)}
{'-11111': -99,
 'hello kitty': -98,
 'hello this is me': -97,
 'give a dog a bone': -96,
 'take a dog for a walk': -95}

Alternative solution with optional start and step size

If you want a custom starting value and a custom step size:

def new_dict(old_dict, new_keys, start=0, step=1):
    keys = [*old_dict, *new_keys]
    stop = len(keys) * step
    return dict(zip(keys, range(start, stop, step)))

Demo:

>>> new_dict(old_dict, new_keys, step=2)
{'-11111': 0,
 'hello kitty': 2,
 'hello this is me': 4,
 'give a dog a bone': 6,
 'take a dog for a walk': 8}

>>> new_dict(old_dict, new_keys, step=3)
{'-11111': 0,
 'hello kitty': 3,
 'hello this is me': 6,
 'give a dog a bone': 9,
 'take a dog for a walk': 12}

>>> new_dict(old_dict, new_keys, start=-4, step=3)
{'-11111': -4,
 'hello kitty': -1,
 'hello this is me': 2,
 'give a dog a bone': 5,
 'take a dog for a walk': 8}

You could iterate over the list of newkeys and set each new key to the length of list of items currently in the dict:

d = {'-11111': 0, 'hello kitty': 1, 'hello this is me': 2}
newkeys = ['give a dog a bone', 'take a dog for a walk']
    newkeys = ['give a dog a bone', 'take a dog for a walk']
    for key in newkeys:
        d[key] = len(d.items())

To add new keys, simply use d[keyname] = keyvalue, and the key is appended.

Update: I found a safer, less magical/hacky way of solving the problem than in my original answer

key_id_map = {}

def update_key_id_map(keys):
    for key in keys:
        if key_id_map.get(key) == None:
            key_id_map[key] = len(key_id_map)

Demonstration:

>>> key_id_map
{}
>>> update_key_id_map([0, 1, 2])
>>> key_id_map
{0: 0, 1: 1, 2: 2}
>>> update_key_id_map([0, 1, 2, 3, 4])
>>> key_id_map
{0: 0, 1: 1, 2: 2, 3: 3, 4: 4}
>>> # Reset to demonstrate original question
>>> key_id_map = {'-11111': 0, 'hello kitty': 1, 'hello this is me': 2}
>>> newkeys = ['give a dog a bone', 'take a dog for a walk']
>>> update_key_id_map(newkeys)
>>> key_id_map
{'-11111': 0,
 'hello kitty': 1,
 'hello this is me': 2,
 'give a dog a bone': 3,
 'take a dog for a walk': 4}

Bonus: Unlike other solutions, this one can tolerate updating the dict with already existing keys. Only the new keys will be taken into account and recieve an incremented ID.

>>> update_key_id_map(['hello kitty', 'give a dog a bone', 'an actual new key, the others already exist'])
>>> dict(key_id_map)
{'-11111': 0,
 'hello kitty': 1,
 'hello this is me': 2,
 'give a dog a bone': 3,
 'take a dog for a walk': 4,
 'an actual new key, the others already exist': 5}

Interested to hear any feedback.

If I understand correctly, you want to add new keys while incrementing the values based on existing values.

Solution 1

d = {'-11111': 0, 'hello kitty': 1, 'hello this is me': 2}
newkeys = ['give a dog a bone', 'take a dog for a walk']
value = max(d.values())
for key in newkeys:
    value = value + 1
    d[key] = value

This solution is easy to understand and does not use any external library.

Solution 2

This solution uses the itertools.count() function. This function will generate increasing numbers. For example, itertools.count(3) will generate 3, 4, 5, 6, 7, ...

import itertools

d = {'-11111': 0, 'hello kitty': 1, 'hello this is me': 2}
newkeys = ['give a dog a bone', 'take a dog for a walk']
values = itertools.count(max(d.values()) + 1)
d.update(zip(newkeys, values))

In this solution, max(d.values()) will give 2, so values will generate 3, 4, 5, ... Next, we zip the newkeys and values and use it to update the dictionary.

One other option assuming you don't want to start from 0 (but want to increment)

class CustomIncrementer:
    def __init__(self, initial_value_or_dictionary):
        if isinstance(initial_value_or_dictionary, dict):
            # Assumes that the values in the input dictionary are only numerics
            self.my_dict = initial_value_or_dictionary
            self.counter = max(initial_value_or_dictionary.values()) + 1
        else:  # assumes it's a numeric 
            self.my_dict = {}
            self.counter = initial_value_or_dictionary

    def update(self, list_of_words: list):
        for element in list_of_words:
            self.my_dict[element] = self.counter
            self.counter += 1

Considering you know exactly what the keys and values should be, why don't you just do:

newkeys = [['give a dog a bone', 3], ['take a dog for a walk', 4]]
d.update(newkeys)

In each sublist, the first item becomes a key and the second one the associated value.

EDIT:

The same idea, with the incremental feature

d = {'-11111': 0, 'hello kitty': 1, 'hello this is me': 2}
newkeys = ['give a dog a bone', 'take a dog for a walk']
d.update([[x, y + len(d)] for y, x in enumerate(newkeys)])
# {'-11111': 0, 'hello kitty': 1, 'hello this is me': 2,
#  'give a dog a bone': 3, 'take a dog for a walk': 4}
Related