Why am I seeing "TypeError: string indices must be integers"?

PythonJsonGithub

Python Problem Overview


I'm playing with both learning Python and am trying to get GitHub issues into a readable form. Using the advice on https://stackoverflow.com/questions/1871524/convert-from-json-to-csv-using-python, I came up with this:

import json
import csv

f = open('issues.json')
data = json.load(f)
f.close()

f = open("issues.csv", "wb+")
csv_file = csv.writer(f)

csv_file.writerow(["gravatar_id", "position", "number", "votes", "created_at", "comments", "body", "title", "updated_at", "html_url", "user", "labels", "state"])

for item in data:
    csv_file.writerow([item["gravatar_id"], item["position"], item["number"], item["votes"], item["created_at"], item["comments"], item["body"], item["title"], item["updated_at"], item["html_url"], item["user"], item["labels"], item["state"]])

Where "issues.json" is the JSON file containing my GitHub issues. When I try to run that, I get

File "foo.py", line 14, in <module>
csv_file.writerow([item["gravatar_id"], item["position"], item["number"], item["votes"], item["created_at"], item["comments"], item["body"], item["title"], item["updated_at"], item["html_url"], item["user"], item["labels"], item["state"]])

TypeError: string indices must be integers

What am I missing here? Which are the "string indices"? I'm sure that once I get this working I'll have more issues, but for now, I'd just love for this to work!

When I tweak the for statement to simply

for item in data:
    print item

what I get is ... "issues" -- so I'm doing something more basic wrong. Here's a bit of my JSON content:

{"issues": [{"gravatar_id": "44230311a3dcd684b6c5f81bf2ec9f60", "position": 2.0, "number": 263, "votes": 0, "created_at": "2010/09/17 16:06:50 -0700", "comments": 11, "body": "Add missing paging (Older>>) links...

when I print data, it looks like it is getting munged really oddly:

{u'issues': [{u'body': u'Add missing paging (Older>>) lin...

Python Solutions


Solution 1 - Python

The variable item is a string. An index looks like this:

>>> mystring = 'helloworld'
>>> print mystring[0]
'h'

The above example uses the 0 index of the string to refer to the first character.

Strings can't have string indices (like dictionaries can). So this won't work:

>>> mystring = 'helloworld'
>>> print mystring['stringindex']
TypeError: string indices must be integers

Solution 2 - Python

item is most likely a string in your code; the string indices are the ones in the square brackets, e.g., gravatar_id. So I'd first check your data variable to see what you received there; I guess that data is a list of strings (or at least a list containing at least one string) while it should be a list of dictionaries.

Solution 3 - Python

TypeError for Slice Notation str[a:b]


Short Answer

Use a colon : instead of a comma , in between the two indices a and b in str[a:b]:

my_string[0,5]  # wrong ❌
my_string[0:5]  # correct ✅

Long Answer

When working with strings and slice notation (a common sequence operation), it can happen that a TypeError is raised, pointing out that the indices must be integers, even if they obviously are.

Example
>>> my_string = "Hello, World!"
>>> my_string[0,5]
TypeError: string indices must be integers

We obviously passed two integers for the indices to the slice notation, right? So what is the problem here?

This error can be very frustrating - especially at the beginning of learning Python - because the error message is a little bit misleading.

Explanation

We implicitly passed a tuple of two integers to the slice notation when we called my_string[0,5]. 0,5 evaluates to the same tuple as (0,5) does - even without the parentheses. Why though?

A trailing comma , is actually enough for the Python interpreter to evaluate something as a tuple:

>>> my_variable = 0,
>>> type(my_variable)
<class 'tuple'>

So what we did there, this time explicitly:

>>> my_string = "Hello, World!"
>>> my_tuple = 0, 5
>>> my_string[my_tuple]
TypeError: string indices must be integers

Now, at least, the error message makes sense.

Solution

We need to replace the comma , with a colon : to separate the two integers correctly, not having them interpreted as a tuple:

>>> my_string = "Hello, World!"
>>> my_string[0:5]
'hello'

A clearer and more helpful error message could have been something like:

TypeError: string indices must be integers not tuple
                                               ^^^^^
                                         (actual type here)

A good error message should show the user directly what they did wrong! With this kind of information it would have been much more easier to find the root cause and solve the problem - and you wouldn't have had to come here.

So next time, when you find yourself responsible for writing error description messages, remind yourself of this example and add the reason (or other useful information) to error message! Help other people (or maybe even your future self) to understand what went wrong.

Lessons learned
  • slice notation uses colons : to separate its indices (and step range, i.e., str[from:to:step])

  • tuples are defined by commas , (i.e., t = 1,)

  • add some information to error messages for users to understand what went wrong

Solution 4 - Python

data is a dict object. So, iterate over it like this:

#Python 2

for key, value in data.iteritems():
    print key, value

#Python 3

for key, value in data.items():
    print(key, value)

Solution 5 - Python

I had a similar issue with Pandas, you need to use the iterrows() function to iterate through a Pandas dataset Pandas documentation for iterrows

data = pd.read_csv('foo.csv')
for index,item in data.iterrows():
    print('{} {}'.format(item["gravatar_id"], item["position"]))

note that you need to handle the index in the dataset that is also returned by the function.

Solution 6 - Python

As a rule of thumb, when I receive this error in Python I compare the function signature with the function execution.

For example:

def print_files(file_list, parent_id):
    for file in file_list:
        print(title: %s, id: %s' % (file['title'], file['id']

So if I'll call this function with parameters placed in the wrong order and pass the list as the 2nd argument and a string as the 1st argument:

print_files(parent_id, list_of_files) # <----- Accidentally switching arguments location

The function will try to iterate over the parent_id string instead of file_list and it will expect to see the index as an integer pointing to the specific character in string and not an index which is a string (title or id).

This will lead to the TypeError: string indices must be integers error.

Due to its dynamic nature (as opposed to languages like Java, C# or Typescript), Python will not inform you about this syntax error.

Solution 7 - Python

This can happen if a comma is missing. I ran into it when I had a list of two-tuples, each of which consisted of a string in the first position, and a list in the second. I erroneously omitted the comma after the first component of a tuple in one case, and the interpreter thought I was trying to index the first component.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAmandaView Question on Stackoverflow
Solution 1 - PythonbluepnumeView Answer on Stackoverflow
Solution 2 - PythonTamásView Answer on Stackoverflow
Solution 3 - PythonwinklerrrView Answer on Stackoverflow
Solution 4 - PythonJohn MachinView Answer on Stackoverflow
Solution 5 - PythoncoremonkeyView Answer on Stackoverflow
Solution 6 - PythonRtmYView Answer on Stackoverflow
Solution 7 - Pythonuser1544219View Answer on Stackoverflow