List index out of range in loop [duplicate] - python

This question already has answers here:
python : list index out of range error while iteratively popping elements
(12 answers)
Closed 2 years ago.
Im getting an error for list index being out of range. Sorry if this is a stupid question.
def filter_list(l):
for x in range(0, len(l)):
if type(l[x]) is str:
del l[x]
return l

When you use del to delete the item in the list, the list actually shortens and it seems like that is what is causing the problem.
If you would like to filter lists, you could use list comprehensions like so:
def filter(l):
return [item for item in l if type(item) is not str]
Usually, when looping over lists, it is good practice not to delete or insert new items.
Hope this helped

You should not definitely change a list while iterating over it. It is very bad practise... it can lead to a whole lot of errors for you. Either you should create a copy of use something else, as list comprehension:
def filter_list(l):
return [x for x in l if type(x) is not str]
print(filter_list([1, 4, 5, 's', 'demo'])) # Prints [1, 4, 5]

Related

Python - List Filtering [duplicate]

This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 2 years ago.
Can someone explain to me why this code removes only one element from the list not each as I would expect?
So I have a list, let's say it is: [1,2,'a','b']
I want to remove each string from the list.
def filter_list(l):
for i in l:
if type(i) == str:
l.remove(i)
filter_list([1,2,'a','b'])
So after this I receive = [1,2,'b'] which is confusing.
Check this out:
l = [1,2,'a','b']
print([item for item in l if isinstance(item, int)])
I suggest you use isinstance built-in function instead of type. And also it is better to check is an integer in case of your list has an item that different from string or integer.
If you want to remove it, you can simply assign the new list on the old one.
l = [1,2,'a','b']
l = [item for item in l if isinstance(item, int)]
print(l)
Why you cannot over loop:
Let me explain.
def filter_list(l):
for index, i in enumerate(l):
if type(i) == str:
l.remove(i)
return l
filter_list([1,2,'a','b'])
1st iteration the index of for loop is 0:
i becomes 1, and no need to remove it.
2nd iteration the index of for loop is 1:
i becomes 2, and no need to remove it.
3rd iteration the index of for loop is 2:
i becomes 'a', and need to remove it.
At this point, we simply removed the 'a'. The length of the list changed.
It decreased by 1. The new list became [1, 2, 'b'] because we removed 'a'.
4th iteration the index of for loop is 3:
Since we removed the variable in the previous iteration, the list no longer has the index of 3. The loop will raise StopIteration error(IndexError), and breaks.
That is why we cannot do this properly.
Lambda Function:
[*filter(lambda x: isinstance(x, int), l)]
Data:
l = [1,2,'a','b']

append function in python [duplicate]

This question already has answers here:
Ellipsis lists [...] and concatenating a list to itself [duplicate]
(3 answers)
Closed 6 years ago.
I have a list X = ['xyz']
I use the below commands for appending to another variable.
L = X
L.append(X)
L
Out[109]: ['xyz', [...]]
I am unable to understand why the second element in the new L list is not having the value as 'xyz'
My question is not how to append or extend a list, but in fact about the functionality of Append function, which has been correctly explained by #sinsuren below.
append add X as an element to L. If you want the element inside X to be inserted to L, use extend instead:
>>> X = ['xyz']
>>> L = X
>>> L.extend(X)
>>> L
['xyz', 'xyz']
Try this, it will extend the list.
L.extend(X)
But if you want to use append. Use a element like this L.append('abc'). It will give same result or L.append(X[0])
Edit: You have Appended list to itself. It will recursively append to itself and due to which even L[1] will give you same response. Like L[1] = ['xyz', [...]] . and for more understanding Please refer What's exactly happening in infinite nested lists?

How does the list comprehension to flatten a python list work? [duplicate]

