1

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]]
3
  • Could it be assumed that the text to parse is always at the same nesting level? Commented Jun 6, 2019 at 10:18
  • Do you have working code that handles a single one of the inner lists? Get that working first, and then put that inside another loop to loop over the lists. Commented Jun 6, 2019 at 10:19
  • Possible duplicate of Python nested loop iterate a list in list Commented Jun 6, 2019 at 10:24

4 Answers 4

3

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]
Sign up to request clarification or add additional context in comments.

10 Comments

This works but I have other characters in the same list that I would like to replace and will not work for those cases. For eg. [ '2' ,'few', '8.5', 'few'] . In these cases, I want to replace 'few' by some numbers. This is the reason I was trying to use replace. But this one works for what I have asked in the question. Could you please help more?
@pari Your question only mentions 'x' as the separator. You can't change the rules of the question when half a dozen people have already gone to the trouble of writing answers!
@PM2Ring, Yeah fair enough! I wouldn't change. I am grateful to everyone.
@pari Thanks for your co-operation. If you need help with that more advanced task, you should ask a fresh question. You can put a link to this question in the new one, if you like.
@pari, check post edits, last version with list comps should be more obvious.
|
2

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).

Comments

1

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]]

Comments

1

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]]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.