20

From another function, I have tuples like this ('falseName', 'realName', positionOfMistake), eg. ('Milter', 'Miller', 4). I need to write a function that make a dictionary like this:

D={realName:{falseName:[positionOfMistake], falseName:[positionOfMistake]...}, 
   realName:{falseName:[positionOfMistake]...}...}

The function has to take a dictionary and a tuple like above, as arguments.

I was thinking something like this for a start:

def addToNameDictionary(d, tup):
    dictionary={}
    tup=previousFunction(string)
    for element in tup:
        if not dictionary.has_key(element[1]):
            dictionary.append(element[1])
    elif:
        if ...

But it is not working and I am kind of stucked here.

jww
  • 97,681
  • 90
  • 411
  • 885
Linus Svendsson
  • 1,417
  • 6
  • 18
  • 21
  • you're indentation is wrong. and what exactly doesn't work ? – yurib Dec 18 '11 at 09:51
  • 2
    The 'tup' in the parameter is being blown away by the 'tup=previ..' line. The code looks like you're not holding the big picture in your head. I reckon stop, step away from the computer, take a deep breath, go for a walk, sit down, close your eyes, and write the code down with pencil and paper. – matiu Dec 18 '11 at 10:08

3 Answers3

16

If it is only to add a new tuple and you are sure that there are no collisions in the inner dictionary, you can do this:

def addNameToDictionary(d, tup):
    if tup[0] not in d:
        d[tup[0]] = {}
    d[tup[0]][tup[1]] = [tup[2]]
AB1908
  • 7
  • 2
aweis
  • 5,350
  • 4
  • 30
  • 46
  • 3
    The has_key test is better written `if tup[0] not in d:` –  Dec 18 '11 at 09:55
  • 7
    `has_key()` is deprecated and has been removed from Python 3. – Tim Pietzcker Dec 18 '11 at 09:58
  • 1
    This is really useful code. I was working on a nested dictionary of data extracted from a file and this made a really clean replacement for a failed function I was putting together (after some slight modifications that were specific to my particular use case). Thanks for this post! – TMWP Mar 24 '17 at 16:41
12

Using collections.defaultdict is a big time-saver when you're building dicts and don't know beforehand which keys you're going to have.

Here it's used twice: for the resulting dict, and for each of the values in the dict.

import collections

def aggregate_names(errors):
    result = collections.defaultdict(lambda: collections.defaultdict(list))
    for real_name, false_name, location in errors:
        result[real_name][false_name].append(location)
    return result

Combining this with your code:

dictionary = aggregate_names(previousFunction(string))

Or to test:

EXAMPLES = [
    ('Fred', 'Frad', 123),
    ('Jim', 'Jam', 100),
    ('Fred', 'Frod', 200),
    ('Fred', 'Frad', 300)]
print aggregate_names(EXAMPLES)
8

dictionary's setdefault is a good way to update an existing dict entry if it's there, or create a new one if it's not all in one go:

Looping style:

# This is our sample data
data = [("Milter", "Miller", 4), ("Milter", "Miler", 4), ("Milter", "Malter", 2)]

# dictionary we want for the result
dictionary = {}

# loop that makes it work
for realName, falseName, position in data:
    dictionary.setdefault(realName, {})[falseName] = position

dictionary now equals:

{'Milter': {'Malter': 2, 'Miler': 4, 'Miller': 4}}
matiu
  • 7,469
  • 4
  • 44
  • 48