how to safely remove elements from a list in Python - python

I loop through a list and remove the elements that satisfy my condition. But why doesn't this work, as noted below? Thank you.
>>> a=[ i for i in range(4)]
>>> a
[0, 1, 2, 3]
>>> for e in a:
... if (e > 1) and (e < 4):
... a.remove(e)
...
>>> a
[0, 1, 3]
>>> a=[ i for i in range(4)]
>>> for e in a:
... if (e > -1) and (e < 3):
... a.remove(e)
...
>>> a
[1, 3]

You cannot change something while you're iterating it. The results are weird and counter-intuitive, and nearly never what you want. In fact, many collections explicitly disallow this (e.g. sets and dicts).
Instead, iterate over a copy (for e in a[:]: ...) or, instead of modifying an existing list, filter it to get a new list containing the items you want ([e for e in a if ...]). Note that in many cases, you don't have to iterate again to filter, just merge the filtering with the generation of the data.

Why don't you just do this initially in the list comprehension? E.g.
[i for i in range(4) if i <= 1 or i >= 4]
You can also use this to construct a new list from the existing list, e.g.
[x for x in a if x <= 1 or x >= 4]

The idea of filtering is a good one, however it misses the point which is that some lists may be very large and the number of elements to remove may be very small.
In which case the answer is to remember the list indexes of the elements to remove and then iterate through the list of indexes, sorted from largest to smallest, removing the elements.

The easiest way to visualize it is to think of the iteration working on list-offsets instead of the actual items - do something to the first item, then the second item, then the third item, until it runs out of items. If you change the number of items in the list, it changes the offsets of all the remaining items in the list:
lst = [1,2,3,4]
for item in lst:
if item==2:
lst.remove(item)
else:
print item
print lst
results in
1
4
[1,3,4]
which makes sense if you step through it like so:
[1,2,3,4]
^
first item is not 2, so print it -> 1
[1,2,3,4]
^
second item is 2, so remove it
[1,3,4]
^
third item is 4, so print it -> 4
The only real solution is do not change the number of items in the list while you are iterating over it. Copy the items you want to keep to a new list, or keep track of the values you want to remove and do the remove-by-value in a separate pass.

It is not safe to remove elements from a list while iterating though it. For that exists the filter function. It takes a function(that admits one argument) and an iterable(in this case your list). It returns a new iterable of the same type(list again here) with the elements where the function applied to that element returned True:
In your case you can use a lambda function like this:
a = filter(lambda x: x > 1 and x < 4, range(4))
or if you have the list already:
a = range(4)
a = filter(lambda x: x > 1 and x < 4, a)
remember that if you are using python3 it will return an iterator and not a list.

Related

Iterate through a list, compare values and remove duplicate - Python

I am curious. How can I correctly iterate through a list, compare two values and delete the duplicate if it exists.
Here I created a nested for loop:
my_list = [ 1, 2, 3, 4, 5 ]
temp = [1, 5, 6]
def remove_items_from_list(ordered_list, temp):
# Removes all values, found in items_to_remove list, from my_list
for j in range(0, len(temp)):
for i in range(0, len(ordered_list)):
if ordered_list[i] == temp[j]:
ordered_list.remove(ordered_list[i])
But when I execute my my code I get an error:
File "./lab3f.py", line 15, in remove_items_from_list
if ordered_list[i] == items_to_remove[j]:
can anyone explain why?
This question, wanted to me compare two lists with one another, and these lists have two different lengths. If an item in list a matched a value in list b, we wanted then to delete it from list a.
You actually can remove items from a list while iterating over it but do read links by #ReblochonMasque.
Here is one way of removing duplicates:
def remove_items_from_list(ordered_list, temp):
n = len(ordered_list)
for i in range(n - 1, -1, -1):
if ordered_list[i] in temp:
del ordered_list[i]
Then
>>> remove_items_from_list(my_list, temp)
>>> print(my_list)
[2, 3, 4]
However, one of the easiest ways of solving your problem is to use sets:
list(set(my_list) - set(temp))
When using this approach, order of items in the resulting list may be arbitrary. Also, this will create a new list instead of modifying an existing list object. If order is important - use list comprehension:
[v for v in my_list if v not in temp]
While you iterating your loop, you remove item from orderer_list which cause index error
Try this:
def remove_items_from_list(ordered_list, temp):
list_ = [x for x in orderer_list if x not in temp]
return list_
first find the duplicated elements and then remove them from the original list.
dup_list = [item for item in temp if item in my_list]
for ele in dup_list:
my_list.remove(ele)
remove() source
You can't remove an items from the list you are iterating over. You can create a copy of the array and remove items from it.

Simple way of excluding an element from a calculation on a list?

