1

I have a list of dictionaries and need to iterate through them and check for the keys that exist already. I have implemented a python code to manually calculate a score as below. In my code, I'm manually combining keys from previous dictionaries in each iteration. Iteration will start from dict11.

How can I change this code to automatically iterate through a dynamic number of dictionaries and in each iteration how can I combine the keys dynamically?


dict10 = {'A': 1, 'C': 2}
dict11 = {'B': 3, 'C': 4}
dict12 = {'A': 5, 'E': 6, 'F': 7}
dict13 = {'G': 8, 'E': 9}

exist_score = 0

for key in dict11.keys() & dict10.keys():
    exist_score += dict11[key]

for key in dict12.keys() & set(dict11.keys()).union(set(dict10.keys())):
    exist_score += dict12[key]

for key in dict13.keys() & set(dict12.keys()).union(set(dict11.keys()).union(set(dict10.keys()))):
    exist_score += dict13[key]

print(exist_score)

  • 1
    You can simply put your different dicts into a list and iterate on the list... – AlexG Jul 24 '19 at 20:00
  • https://stackoverflow.com/questions/38987/how-to-merge-two-dictionaries-in-a-single-expression does this answer your question? – rawwar Jul 24 '19 at 20:01
  • 1
    I may be able to give you a faster solution than those below but it would be helpful if you add the desired output and the logic to get that output. – Error - Syntactical Remorse Jul 24 '19 at 20:16

3 Answers3

3

First you need to turn this into something that can be put into a loop. The same thing has to happen with dict11, dict12 and dict13:

# same definition for dict10, dict11, dict12, dict13

exist_score = 0
seen_keys = set(dict10.keys())

for key in dict11.keys():
  if key in seen_keys:
    exist_score += dict11[key]
seen_keys.update(dict11.keys())

for key in dict12.keys():
  if key in seen_keys:
    exist_score += dict12[key]
seen_keys.update(dict12.keys())

for key in dict13.keys():
  if key in seen_keys:
    exist_score += dict13[key]
seen_keys.update(dict13.keys())

This should do the same thing as your script. Now you can put that into a loop …

# same definition for dict10, dict11, dict12, dict13

exist_score = 0
seen_keys = set(dict10.keys())
other_dicts = [dict11, dict12, dict13]

for d in other_dicts:
  for key in d.keys():
    if key in seen_keys:
      exist_score += d[key]
  seen_keys.update(d.keys())
zwirbeltier
  • 867
  • 9
  • 27
2

It makes the most sense to keep dicts in a list themselves. Putting this logic into a function is also a no-brainer.

dicts = [
    {'A': 1, 'C': 2},
    {'B': 3, 'C': 4},
    {'A': 5, 'E': 6, 'F': 7},
    {'G': 8, 'E': 9}
]

def score_dicts(dicts):
    score = 0
    all_keys = set()
    for d in dicts:
        keys = d.keys()
        for key in keys & all_keys:
            score += d[key]
        all_keys.update(keys)
    return score

exist_score = score_dicts(dicts)

If you need to update the score periodically (one dict at a time), you can maintain the state in either a class or a closure.

Class:

class DictScorer():
    def __init__(self):
        self.exist_score = 0
        self.all_keys = set()
    def score(self, d):
        keys = d.keys()
        for key in keys & self.all_keys:
            self.exist_score += d[key]
        self.all_keys.update(keys)
        return self.exist_score

dict10 = {'A': 1, 'C': 2}
dict11 = {'B': 3, 'C': 4}
dict12 = {'A': 5, 'E': 6, 'F': 7}
dict13 = {'G': 8, 'E': 9}

scorer = DictScorer()

exist_score = scorer.score(dict10)
print(exist_score)

exist_score = scorer.score(dict11)
print(exist_score)

exist_score = scorer.score(dict12)
print(exist_score)

exist_score = scorer.score(dict13)
print(exist_score)

Closure:

# returns a scorer which can
# be used incrementally
def create_dict_scorer():
    score = 0
    all_keys = set()
    def dict_scorer(d):
        nonlocal score
        keys = d.keys()
        for key in keys & all_keys:
            score += d[key]
        all_keys.update(keys)
        return score
    return dict_scorer

dict10 = {'A': 1, 'C': 2}
dict11 = {'B': 3, 'C': 4}
dict12 = {'A': 5, 'E': 6, 'F': 7}
dict13 = {'G': 8, 'E': 9}

scorer = create_dict_scorer()

exist_score = scorer(dict10)
print(exist_score)

exist_score = scorer(dict11)
print(exist_score)

exist_score = scorer(dict12)
print(exist_score)

exist_score = scorer(dict13)
print(exist_score)
Evan
  • 2,120
  • 1
  • 15
  • 20
2

Short magic with slicing and set operations:

dicts = [dict10, dict11, dict12, dict13]
exist_score = 0

for i, d in enumerate(dicts[:0:-1]):
    offset = -(i - 2)
    exist_score += sum(d[k] for k in d.keys() & set().union(*dicts[offset::-1]))

print(exist_score)
  • dicts[:0:-1] - slice of dictionaries in reversed order excluding the 1st one
  • -(i - 2) - negative offset to get consecutive "backward" slices for further set unions

The output (the same as in your initial approach):

18
RomanPerekhrest
  • 88,541
  • 4
  • 65
  • 105