2
def move_zeros(array):
    for element in array:
        if element == 0 and type(element) is not bool:
            array.append(array.pop(array.index(element)))
    return array

print(move_zeros([False,1,0,1,2,0,1,3,"a"]))

My result is [1, 1, 2, 1, 3, 'a', False, 0, 0] I don't want False to move, my program sees False as 0.

Alif Jahan
  • 793
  • 1
  • 7
  • 20
Achebe Peter
  • 131
  • 8
  • 3
    What the answers neglect to tell you is that your conditional logic is correct. You're being tripped up by `array.index(element)` which matches `False` even if `element` is `0`. If you were to build a new list rather than mutating your original one your code would work. – Andras Deak -- Слава Україні Jan 06 '20 at 11:42
  • 2
    Does this answer your question? [Moving all zeros to the end of the list while leaving False alone](https://stackoverflow.com/questions/42187105/moving-all-zeros-to-the-end-of-the-list-while-leaving-false-alone) – Georgy Jan 07 '20 at 16:25

7 Answers7

4

This is coming about because you're operating on the list while looping over it, as well as the issue that you've correctly identified, that False == 0 and 0 == 0 both are True in Python. One way to deal with this is the following, using is instead of == to check equality to 0:

def move_zeros(a):
    return [x for x in a if x is not 0] + [x for x in a if x is 0]

print(move_zeros([False,1,0,1,2,0,1,3,"a"]))

Output:

[False, 1, 1, 2, 1, 3, 'a', 0, 0]

Note that the use of is to compare integers is not safe in general because integers outside of the range (-5, 256) have different ids from each other (see this question). In our case however, we are just using 0, so the solution above is safe.

CDJB
  • 14,043
  • 5
  • 29
  • 55
  • 3
    Where does Python guarantee that there's only one `0` object? For example, it doesn't work with 666. After `a = [666]`, `[x for x in a if x is 666]` gives me `[]` because the `666` object in the list comprehension is a different `666` object than the one in the list. – Stefan Pochmann Jan 06 '20 at 11:33
  • 1
    See [this question](https://stackoverflow.com/questions/306313/is-operator-behaves-unexpectedly-with-integers) - `is` works in this case because `0` is in the range `(-5, 256)`. This won't work if we change the value to `666`, but then `666 == False` isn't `True`, so we don't have the problem that the OP is facing. – CDJB Jan 06 '20 at 11:36
  • 3
    @CDJB that's more than just an aside, I would suggest adding that information (and a safe alternative) to your answer. I also don't know how non-CPython implementations handle small ints. – Andras Deak -- Слава Україні Jan 06 '20 at 11:36
  • 4
    @CDJB I don't see a guarantee there. Only the statement that "The current implementation" (of CPython) does that. That's not safe. – Stefan Pochmann Jan 06 '20 at 11:40
  • 8
    Absolutely **not** a correct solution. You should **never** rely on implementation details like this, this is not a guarantee. It could change in a different version of Python, heck, not even a different micro-level could change this. Totally wrong. This solution **is not safe**. – juanpa.arrivillaga Jan 06 '20 at 11:49
  • 3
    Just noticed the paragraph right before the [section of the language reference @StefanPochmann cited](https://docs.python.org/3.8/reference/datamodel.html#the-standard-type-hierarchy) states that "for immutable types, operations that compute new values may actually return a reference to any existing object with the same type and value" and gives an example as well: "after `a = 1; b = 1`, `a` and `b` may or may not refer to the same object with the value one, depending on the implementation". This doubles down on the fact, that identity comparison to small ints is unsafe. – jofrev Jan 06 '20 at 12:44
  • 1
    It is possible in CPython also to have low integers in different memory locations. See [*How to create the int 1 at two different memory locations?*](https://stackoverflow.com/q/21456318/674039) for an example. The intern is there for a performance boost, nothing more - must not be relied on in logic. – wim Jan 06 '20 at 19:36
2

Simple solution and showing that your check is actually ok:

>>> sorted(array, key=lambda x: x == 0 and type(x) is not bool)
[False, 1, 1, 2, 1, 3, 'a', 0, 0]

Or compare with the False singleton:

>>> sorted(array, key=lambda x: x == 0 and x is not False)
[False, 1, 1, 2, 1, 3, 'a', 0, 0]

Or if you're feeling playful, chained comparisons:

>>> sorted(array, key=lambda x: 0 == x is not False)
[False, 1, 1, 2, 1, 3, 'a', 0, 0]
Stefan Pochmann
  • 27,593
  • 8
  • 44
  • 107
1

This does the inplace shuffling of "zero" to the end, costing O(1) space and O(n) time complexity.

def move_zeroes_to_end(nums):
    n = len(nums)
    non_zero = 0
    for i in range(n):
        if nums[i] != 0 or type(nums[i])==bool:
            nums[non_zero],nums[i] = nums[i],nums[non_zero]
            non_zero +=1
    return nums


l=[False,1,0,1,2,0,1,3,"a"]

print(move_zeroes_to_end(l)) #Output:[False, 1, 1, 2, 1, 3, 'a', 0, 0]
mozilla-firefox
  • 864
  • 1
  • 15
  • 23
1

You can do that with pop()+append(), just with a "free counting" loop, which is mainly a while:

def move_zeros(a):
  l=len(a)
  i=0
  while i<l:
    if a[i] is 0:
      a.append(a.pop(i))
      l=l-1
    else:
      i=i+1
  return a

print(move_zeros([False,1,0,1,2,0,1,3,"a"]))

Produces the output maintaining the order as:

[False, 1, 1, 2, 1, 3, 'a', 0, 0]
mozilla-firefox
  • 864
  • 1
  • 15
  • 23
tevemadar
  • 12,389
  • 3
  • 21
  • 49
1

This is my implementation, using counter with loop:

def move_zeros(array):
    result, count = [], 0
    for i in array:
        if i == 0 and type(i) == int: count+= 1
        else: result.append(i)
    return result + [0]*count

print(move_zeros([False,1,0,1,2,0,1,3,"a"]))

Output

[False, 1, 1, 2, 1, 3, 'a', 0, 0]
Vicrobot
  • 3,795
  • 1
  • 17
  • 31
0

You can save False as a String. Then it is not seen as 0. change your code to:

print(move_zeros(["False",1,0,1,2,0,1,3,"a"]))
Reyhaneh Torab
  • 360
  • 5
  • 9
-1

Change:

type(element) is not bool

to:

not element == False and not element == True

It's longer, but it fixes your problem