khushbu
khushbu

Reputation: 579

How to loop through each item inside a list of list in python?

I have a list of items stored in a list as a list of the list. I want to remove a particular character from each item if it is found. I am able to do it if I just use the first element of the list. However, I get "IndexError: list index out of range" while using for loop.

This is my list.

list1 = [['2.6x3.65'],[],['2','2.9x1.7','2.5x1.3']]

This is how I do for the first element.

if('x' in list1[0][0]):
  rep1 = re.sub('x', ', ', list1[0][0])

This gives me the output as 2.6, 3.65 in string format which I can later convert to float.

However, when I implement the same using for loop using the following code:

 for i in list1[i][i]:
   if('x' in list1[i][i]):
     rep2 = re.sub('x', ', ', list1[i][i])

It gives "IndexError: list index out of range" while using for loop.

My expected result is to get like the following:

 list2 = [[2.6, 3.65],[],[2, 2.9, 1.7, 2.5, 1.3]]

Upvotes: 1

Views: 1140

Answers (4)

norok2
norok2

Reputation: 26886

Your code is likely getting the error because of this line:

for i in list1[i][i]:
    ...

It is not clear what the value of i is when evaluating list[i][i], but it would not work regardless, because what you would like to do is looping through your main list and also within each of its elements, which are also lists. While you could figure this out with explicit indexing, Python offers you a better approach, namely looping through the elements directly.

Most importantly, in general, your regular expression approach would not be working because:

re.sub('x', ', ', list1[0][0])

is actually producing a string, which when printed, looks identical to the way Python would print a list of numbers, but it is not a list of numbers!

What you want to do instead is to convert your string to a numeric type. If all you need is a list of floats, then just casting a valid text representation of a float would do the trick e.g. float('1.5') == 1.5. The same would hold for int, e.g. int('432') == 432 (but int('1.5') will raise a ValueError). If you want to have different objects for ints and floats, as your expected output indicates, you could just first try to convert to int and if that fails, convert to float:

def to_number(value): 
    try: 
        value = int(value) 
    except ValueError: 
        value = float(value) 
    finally: 
        return value

With this in mind, you now need to make sure that the input of that function is going to be a string with a number. Obviously, 1x2 or even 1, 2 are not, i.e. both int('1x2') and int('1, 2') (or with float instead of int) would rise a ValueError. However, Python offers a simple way of parsing (well, splitting) that string in such a way that you would get '1' and '2' in a list:

'1x2'.split('x') == ['1', '2']

This also has the rather benign behavior of gracefully producing a consistent output even if you apply this to a text not containing the char to split, e.g.:

'1'.split('x') == ['1']

With all this building blocks, it is now possible to craft a sensible solution (making heavy use of list comprehensions):

list2 = [
    [to_number(x) for elem in inner_list for x in elem.split('x')]
    for inner_list in list1]

print(list2)
# [[2.6, 3.65], [], [2, 2.9, 1.7, 2.5, 1.3]]

(EDIT: added a number of explanations and wrote the code fully as list comprehensions).

Upvotes: 2

Olvin Roght
Olvin Roght

Reputation: 7812

You can use nested list comprehension:

list1 = [['2.6x3.65'], [], ['2', '2.9x1.7', '2.5x1.3']]
list2 = [sum([list(map(float, i.split('x'))) for i in l], []) for l in list1]

Output:

[[2.6, 3.65], [], [2.0, 2.9, 1.7, 2.5, 1.3]]

To not mix map() with list comprehension:

list2 = [[float(e) for i in l for e in i.split('x')] for l in list1]

Upvotes: 3

AdamGold
AdamGold

Reputation: 5051

One approach would be to loop over your list and append to a new one:

list1 = [['2.6x3.65'],[],['2','2.9x1.7','2.5x1.3']]
new_l = []
for l in list1:
     temp = []
     for elem in l:
         new_s = elem.split("x")
         temp.extend([float(x) for x in new_s])
     new_l.append(temp)


print(new_l)
# [[2.6, 3.65], [], [2.0, 2.9, 1.7, 2.5, 1.3]]

Upvotes: 1

Rakesh
Rakesh

Reputation: 82755

This is one approach using ast and itertools.chain.

Ex:

from itertools import chain
import ast

list1 = [['2.6x3.65'],[],['2','2.9x1.7','2.5x1.3']]
result = []

for i in list1:
    temp = []
    for j in i:
        temp.append(map(ast.literal_eval, j.split("x")))
    result.append(list(chain.from_iterable(temp)))

print(result)

Output:

[[2.6, 3.65], [], [2, 2.9, 1.7, 2.5, 1.3]]

Upvotes: 1

Related Questions