For example I want to check the correlation coefficient between two lists like:
r = np.corrcoef(list25, list26)[0,1]
but I want to exclude -1's in the lists from the calculation. Is there a simple one-liner way of doing this instead of making a new copies of the lists and iterating through to remove all -1's and such?
There is a one liner solution. It's creating a new list without the ones. It can be done using List Comprehension:
new_list = [x for x in old_list if x != -1]
it basically copies everything that matches the condition from the old list to the new list.
So, for your example:
r = np.corrcoef([x for x in list25 if x != -1], [x for x in list26 if x != -1])[0,1]
Use a generator
def greater_neg_1(items):
for item in items:
if item>-1:
yield item
Usage:
>>> L = [1,-1,2,3,4,-1,4]
>>> list(greater_neg_1(L))
[1, 2, 3, 4, 4]
or:
r = np.corrcoef(greater_neg_1(list25), greater_neg_1(list26))[0,1]
Won't require any extra memory.
If you actually want to remove the -1 from the lists:
while -1 in list25: list25.remove(-1)

Removing elements from a List in Python

I'm using python 3.4 and just learning the basics, so please bear with me..
listA = [1,2]
for a in listA:
listA.remove(a)
print(listA)
What is suppose is I get an empty list, but what I get is a list with value '2'. I debugged the code with large no. of values in list and when the list is having a single element the for loop exit.
Why is the last element not removed from the list..?
You should not change a list while iterating over it. The indices of the list change as you remove items, so that some items are never evaluated. Use a list comprehension instead, which creates a new list:
[a for a in list if ...]
In other words, try something like this:
>>> A = [1, 2, 3, 4]
>>> A = [a for a in A if a < 4] # creates new list and evaluates each element of old
>>> A
[1, 2, 3]
When you use a for-loop, an internal counter is used. If you shift the remaining elements to the left while iterating over the list, the left-most element in the remaining list will be not be evaluated. See the note for the for statement.
That happens because the length of the for is evaluated only at the beginning and you modify the list while looping on it:
>>> l = [1,2,3]
>>> l
[1, 2, 3]
>>> for a in l:
print(a)
print(l)
l.remove(a)
print(a)
print(l)
print("---")
1
[1, 2, 3]
1
[2, 3]
---
3
[2, 3]
3
[2]
---
>>>
See? The value of the implicit variable used to index the list and loop over it increases and skip the second element.
If you want to empty a list, do a clear:
>>> l.clear()
>>> l
[]
Or use a different way of looping over the list, if you need to modify it while looping over it.
As mentioned by #Justin in comments, do not alter the list while iterating on it. As you keep on removing the elements from the list, the size of the list shrinks, which will change the indices of the element.
If you need to remove elements from the list one-by-one, iterate over a copy of the list leaving the original list intact, while modifying the duplicated list in the process.
>>> listA = [1,2,3,4]
>>> listB = [1,2,3,4]
>>> for each in listA:
... print each
... listB.remove(each)
1
2
3
4
>>> listB
[]

Duplicate list items using list comprehension

I would like to duplicate the items of a list into a new list, for example
a=[1,2]
b=[[i,i] for i in a]
gives [[1, 1], [2, 2]], whereas I would like to have [1, 1, 2, 2].
I also found that I could use:
b=[i for i in a for j in a]
but it seemed like overkill to use two for loops. Is it possible to do this using a single for loop?
You want itertools.chain.from_iterable(), which takes an iterable of iterables and returns a single iterable with all the elements of the sub-iterables (flattening by one level):
b = itertools.chain.from_iterable((i, i) for i in a)
Combined with a generator expression, you get the result you want. Obviously, if you need a list, just call list() on the iterator, but in most cases that isn't needed (and is less efficient).
If, as Ashwini suggests, you want each item len(a) times, it's simple to do that as well:
duplicates = len(a)
b = itertools.chain.from_iterable([i] * duplicates for i in a)
Note that any of these solutions do not copy i, they give you multiple references to the same element. Most of the time, that should be fine.
Your two-loop code does not actually do what you want, because the inner loop is evaluated for every step of the outer loop. Here is an easy solution:
b = [j for i in a for j in (i, i)]
You could use xrange and using a generator expression or a list comprehension
b = (x for x in a for _ in xrange(2))
b = [x for x in a for _ in xrange(2)]
if you do not mind the order:
>>> a = [1,2]
>>> a * 2
[1, 2, 1, 2]

Check if all values of a list are less than a certain number, if not set it to that number

I have a list in python and I want to make sure that all values in the list are greater than some value. If not then I want to set it to that value.
eg: let us assume the list is
a = [1,2,3,4]
and the value to compare is 3. So I want the list to become
a = [3,3,3,4]
I can do this by iterating through all the elements in the list. Is there a better way to do that?
You can reconstruct the list with a simple conditional expression and list comprehension, like this
a = [1, 2, 3, 4]
print [item if item > 3 else 3 for item in a]
# [3, 3, 3, 4]
For every item in a, it checks if it is greater than 3, then use item as it is otherwise use 3.
This is similar but very efficient than the following,
result = []
for item in a:
if item > 3:
result.append(item)
else:
result.append(3)
But remember that list comprehension creates a new list. So, you have may have to do
a = [item if item > 3 else 3 for item in a]

Categories

Resources