dissidia
dissidia

Reputation: 1581

Read and add the values of same keys in dictionary

I am trying to add the values of a dictionary of the same key(s). In my example below, I strip off the version numbering for each keys while trying to add the values of the 'same' key.

item_dict = {
"item_C_v001" : 100,
"item_C_v002" : 100,
"item_A_v001" : 50,
"item_B_v001" : 75
}

My expected output should be:

"item_C_v" : 200,
"item_A_v" : 50,
"item_B_v" : 75

but instead I got the following if I tried printing item_dict:

"item_C_v" : 100,
"item_A_v" : 50,
"item_B_v" : 75

But the moment I tried to strip off, it seems that item_C_v is read once. How can I make it read 'twice' so that I can get 200 as the output for it?

Upvotes: 0

Views: 83

Answers (3)

AChampion
AChampion

Reputation: 30288

You can use itertools.groupby() to collect all the similar keys and then construct a new dictionary summing up all the group values, e.g.:

>>> import itertools as it
>>> {gk: sum(item_dict[k] for k in g) for gk, g in it.groupby(item_dict, lambda k: k[:-3])}
{'item_A_v': 50, 'item_B_v': 75, 'item_C_v': 200}

@alecxe describes several methods to getting the desired part of the key, above uses key[-3].

There was a request to get both the count and sum, which you could implement in a simple loop and manage a counter but here's one way of doing it:

>>> from collections import deque
>>> {key: deque(enumerate(it.accumulate(item_dict[k] for k in g), 1), maxlen=1).pop()
...     for key, g in it.groupby(item_dict, lambda key: key[:-3])}
{'item_A_v': (1, 50), 'item_B_v': (1, 75), 'item_C_v': (2, 200)}

Upvotes: 2

omri_saadon
omri_saadon

Reputation: 10671

You could iterate the original dictionary and append to the new dictionary the new keys, if the key already exists, add the current value to the original value.

item_dict = {
"item_C_v001" : 100,
"item_C_v002" : 100,
"item_A_v001" : 50,
"item_B_v001" : 75
}

new_dict = {}

for k, v in item_dict.items():
    k_new = k[:-3]
    new_dict[k_new] = new_dict[k_new] + v if k_new in new_dict.keys() else v

print (new_dict)

>>> {'item_A_v': 50, 'item_C_v': 200, 'item_B_v': 75}

Upvotes: 2

alecxe
alecxe

Reputation: 474131

You can use a collections.defaultdict(int) to group and sum the result by a desired part of a key which you can get with, for example, a regular expression. Something along these lines:

In [1]: from collections import defaultdict

In [2]: import re

In [3]: item_dict = {
   ...: "item_C_v001" : 100,
   ...: "item_C_v002" : 100,
   ...: "item_A_v001" : 50,
   ...: "item_B_v001" : 75
   ...: }

In [4]: result = defaultdict(int)

In [5]: for key, value in item_dict.items():
   ...:     result[re.search(r"(\w+?)\d+", key).group(1)] += value   

In [6]: dict(result)
Out[6]: {'item_A_v': 50, 'item_B_v': 75, 'item_C_v': 200}

You can approach extracting the desired part of a key differently - with the str.partition():

>>> key = "item_C_v001"
>>> "".join(key.partition("_v")[:-1])
'item_C_v'

Or, simply via slicing, if the length of the substring before the digits or the number of digits at the end of the string is constant:

>>> key = "item_C_v001"
>>> key[:8]
'item_C_v'
>>> key[:-3]
'item_C_v'

Or, by right-stripping the digits:

>>> import string
>>> key.rstrip(string.digits)
'item_C_v'

Upvotes: 2

Related Questions