ahlusar1989
ahlusar1989

Reputation: 349

TypeError: unhashable type: 'dict' when attempting to write out to CSV in python

I am using Jupyter Notebook and Python 3.4. I have a data structure in the format:

[{'AccountNumber': N,
'Amount': '0',
 'Answer': '12:00:00 PM',
  'ID': None,
  'Type': 'WriteLetters',
  'Amount': '10',
  {'AccountNumber': Y,
      'Amount': '0',
      'Answer': ' 12:00:00 PM',       
       'ID': None,
      'Type': 'Transfer',
      'Amount': '2'}]

The end goal is to write this out to CSV.

For the above example the output would look like:

AccountNumber, Amount, Answer, ID, Type, Amount
N,0,12:00:00 PM,None,WriteLetters,10
Y,2,12:00:00 PM,None,Transfer,2

Below is the function that I am using to write out this data structure. Please excuse any indentation formatting issues. The data structure is returned through the function construct_results(get_just_xml_data). The data that is returned is in the format as above. construct_headers(get_just_xml_data) returns a list of headers. Writing out the row for headers_list works.

The list comprehension data is to remove duplicates and maintain the integrity of the column headers and the values for each new instance of the data structure (where the keys in the dictionary are the headers and values - row instances). The keys in this specific data structure are meant to check if there is a value instance, and if there is not - place an ''.

def write_to_csv(results, headers):

    headers = construct_headers(get_just_xml_data)
    results = construct_results(get_just_xml_data)
    headers_list = list(headers)

    with open('real_csv_output.csv', 'wt') as f:
        writer = csv.writer(f)
        writer.writerow(headers_list)
        for row in results:
            data = [row.get(index, '') for index in results]
        writer.writerow(data)

However, when I run this, I receive this error:

The end goal is to write this out to CSV. 

For the above example the output would look like:

AccountNumber, Amount, Answer, ID, Type, Amount
N,0,12:00:00 PM,None,WriteLetters,10
Y,2,12:00:00 PM,None,Transfer,2

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-744-7746797fc9a5> in <module>()
----> 1 write_to_csv(results, headers)

<ipython-input-743-c822437eeaf0> in write_to_csv(results, headers)
      9         writer.writerow(headers_list)
     10         for item in results:
---> 11             data = [item.get(index, '') for index in results]
     12         writer.writerow(data)

<ipython-input-743-c822437eeaf0> in <listcomp>(.0)
      9         writer.writerow(headers_list)
     10         for item in results:
---> 11             data = [item.get(index, '') for index in results]
     12         writer.writerow(data)

TypeError: unhashable type: 'dict'

I welcome any feedback on what may be the cause.

Upvotes: 0

Views: 2248

Answers (2)

ahlusar1989
ahlusar1989

Reputation: 349

I went back to the drawing board:

These are my findings:

The ‘for’ statement iterates over ‘results’, getting an item each time. The name ‘row’ is bound to each item in turn.

Then, each time through the ‘for’ loop, I iterate again over ‘results’. The name ‘index’ is bound to each item.

I then attempt to use the dict (each item from ‘results’ is itself a dict) as a key into that same dict. A dict is not a valid key; it is not a “hashable type” i.e. a type with a fixed value, that can produce a hash of the value).

So I was getting dicts and attempting to use those dicts as keys into dicts. That will give the error “TypeError: unhashable type: 'dict'”.

I wanted not the items from the original sequence, but the keys from the mapping::

for input_record in results: 
    output_record = [input_record.get(key, "") for key in input_record] 

But I've then throwing away the constructed list, since I do nothing with it before the end of the loop.

     `writer.writerow(data)` 

This statement occurs only after all the items from ‘results’ have been iterated. You will only have the most recent constructed row.

I wanted the following:

   for input_record in results: 
        output_record = [input_record.get(key, "") for key in input_record] 
        writer.writerow(output_record) 

Upvotes: 0

MrAlexBailey
MrAlexBailey

Reputation: 5289

You can use csv.DictWriter() to do this a little more easily:

with open('real_csv_output.csv', 'wt') as f:
    csvw = csv.DictWriter(f, fieldnames=results[0].keys())
    csvw.writeheader()
    csvw.writerows(results)

BTW the error you are getting is because row is a string... You probably meant item.get() instead.

Upvotes: 1

Related Questions