Reputation: 12737
I'm playing with both learning Python and am trying to get GitHub issues into a readable form. Using the advice on How can I convert JSON to CSV?, 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"])
for item in data:
csv_file.writerow([item["gravatar_id"], item["position"], item["number"]])
Where "issues.json" is the JSON file containing my GitHub issues. When I try to run that, I get
TypeError: string indices must be integers
What am I missing here? Which are the "string indices"?
Here's a bit of my JSON content:
{"issues": [{"gravatar_id": "44230311a3dcd684b6c5f81bf2ec9f60", "position": 2.0, "number": 263...
Upvotes: 440
Views: 2179815
Reputation: 23021
As the message says, this error occurs when anything but an integer is used to index a string. Most of the cases leading to this error can be summarized in the following cases (along with a possible solution).
A for-loop over a dictionary is a for-loop over its keys, not its values, so iterating over it to access the values might result in this error. This is common especially if the dictionary is heavily nested.
For example, in the OP's case, a value in a dictionary was a list of dictionaries which contained the key-value pairs needed. So to iterate over the list under the issues
key, access it by data['issues']
and loop over it.
# the data is structured like this
data = {"issues": [
{"gravatar_id": "a", "position": 2.0, "number": 263},
{"gravatar_id": "b", "position": 1.0, "number": 260},
]}
# iterating over `data` would be over `data`'s keys
# we want to loop over the list under `'issues'` key
for item in data:
print(item["gravatar_id"], item["position"], item["number"]) # <--- TypeError
# loop over the list under `issues`
for item in data['issues']:
print(item["gravatar_id"], item["position"], item["number"]) # <--- OK
Yet another example to illustrate the point. Here an attempt to access the inner dictionary while looping over the outer dictionary was made. If we loop over dict_items
of the outer dictionary, we could loop over the inner dictionary because now we have access to them.
data = {
'o1': {'i1': 'value1', 'i2': 'value2'},
'o2': {'i1': 'valu11', 'i2': 'valu22'},
'o3': {'i1': 'val111', 'i2': 'val222'}
}
for item in data:
for k in data[item]:
print(item[k]) # <---- TypeError
for i, item in data.items():
for k in item:
print(item[k]) # <---- OK
dict_items
A value in a dictionary is accessed by its key. However, when the intention was to simply access values in a dictionary but a for-loop over utilizes instead, this error may show up. If .items()
is called on a dictionary, there's no need to access the value/item by key again; simply use the value as is.
data = {'k1': 'value1', 'k2': 'value2', 'k3': 'value3'}
for k, item in data.items():
print(item['k1'], item['k2'], item['k3']) # <---- TypeError
for k, item in data.items():
print(item) # <---- OK
This case commonly occurs when a json object is yet to be converted into a Python object but is used as if it were a dictionary. In the example below, 'data'
is a json object, so if you try to get the value under 'key1'
by data['key1']
, it will show an error.
import json
data = '''
{
"key1": "value1",
"key2": "value2"
}
'''
data['key1'] # <---- TypeError: string indices must be integers
j = json.loads(data)
j['key1'] # <---- OK
When making a http request, an API call, etc. the outcome is usually very nested and it's not very obvious how to handle that data but with a simple debugging step such as printing the type, length etc. of the data usually shows how to handle it.
print(type(data)) # <class 'str'> <---- check the data type
Sometimes the data is not a json object but just a string representation of a Python object, in which case ast.literal_eval()
could be useful to parse it. This case is especially common if these strings are in a list or a pandas DataFrame or some other collection where it's not visibly clear that they are strings.
import ast
data = "{'key1': 'value1', 'key2': 'value2'}"
data['key1'] # <---- TypeError: string indices must be integers
j = json.loads(data) # <---- JSONDecodeError
j = ast.literal_eval(data)
j['key1'] # <---- OK
input()
A common mistake is when one tries to index a string using a value from a user input. Because input()
returns a string, it must be converted into an integer before being used to index a string.
lst = 'my string'
index = input()
lst[index] # <---- TypeError
lst[int(index)] # <---- OK
Another case (that is partially covered in the top two answers here) is to index a string using anything but an integer. The solution is either to slice a string or loop over the list / Series of indices and index the string.
s = 'my string'
s[1,3] # <--- TypeError
s[[1,3]] # <--- TypeError
s[pd.Series([1,3])] # <--- TypeError
s[1:3] # <--- OK
''.join([s[i] for i in [1,3]]) # <--- OK
Upvotes: 3
Reputation: 8388
For me I go this error when I tried to get the id
of each clients looping throw the result returned by the function getClientByPoweruser
;
forgetting that this function returns an object with success
and data
keys rather then list of clients item,
result = await getClientByPoweruser(poweruser_id, db)
for client in result:
print(f'client id:{client["id"]}')
that is why I got the error:
string indices must be integers, not 'str'
to fix this I had simply to loop throw result['data']
array which really contains the list of clients:
for client in result['data']:
print(f'client id:{client["id"]}')
#results
#client id:1
#client id:2
Upvotes: 1
Reputation: 1
Converting the lower case letters to upper:
str1 = "Hello How are U"
new_str = " "
for i in str1:
if str1[i].islower():
new_str = new_str + str1[i].upper()
print(new_str)
Error :
TypeError: string indices must be integers
Solution :
for i in range(0, len(str1))
// Use range while iterating the string.
Upvotes: -2
Reputation: 14717
str[a:b]
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 ✅
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.
>>> 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.
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.
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.
:
to separate its indices (and step range, i.e., str[from:to:step]
),
(i.e., t = 1,
)Upvotes: 120
Reputation: 22068
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.
Upvotes: 1
Reputation: 81
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.
Upvotes: 4
Reputation:
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.
Upvotes: -1
Reputation: 82924
data
is a dict
object. So, iterate over it like this:
for key, value in data.iteritems():
print key, value
for key, value in data.items():
print(key, value)
Upvotes: 66
Reputation: 17108
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
Upvotes: 291
Reputation: 48041
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.
Upvotes: 183