8

The question is to remove negatives from numbers.

When remove_negs([1, 2, 3, -3, 6, -1, -3, 1]) is executed, the result is: [1, 2, 3, 6, -3, 1]. The result is suppose to be [1, 2, 3, 6, 3, 1]. what is happening is that if there are two negative numbers in a row (e.g., -1, -3) then the second number will not get removed. def main(): numbers = input("Enter a list of numbers: ") remove_negs(numbers)

def remove_negs(num_list): 
  '''Remove the negative numbers from the list num_list.'''
    for item in num_list: 
        if item < 0: 
           num_list.remove(item) 

    print num_list

main()
user3161743
  • 161
  • 4
  • 8
  • 13
  • 1
    Sounds like a good job for a list comprehension which checks for val >= 0. – Dogweather Jan 06 '14 at 21:56
  • 2
    Don't remove items from a list you are iterating over as that changes the index of the items and you could end up skipping over some of the elements. – kylieCatt Jan 06 '14 at 21:58
  • 1
    As others said, don't change the content of data container while you are iterating over it. Iterate over a copy - for item in list(num_list): – macfij Jan 06 '14 at 22:00
  • 2
    Also, you should not call `remove` to delete an item when you already know where it is. This is (a) potentially wrong, because `remove` deletes the first item that's equal to `item`, which may not be the one you want, and (b) slow, because it means you have to search the whole list to find the item even though you already knew where it was. `del num_list[index]` is always better. But how do you know the index? Simple: `for index, item in enumerate(num_list):`. (This is all a side note; it won't solve your actual problem… but there are already good answers and comments for that.) – abarnert Jan 06 '14 at 22:02

8 Answers8

15

It's generally a bad idea to remove elements from a list while iterating over it (see the link in my comment for an explanation as to why this is so). A better approach would be to use a list comprehension:

num_list = [item for item in num_list if item >= 0]

Notice that the line above creates a new list and assigns num_list to that. You can also do an "in-place" assignment of the form

num_list[:] = ...

which does not create a new list in memory, but instead modifies the memory location already being pointed to by num_list. This difference is explained in more detail here.

Community
  • 1
  • 1
arshajii
  • 127,459
  • 24
  • 238
  • 287
  • I don't think the OP actually needs to mutate `num_list` here, so the original version (without the slice assignment) was probably fine. Maybe it would be even better to show both and explain the difference? – abarnert Jan 06 '14 at 22:00
  • @abarnert Yes, perhaps you're right. I'll edit to explain the difference. – arshajii Jan 06 '14 at 22:02
  • 1
    @user3161743: You said "THe result is suppose to be `[1, 2, 3, 6, 3, 1]`." That's exactly what arshajii's code produces. If you did it the way you're claiming to want, the result would be `[1, 2, 3, 3, 6, 1, 3, 1]`. – abarnert Jan 06 '14 at 22:04
13

Much simpler:

>>> a = [ 1, 2, 3, -3, 6, -1, -3, 1]
>>> [x for x in a if x >= 0 ]
[1, 2, 3, 6, 1]

If you really do want to loop, try this:

def remove_negs(num_list): 
    r = num_list[:]
    for item in num_list: 
        if item < 0: 
           r.remove(item) 
    print r

This does what you want:

>>> remove_negs([ 1, 2, 3, -3, 6, -1, -3, 1])
[1, 2, 3, 6, 1]

The key is that the assignment statement r = num_list[:] makes a copy of num_list. In order not to confuse the loop, We then remove items from r rather than from the list we are looping over.

More: Python's treatment of variables is a bit subtle. Python keeps variable names, like r or num_list separate from variable data, such as [1, 2, 3, 6, 1]. The names are merely pointers to the data. Consider the assignment statement:

r = num_list

After this statement is run, r and num_list both point to the same data. If you make a change to r's data, you are also making a change to num_list's data because they both point to the same data. Now, consider:

r = num_list[:]

This statement tells python to modify num_list's data by taking only certain elements of it. Because of this, python makes a copy of num_list's data. It just so happens that [:] specifies that we want all of num_list's data unchanged but that doesn't stop python from making a copy. The copy is assigned to r. This means that r and mum_list now point to different data. We can make changes to r's data and it doesn't affect num_list's data because they have different data.

If this is new to you, you might want to look at this tutorial about python's approach to variable names and variable data: Understanding Python variables and Memory Management

Examples:

>>> a = [ 1, 2, 3, -3, 6, -1, -3, 1]
>>> b = a   # a and b now point to the same place
>>> b.remove(-1) 
>>> a
[1, 2, 3, -3, 6, -3, 1]

Contrast with:

>>> a = [ 1, 2, 3, -3, 6, -1, -3, 1]
>>> b = a[:] # a and b now point to different data
>>> b
[1, 2, 3, -3, 6, -1, -3, 1]
>>> b.remove(-1)
>>> b
[1, 2, 3, -3, 6, -3, 1]
>>> a
[1, 2, 3, -3, 6, -1, -3, 1]
John1024
  • 109,961
  • 14
  • 137
  • 171
5

Another solution

filter( lambda x: x>0, [ 1, 2, 3, -3, 6, -1, -3, 1])
[1, 2, 3, 6, 1]
sergiohzlz
  • 113
  • 2
  • 5
1

From a comment on arshajii's answer:

but that's removing the negative numbers. i need the negative signs removed but still keep the number in the list.

Removing the negative numbers is exactly what your code is clearly trying to do, and it's also the only way to get the desired result:

THe result is suppose to be [1, 2, 3, 6, 3, 1]

But if you really want to "remove the negative signs" from the numbers, that's even easier. For example, to remove the negative sign from -3, you just negate it and get 3, right? You can do this in-place, as in your existing code:

for index, item in enumerate(num_list): 
    if item < 0: 
       num_list[index] = -item

… or in a list comprehension, as in arshajii's:

num_list = [-item if item < 0 else item for item in num_list]

And it's even easier with the abs function, which does exactly that—negates negative numbers, leaves positive and zero alone:

num_list = [abs(item) for item in num_list]

Either way, of course, this will give you [1, 2, 3, 3, 6, 1, 3, 1], which is the wrong answer… but if your comment is correct, it's the answer you asked for.

abarnert
  • 354,177
  • 51
  • 601
  • 671
1

Other the the conventional variable operator non-variable Try some yoda conditions too =)

>>> [i for i in x if 0 <= i]
[1, 2, 3, 6, 1]
alvas
  • 115,346
  • 109
  • 446
  • 738
1

I think an elegant solution can be like this:

import numpy as np

x = [1, 2, 3, -3, 6, -1, -3, 1] # raw data
x = np.array(x) # convert the python list to a numpy array, to enable 
                  matrix operations 
x = x[x >=0] # this section `[x >=0]` produces vector of True and False 
               values, of the same length as the list x
             # as such, this statement `x[x >=0]` produces only the 
               positive values and the zeros

print(x)   
[1 2 3 6 1] # the result
Amjad
  • 3,110
  • 2
  • 20
  • 19
0

to strip the numbers off their negative signs, this is the easiest thing to do

def remove_negs(somelist):
    for each in somelist:
        if each < 0:
            somelist[somelist.index(each)] = -each
    print(somelist)

for example

rNegatives([-2,5,11,-1])

prints out

[2,5,11,1]

zx485
  • 28,498
  • 28
  • 50
  • 59
0
A=[1,2,-3,5,-8,-22,-4,6,9,-12]
B=list(filter(lambda x: x >= 0, A))
print(B)
xuhdev
  • 8,018
  • 2
  • 41
  • 69