4

I have the following problem:

I have a list:

temp= [950, 1000, 1100, 1200, 1400, 1450, 1500, 1600, 1650, 1700, 1900, 1950, 2000, 2100, 2200]

in this list I want to remove every value that is < 1200 and > 1950.

I tried the following:

for x in temp:
    if x < 1200 or x > 1950:
        temp.remove(x)

That gives me the following result:

[1000, 1200, 1400, 1450, 1500, 1600, 1650, 1700, 1900, 1950, 2100]

But the output I am aiming for would be the following:

[1200, 1400, 1450, 1500, 1600, 1650, 1700, 1900, 1950]

I found a way to complete my task with slicing:

new_temp = temp[temp.index(1200):temp.index(1950)+1]

That gives me the output I want:

[1200, 1400, 1450, 1500, 1600, 1650, 1700, 1900, 1950]

But I would like to understand why attemp1 doesn´t work and if maybe there are better ways to fulfil my task than attemp2

Can anyone help? Thanks in advance.

amitchone
  • 1,630
  • 3
  • 21
  • 45
Flow
  • 75
  • 8
  • 1
    `new_list = [num for num in temp if num > 1200 and num < 1950]` – Vineeth Sai Nov 13 '18 at 10:33
  • thanks for the answers. I dont know why my question got graded down, but i can accept that. But it would be very helpful to know why to maybe improve one day. Without any comments to my question in behave of downgrading I unfortunately can not improve myself – Flow Nov 13 '18 at 10:42
  • There's no reason to downvote the question. I've upvoted it. – freakish Nov 13 '18 at 10:43
  • Although there was no reason to downvote, It was downvoted because it was a rookie mistake. Don't worry everybody has their opinions. – Vineeth Sai Nov 13 '18 at 10:44
  • What you want is a list comprehension with inline `if`. If you can't figure out the code from that information let me know ;) – SV-97 Nov 13 '18 at 10:34

4 Answers4

2

Your attempt1 doesn't work because you modify the same list that you loop over. This can lead to all sort of weird behaviour like skipping some values. Here's an example:

>>> lst = [1, 2]
>>> for val in lst:
...     lst.remove(val)
... 
>>> lst
[2]  # wat?

Just don't do that: never modify the list you loop over.

And for the record here's the simpliest correct way (not necessarily fastest):

result = [value for value in temp if 1200 <= value <= 1950]

Also note that your slicing method temp[temp.index(1200):temp.index(1950)+1] works only under the assumption that the list is sorted. And under this assumption we can get a lot faster then any of the presented method. i.e. we could do binary search + slice. But the complexity might not be worth it unless you deal with millions of values.

freakish
  • 54,167
  • 9
  • 132
  • 169
2

I would create a function for identifying valid values:

def validated(value):
    return 1200 <= value <= 1950

Then you can use this in a list comprehension:

>>> [value for value in temp if validated(value)]
[1200, 1400, 1450, 1500, 1600, 1650, 1700, 1900, 1950]

Or you can use filter

>>> list(filter(validated, temp))
[1200, 1400, 1450, 1500, 1600, 1650, 1700, 1900, 1950]

edit: The reason you were having trouble is because you can't modify something you are iterating over. It doesn't "know" that you've pulled the rug out from under it.

An alternative is to make a copy of the list and iterate over that instead.

temp2 = temp[:]  # makes a copy
for x in temp2:
    if x < 1200 or x > 1950:
        temp.remove(x)

Also, the remove is a linear search (which is slow). It's usually better to do the filter. If you really need to modify the list temp refers to (rather than just making temp point at a different list) you can do:

temp[:] = [value for value in temp if validated(value)]

[:] is a slice operator which if you assign to it will replace the entire contents of the original list (rather than assigning a new list to the name).

Peter Wood
  • 23,859
  • 5
  • 60
  • 99
1

@freakish answer above is correct, I just want to make it more clearer.

Skipping values due the list iteration is the main problem of this behaviur

See python list iteration visualization in for loop down below


iteration 1

temp= [950, 1000, 1100, 1200, 1400, 1450, 1500, 1600, 1650, 1700, 1900, 1950, 2000, 2100, 2200]

currentIndex = 0 (item with value 950)

removing element with index 0

going to second iteration


iteration 2

temp looks like that

temp= [1000, 1100, 1200, 1400, 1450, 1500, 1600, 1650, 1700, 1900, 1950, 2000, 2100, 2200]

currentIndex = 1 (item with value 1100)

removing element with index 1

going to third iteration


iteration 3

temp looks like that

temp= [1000, 1200, 1400, 1450, 1500, 1600, 1650, 1700, 1900, 1950, 2000, 2100, 2200]

currentIndex = 2 (item with value 1400)

1400 < 1200 then going to next element.


I hope it should be enought to clearly understand what's going on due such iteration. To avoid such behaviour try to use other methods list exporessions or numpy arrays.

keipa
  • 702
  • 1
  • 8
  • 17
0
import numpy as np
temp= np.array([950, 1000, 1100, 1200, 1400, 1450, 1500, 1600, 1650, 1700, 1900, 1950, 2000, 2100, 2200])
temp[(temp>=1200) & (temp<=1950)]

Output:

array([1200, 1400, 1450, 1500, 1600, 1650, 1700, 1900, 1950])
Venkatachalam
  • 16,288
  • 9
  • 49
  • 77