1

I've got a dictionary of dictionaries but each key has a differing number of dictionaries as value. Also, the keys for the nested dictionary can take three different forms.

myDict = {
    u'A': {'1998': u'ATLANTA'},
    u'B': {'1999': u'MANNHEIM'},
    u'C': {'2000': u'BERLIN'},
    u'D': {'1998': u'CHICAGO', '1999': u'PRINCETON'},
    u'E': {'2000': u'LOUISIANA'},
    u'F': {'1998': u'NEW YORK', '1999': u'NEW YORK'}
}

I want to write myDict as a table looking like

  | 1998     | 1999     | 2000
A | ATLANTA  |          |
B |          | MANNHEIM |
C |          |          | BERLIN
D |          | CHICAGO  | PRINCETON
E |          |          | LOUISANA
F | NEW YORK | NEW YORK |

How would I do this? I tried using a DictWriter and a Writer from csv, but both don't work:

DictWriter:

import csv

with open("outfilename.csv", 'w') as f:
    fieldnames = ['author', '1998', '1999', '2000']
    csvWriter = csv.DictWriter(f, fieldnames)
    csvWriter.writerows(myDict)

results in:

  File "./011_create_node_lists.py", line 122, in <module>
    csvWriter.writerows(myDict)
  File "/usr/lib/python2.7/csv.py", line 157, in writerows
    rows.append(self._dict_to_list(rowdict))
  File "/usr/lib/python2.7/csv.py", line 149, in _dict_to_list
    return [rowdict.get(key, self.restval) for key in self.fieldnames]
AttributeError: 'unicode' object has no attribute 'get'

writer:

import csv

with open("outfilename.csv", 'w') as f:
    csvWriter = csv.writer(f)
    for key, value in myDict.items():
       csvWriter.writerow([key, value])

results in:

A | {'1998': u'ATLANTA'}
B | {'1999': u'MANNHEIM'}
C | {'2000': u'BERLIN'}
D | {'1998': u'CHICAGO'    | '1999': u'PRINCETON'}
E | {'2000': u'LOUISIANA'}
F | {'1998': u'NEW YORK'   | '1999': u'NEW YORK'}

Additionally, I am not even sure whether it's the best way to print a structured table.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
MERose
  • 4,048
  • 7
  • 53
  • 79

2 Answers2

4

A DictWriter's writerows method is expecting a list of dictionaries, so that it can iterate over the list to get each dictionary and create a CSV row from it (this is done by _dict_to_list, per your traceback). If you pass it a dictionary of dictionaries, then when you iterate over it you get each key, which it cannot create a row from (it is unicode, not a dict, so _dict_to_list chokes). Therefore you need to process your dictionary of dictionaries into a list of dictionaries, making sure each one has the appropriate keys.

For example:

rows = [dict(author=author, **data) for author, data in myDict.items()]

(see e.g. Python for-in loop preceded by a variable and What does ** (double star) and * (star) do for parameters? if any part of this syntax is unfamiliar)

On your input, this gives me:

[{'1998': u'ATLANTA', 'author': u'A'}, 
 {'2000': u'BERLIN', 'author': u'C'}, 
 {'1999': u'MANNHEIM', 'author': u'B'}, 
 {'2000': u'LOUISIANA', 'author': u'E'}, 
 {'1999': u'PRINCETON', '1998': u'CHICAGO', 'author': u'D'}, 
 {'1999': u'NEW YORK', '1998': u'NEW YORK', 'author': u'F'}]

Note that the ordering may vary, as dictionaries are unordered (unless you use e.g. [... in sorted(myDict.items())], for alphabetical order by author).

Community
  • 1
  • 1
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
3

csv.DictWriter.writerows takes a list of dictionaries, not a dictionary of dictionaries. You need to add an author key to the inner dictionary.

So:

csvWriter.writerows(
    dict(data.items() + [('author', author)])
    for author, data in author_aff_dict.items()
)

Or more verbosely:

author_rows = []
for author, data in author_aff_dict.items():
    row = {'author': author}
    row.update(data)
    author_rows.append(row)
csvWriter.writerows(author_rows)

You may also want to start with a writeheader() call

Eric
  • 95,302
  • 53
  • 242
  • 374