0

Person Node Value

Bob    A    2
Bob    A    3
Bob    A    4
Bob    B    2
Bob    B    3
Jill   A    1
Jill   B    2

I am attempting to get the following into a data structure similar to this

{ 'Bob': { 'A':[2,3,4],'B':[2,3], :'Jill':{'A':[1], 'B':[2]}

I know this might not be the best approach, but what I am trying to do with my data structure is the following:

  1. Dictionary whose key is a value and check if it a value.
  2. Value of dictionary is another dictionary and need to check if key is already in the value.
  3. Value of the second dictionary is a list which needs to be appended to if the list exists like in Bob's case.

I have tried numerous approaches, but right now, my code is looking like this.

names = {}


with open('impacts.csv', 'rb') as csvfile:
    namesreaders = csv.reader(csvfile, delimiter=' ', quotechar='|')
    for row in namesreaders:
        person, letter, value = row[0], row[1], row[2]
        if person not in names:
           names[person] = { letter: value}
        else:
            print 'Lost a bit'
            ### Lost here
print names
2
  • 1
    You want to have lists as values, and still, no list is created in your code snippet. Commented Jan 17, 2017 at 17:59
  • Minor point, but your data structure example is missing a few curly braces to close up the second level dictionaries. Commented Jan 17, 2017 at 18:07

5 Answers 5

4

I would use a defaultdict, where the default is a defaultdict with a default list. Then it's very easy to populate that dictionary

from collections import defaultdict
d = defaultdict(lambda: defaultdict(list))
with open('impacts.csv', 'rb') as csvfile:
    namesreaders = csv.reader(csvfile, delimiter=' ', quotechar='|')
    for row in namesreader:
        person, letter, value = row[0], row[1], row[2]
        d[person][letter].append(value)
Sign up to request clarification or add additional context in comments.

1 Comment

Terrific way to do this and learned a lot from your answer. Thank you!
0

Use nested dictionaries by implementing the perl’s autovivification feature.

class AutoVivification(dict):
    """Implementation of perl's autovivification feature."""
    def __getitem__(self, item):
        try:
            return dict.__getitem__(self, item)
        except KeyError:
            value = self[item] = type(self)()
            return value​

​# For your case
names = AutoVivification()

with open('impacts.csv', 'rb') as csvfile:
    namesreaders = csv.reader(csvfile, delimiter=' ', quotechar='|')
    for row in namesreaders:
        person, letter, value = row[0], row[1], row[2]
        if names[person][letter]:
            names[person][letter].append(value)
        else:
            names[person][letter] = [value]

Comments

0

I would solve it like this. I am sure it could be shortened quite a bit if you make it more recursive and use comprehension. But to understand the principle and make it similar to your start (solution not tested for syntactic errors):

names = {}

with open('impacts.csv', 'rb') as csvfile:
    namesreaders = csv.reader(csvfile, delimiter=' ', quotechar='|')
    for row in namesreaders:
       person, letter, value = row[0], row[1], row[2]
       if person not in names:
          names[person] = {}
       if letter not in names[person]:
           names[person][letter] = []
       names[person][letter].append(value)
 print names

Comments

0

Don't let the code confuse you. You defined pretty clearly what you need to do. Now just take it slow and do it.

First, as you said: value of second dictionary is a list. You don't want

names[person] = { letter: value }

Instead you want:

names[person] = { letter: [value] }

Now in your else, you know that person is already in names. Is value? If it is, add to the list, otherwise, create a new list:

else:
    if letter in names[person]:
        names[person][letter].append(value)
    else:
        names[person][letter] = value

Now, you can probably go about this a whole lot cleaner using the setdefault method of a dict.

This would do the same thing:

names = {}

with open('impacts.csv', 'rb') as csvfile:
    namesreaders = csv.reader(csvfile, delimiter=' ', quotechar='|')
for row in namesreaders:
    person, letter, value = row[0], row[1], row[2]

    # Make sure that if person isn't in names, 
    # person is added to names as a dict
    names.setdefault(person, {})
    # Make sure that if letter isn't in the names[person] dict
    # letter is added as an empty list
    names[person].setdefault(letter, [])
    # At this point, names[person][letter] is an existing, possibly empty
    # list.  Simply add the new value
    names[person][letter].append(value)

Comments

0

Quite a well known pythonic technique is to use defaultdict() to specify complex dictionary structures at initialization time.

Disclaimer: I would highly recommend considering class structures to handle this data, as these kinds of nested dictionaries can lead to code smells rather quickly.

import functools
from collections import defaultdict

d_inner = functools.partial(defaultdict, list)
d = defaultdict(d_inner)

# Do something with data here
for name, letter, number in row:
    d[name][letter].append(number)

Sample Output:

defaultdict(<functools.partial object at 0x109de71b0>, {'Bob': defaultdict(<type 'list'>, {'A': [2, 3, 4], 'B': [2, 3]}), 'Jill': defaultdict(<type 'list'>, {'A': [1], 'B': [2]})})

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.