0

I have some dictionaries that share keys, e.g.

dict1 = {'a' : 1, 'b' : 2, 'c' : -1}
dict2 = {'a' : -1, 'b' : -3, 'c' : 3}

I would like to perform a conditional operation of both, but only change the values of the keys that meet the condition. For example I would like to add 10 to any key 'b' that has a negative value which would yield:

dict1 = {'a' : 1, 'b' : 2, 'c' : -1}
dict2 = {'a' : -1, 'b' : 7, 'c' : 3}

Is there a way to loop through 'keys' that are common to a dictionary and only operate on those? e.g.

dicts = [dict1, dict2]

for i in dicts:
    if i['b'] < 0:
        i['b'] = i['b'] + 10

but this yeilds the interesting result of:

print dicts[0]
print dicts[1]

{'a': 1, 'c': -1, 'b': 12}
{'a': -1, 'c': 3, 'b': -3}

Which I'm not sure I understand.

I have many (1000s) pairs of this kind of structure that are being generated in a loop so I'd like it to be fairly efficient if possible.

Thanks!

edit:

The accepted solution of

for key in set(dict1).intersection(set(dict2)):
  for i in dicts:
    if i[key] < 0:
      i[key] = i[key] + 10

is great for 2 dicts. However if I initially had 'n' dicts that were all similar, e.g. for 4 dicts:

dict1 = {'a' : 1, 'b' : 2, 'c' : -1}
dict2 = {'a' : -1, 'b' : -3, 'c' : 3}
dict3 = {'a' : 1, 'b': -1, 'c' : -4}
dict4 = {'a' : 0, 'b': 5, 'c' : 2}

dicts = [dict1, dict2, dict3, dict4]

And the desired outcome (of only adding 10 to all negative 'b''s) would be:

dict1 = {'a' : 1, 'b' : 2, 'c' : -1}
dict2 = {'a' : -1, 'b' : 7, 'c' : 3}
dict3 = {'a' : 1, 'b': 9, 'c' : -4}
dict4 = {'a' : 0, 'b': 5, 'c' : 2}

Would the same loop structure still apply?

rh1990
  • 880
  • 7
  • 17
  • 32
  • 2
    I can't reproduce what you're seeing (on Python 3), I see only the second dictionary changed, which is what you want. – jpp Oct 08 '18 at 15:51
  • Cannot reproduce your problem under either Python 2.7 or 3.4.5 – Prune Oct 08 '18 at 15:54
  • This is Python 2.7 in a Jupyter Notebook, can't explain why you're not seeing what I'm seeing... – rh1990 Oct 08 '18 at 15:55
  • Although your code does actually work as others have said, it's not the way you should do it. If you now mutate `i` afterwards then `dict2` will also change, they are the same dict – Chris_Rands Oct 08 '18 at 15:58

3 Answers3

1

I cannot reproduce your issue in Python 3. I would suggest you use set() and intersection() to gather the common keys:

dict1 = {'a' : 1, 'b' : 2, 'c' : -1}
dict2 = {'d' : -1, 'b' : -3, 'e' : 3}

dicts = [dict1, dict2]

for key in set(dict1).intersection(set(dict2)):
  for i in dicts:
    if i[key] < 0:
      i[key] = i[key] + 10

Yields:

{'a': 1, 'b': 2, 'c': -1}
{'d': -1, 'b': 7, 'e': 3}
rahlf23
  • 8,869
  • 4
  • 24
  • 54
  • Yes this works nicely, thanks! I'll accept it as the answer when the timer runs out. Additional question, my keys have been rearranged such that `'b'` is at the end, is that expected? – rh1990 Oct 08 '18 at 15:58
  • 1
    Standard dictionaries in Python are not ordered, opposed to an `OrderedDict()`, which remembers insertion order. There is a good overview here: https://stackoverflow.com/q/25056387/8146556 – rahlf23 Oct 08 '18 at 16:02
  • Thank you for the reference. How would your answer change if `dicts = [dict1, dict2, dict3, dict4]` where all dicts are the same size and share common keys? – rh1990 Oct 08 '18 at 16:07
  • IIUC, if all of your dictionaries contain the same set of keys, then you can modify the first for loop to `for key in dict1.keys()` – rahlf23 Oct 08 '18 at 16:12
  • Thank you, I'll add an extension to my initial post for clarity to check if I've asked it properly. – rh1990 Oct 08 '18 at 16:14
  • Just to clarify, I meant doing this operation simultaneously on 4 dictionaries not doing it 4 times between 4 different pairs. – rh1990 Oct 08 '18 at 16:30
1

I can't reproduce your problem. But since the second part of your question considers efficiency, I suggest you try a dictionary comprehension. The algorithm still has O(n) complexity but construction in a loop is implemented more efficiently:

def num_changer(d, inc=10, k='b'):
    return {k: v if (k != 'b') or (v >= 0) else v + 10 for k, v in d.items()}

dict1 = num_changer(dict1)
dict2 = num_changer(dict2)

print(dict1, dict2, sep='\n')

{'a': 1, 'b': 2, 'c': -1}
{'a': -1, 'b': 7, 'c': 3}
jpp
  • 159,742
  • 34
  • 281
  • 339
0

You can create a dict on the fly that contains the intersecting keys.

for a in set([x for x in dict1 if x in dict2]):
    if dict1[a] < 0: dict1[a] += 10
    if dict2[a] < 0: dict2[a] += 10


>>> dict1
{'a': 1, 'c': 9, 'b': 2}
>>> dict2
{'a': 9, 'c': 3, 'b': 7}
>>> 

However, for many dicts you should do this in a more readable way

forgetso
  • 2,194
  • 14
  • 33