3

I have a structured list of lists in which each element is a string. I want to convert certain (known index, always the same) elements in this list of lists to integers. I've tried using list comprehension or the isdigit() method (there are no negative elements) but can't figure it out.

list_of_lists = [['spam','1','toast'], ['bacon','5','eggs'], ['juice', '8', 'tea']]
new_breakfast_list = [[int(element) for element in row] for row in list_of_lists]

The above code understandably gives ValueError: invalid literal for int() with base 10: 'spam' when it tries converting the first element. I want to either ignore the error and move forward to the next element or maybe specifically loop over something like list_of_lists[i][1] so I can get:

print(new_breakfast_list)
[['spam', 1, 'toast'], ['bacon', 5, 'eggs'], ['juice', 8, 'tea']]

3 Answers3

5

Your list comprehension is almost correct, you just need to check using isdigit() in the inner loop, and use int(element) or just element depending on the value if isdigit.

new_breakfast_list = [[int(element) for element in row if element.isdigit() else element] for row in list_of_lists]

Alternatively, as you know integer elements occur at index 1, you could use:

for element in list_of_lists:
    element[1]=int(element[1])

As you know the indexes you want to change, you should use this method as it saves needlessly looping through every element.

Trelzevir
  • 767
  • 6
  • 11
  • Agree with this: If you know for sure that each inner list has 3 elements, then why loop over them...just index them. (Maybe that's not Pythonic?) – Kenneth K. Feb 21 '17 at 20:03
  • the `for` loop is maybe the simplest way to do it. Not every problem must be solved with list comprehension. – Jean-François Fabre Feb 21 '17 at 20:11
  • I was confused by that to call the correct element I needed a two-digit index e.g. `print(list_of_lists[1])` gives `['bacon', 5, 'eggs']` not `5`. If you write it like `for row in list_of_lists:` \n `row[1]=int(row[1])` it makes more sense to me. Many thanks for the help! – codymccodeface Feb 21 '17 at 20:30
  • Outside of the loop, you would need `list_of_lists[x][1]` to get the desired element as `list_of_lists` is 2-dimensional. However, in the loop, `element` is a list e.g on the first iteration, `element` would be `['spam', 1, 'toast']`. As this is only a 1-dimensional list, you only need to index it using `element[1]` – Trelzevir Feb 21 '17 at 20:34
  • Try `new_lunch_list` – Jon Apr 05 '17 at 14:15
2

since you're dealing with positive integers you can check if the string has only digits in it and convert or not using a ternary.

list_of_lists = [['spam','1','toast'], ['bacon','5','eggs'], ['juice', '8', 'tea']]
new_breakfast_list = [[int(element) if element.isdigit() else element for element in row] for row in list_of_lists]

yields:

[['spam', 1, 'toast'], ['bacon', 5, 'eggs'], ['juice', 8, 'tea']]

2 downsides though:

  • it tries to convert all items, even though only the 2nd one is an integer
  • it doesn't handle negatives (if you need them)

note: there's a cumbersome/not so performant way to handle the general positive/negative/starting with space integer case, use it only it you have to! otherwise the wim answer with try/except is better

new_breakfast_list = [[int(element) if element.strip().isdigit() or (element.strip()[0]=="-" and element.strip()[1:].isdigit()) else element for element in row] for row in list_of_lists]
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
1

Just define your own little helper function, something simple like:

def maybe_int(val):
    try:
        return int(val)
    except ValueError:
        return val

Now you can use that in any kind of comprehension you want. It will continue to work in some cases where using an str.isdigit check fails (e.g. whitespace in the string, negative numbers, etc).

If the structure of the data is reliable, don't bother with this stuff at all. Just do a simple list-comp:

>>> [[x, int(n), y] for x, n, y in list_of_lists]
[['spam', 1, 'toast'], ['bacon', 5, 'eggs'], ['juice', 8, 'tea']]
wim
  • 338,267
  • 99
  • 616
  • 750