This question already has answers here:
How can I use list comprehensions to process a nested list?
(13 answers)
Closed 7 months ago.
I recently looked for a way to flatten a nested python list, like this: [[1,2,3],[4,5,6]], into this: [1,2,3,4,5,6].
Stackoverflow was helpful as ever and I found a post with this ingenious list comprehension:
l = [[1,2,3],[4,5,6]]
flattened_l = [item for sublist in l for item in sublist]
I thought I understood how list comprehensions work, but apparently I haven't got the faintest idea. What puzzles me most is that besides the comprehension above, this also runs (although it doesn't give the same result):
exactly_the_same_as_l = [item for item in sublist for sublist in l]
Can someone explain how python interprets these things? Based on the second comprension, I would expect that python interprets it back to front, but apparently that is not always the case. If it were, the first comprehension should throw an error, because 'sublist' does not exist. My mind is completely warped, help!
Let's take a look at your list comprehension then, but first let's start with list comprehension at it's easiest.
l = [1,2,3,4,5]
print [x for x in l] # prints [1, 2, 3, 4, 5]
You can look at this the same as a for loop structured like so:
for x in l:
print x
Now let's look at another one:
l = [1,2,3,4,5]
a = [x for x in l if x % 2 == 0]
print a # prints [2,4]
That is the exact same as this:
a = []
l = [1,2,3,4,5]
for x in l:
if x % 2 == 0:
a.append(x)
print a # prints [2,4]
Now let's take a look at the examples you provided.
l = [[1,2,3],[4,5,6]]
flattened_l = [item for sublist in l for item in sublist]
print flattened_l # prints [1,2,3,4,5,6]
For list comprehension start at the farthest to the left for loop and work your way in. The variable, item, in this case, is what will be added. It will produce this equivalent:
l = [[1,2,3],[4,5,6]]
flattened_l = []
for sublist in l:
for item in sublist:
flattened_l.append(item)
Now for the last one
exactly_the_same_as_l = [item for item in sublist for sublist in l]
Using the same knowledge we can create a for loop and see how it would behave:
for item in sublist:
for sublist in l:
exactly_the_same_as_l.append(item)
Now the only reason the above one works is because when flattened_l was created, it also created sublist. It is a scoping reason to why that did not throw an error. If you ran that without defining the flattened_l first, you would get a NameError
The for loops are evaluated from left to right. Any list comprehension can be re-written as a for loop, as follows:
l = [[1,2,3],[4,5,6]]
flattened_l = []
for sublist in l:
for item in sublist:
flattened_l.append(item)
The above is the correct code for flattening a list, whether you choose to write it concisely as a list comprehension, or in this extended version.
The second list comprehension you wrote will raise a NameError, as 'sublist' has not yet been defined. You can see this by writing the list comprehension as a for loop:
l = [[1,2,3],[4,5,6]]
flattened_l = []
for item in sublist:
for sublist in l:
flattened_l.append(item)
The only reason you didn't see the error when you ran your code was because you had previously defined sublist when implementing your first list comprehension.
For more information, you may want to check out Guido's tutorial on list comprehensions.
For the lazy dev that wants a quick answer:
>>> a = [[1,2], [3,4]]
>>> [i for g in a for i in g]
[1, 2, 3, 4]
While this approach definitely works for flattening lists, I wouldn't recommend it unless your sublists are known to be very small (1 or 2 elements each).
I've done a bit of profiling with timeit and found that this takes roughly 2-3 times longer than using a single loop and calling extend…
def flatten(l):
flattened = []
for sublist in l:
flattened.extend(sublist)
return flattened
While it's not as pretty, the speedup is significant. I suppose this works so well because extend can more efficiently copy the whole sublist at once instead of copying each element, one at a time. I would recommend using extend if you know your sublists are medium-to-large in size. The larger the sublist, the bigger the speedup.
One final caveat: obviously, this only holds true if you need to eagerly form this flattened list. Perhaps you'll be sorting it later, for example. If you're ultimately going to just loop through the list as-is, this will not be any better than using the nested loops approach outlined by others. But for that use case, you want to return a generator instead of a list for the added benefit of laziness…
def flatten(l):
return (item for sublist in l for item in sublist) # note the parens
Note, of course, that the sort of comprehension will only "flatten" a list of lists (or list of other iterables). Also if you pass it a list of strings you'll "flatten" it into a list of characters.
To generalize this in a meaningful way you first want to be able to cleanly distinguish between strings (or bytearrays) and other types of sequences (or other Iterables). So let's start with a simple function:
import collections
def non_str_seq(p):
'''p is putatively a sequence and not a string nor bytearray'''
return isinstance(p, collections.Iterable) and not (isinstance(p, str) or isinstance(p, bytearray))
Using that we can then build a recursive function to flatten any
def flatten(s):
'''Recursively flatten any sequence of objects
'''
results = list()
if non_str_seq(s):
for each in s:
results.extend(flatten(each))
else:
results.append(s)
return results
There are probably more elegant ways to do this. But this works for all the Python built-in types that I know of. Simple objects (numbers, strings, instances of None, True, False are all returned wrapped in list. Dictionaries are returned as lists of keys (in hash order).

Get single elements of list of list of list [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Flatten (an irregular) list of lists in Python
I have a list in python like
l=[1,2,[3,4,5],[[4,2,4],[4,7,8]]]
I want using a set to get all unique values, but this fails
set(l)
TypeError: unhashable type: 'list'
So anybody help please? Want to use set with list of list of list etc etc THX
You'll need to 'unwind', or flatten the nested structure before you can put this in a set. You can use a generator for that to keep this efficient for large lists:
def flatten(lst):
for element in lst:
if isinstance(element, list):
for subelement in flatten(element):
yield subelement
else:
yield element
then use that generator on your list l to create a set:
set(flatten(l))
How about this approach, you flatten the list first before you apply the set operation on it.
import collections
def flat_list(tlist):
if isinstance(tlist, collections.Iterable):
return [j for i in tlist for j in flat_list(i)]
else:
return [tlist]
then:
myl=[1,2,[3,4,5],[[4,2,4],[4,7,8]]]
print set(flat_list(myl))
gives:
set([1, 2, 3, 4, 5, 7, 8])
#MartijnPieters approach with a generator will work more efficiently with very large lists than this list comprehension based approach.

Python removing items from list [duplicate]

This question already has answers here:
Strange result when removing item from a list while iterating over it
(8 answers)
Closed 7 years ago.
I have a list in the given format:
[['John', 'Smith'], ['Linus', 'Torvalds'], ['Bart', 'Simpson']]
There are some elements like this in the list ['Linus Torvalds', ''] and I want to remove those. So why doesn't the following code remove them?
for i in people:
if(i[0] == '' or i[1] == ''):
print people.pop(people.index(i))
You are changing the list while iterating over it and this is the source of your problems. An approach that works is
people[:] = [p for p in people if p[0] != '' and p[1] != '']
this way a new temporary list containing only the elements you want is built and then assigned to the original list object when the operation is complete.
Or even people[:] = [p for p in people if all(p)] if you want to resize the list "in place".
You're modifying the list's length while iterating over it. That causes you to skip values. When you pop one item off the list, here's what happens (stealing from this answer):
[1, 2, 3, 4, 5, 6...]
^
That's the state of the list initially; now say 1 is removed and the loop goes to the second item in the list:
[2, 3, 4, 5, 6...]
^
And so on.
It's a bad idea to remove things from a list as you iterate over it. So, try one of these instead (Also, I think your condition is not what you want it to be - I've fixed it):
L = [['John', 'Smith'], ['Linus', 'Torvalds'], ['Bart', 'Simpson']]
delete_these = []
for index, i in enumerate(L):
if not i[-1].strip():
delete_these.append(i)
for i in delete_these:
L.pop(i)
delete_these = map(lambda x: x-1, delete_these)
OR
L = [i for i in L if i[-1].strip()]
OR
answer = []
for i in L:
if i[-1].strip():
answer.append(i)
OR
i = 0
while i < len(L):
if not L[i][-1].strip():
L.pop(i)
else:
i += 1
Hope this helps

Categories

Resources