Why is this code removing `False` from the array? - python

This is the task:
Write an algorithm that takes an array and moves all of the zeros to the end, preserving the order of the other elements
What is wrong with this code? Why is it not keeping boolean values while iterating through the list and removing zeros? Is it because False is equal to 0?
move_zeros([False,1,2,0,1,0,1,0,3,0,1])
def move_zeros(array):
count=0
for num in array:
if num == 0:
count +=1
array.remove(0)
return array +[0]*count

Yes, False == 0 will evaluate to True in Python which is why False values are being removed from your array.
In Python, True and False are implemented as singletons, meaning all False values point to the same instance. Therefore, you can use the is operator to check if a value is exactly equal to this singleton.
False is 0 will return False, while False == 0 will return True.

x = len(lis)
y = []
for i in range(len(lis)):
if lis[i]!=0:
y.append(lis[i])
if len(y)!=len(lis):
z = len(lis)-len(y)
for i in range(z):
y.append(0)
Seems like its a homework problem. Happy to help tho.
Edit: use print(y) and you'll get what you want

Yes, indeed False == 0. Actually, bool is a subtype of int. If you want to strictly compare if False is equal to 0, then another comparison should be added to the if statement.
Also, do not modify a list while you are iterating over itself. Create a new one instead.
def move_zeros(list_):
result = []
zeros = 0
for item in list_:
if item == 0 and type(item) == int:
zeros += 1
continue
result.append(item)
return result + [0] * zeros

Yes 0 evaluates to False and 1 evaluates to True, use this
def move_zeros(obj):
new_list = []
zeros_list = []
for item in obj:
if item != 0 or is False:
new_list.append(item)
else:
zeros_list.append(item)
new_list.extend(zero_list)
return new_list
This adds all non 0 to one list and all 0's to another, then returns the new_list after iterating over the zeros_list and adding them to the end of the new_list

There are 2 issues in your code:
You directly changed the list over which you iterate — it is always dangerous.
Other answers explained to you why False == 0 is True.
I made as few changes in your code as it was possible to fix these issues:
I created a new, empty array, and instead of removing “bad” elements from the original list, I appended the “good” ones to that new array.
I tested a current element for its type, too.
def move_zeros(array):
count=0
new_array = []
for num in array:
if num == 0 and type(num) is int:
count +=1
else:
new_array.append(num)
return new_array + [0]*count
Test:
move_zeros([False,1,2,0,1,0,1,0,3,0,1])
[False, 1, 2, 1, 1, 3, 1, 0, 0, 0, 0]

Related

'False' is removed from the list when removing other elements from list

I wrote a function to remove all zeroes from a list of elements with different datatypes. But while removing the zeroes. The boolean value 'False' is also removed. But when I change it to 'True' it is not removed. I tried many different methods for doing this, but the result is the same.
def move_zeros(array):
for i in range(array.count(0)):
array.remove(0)
print(array)
move_zeros([0,1,None,2,False,1,0])
The output is
[1, None, 2, 1]
How can I do this without getting the 'False' value removed ?
False and True are equal to 0 and 1 respectively. If you want to remove 0 from a list without removing False, you could do this:
my_list = [0,1,None,2,False,1,0]
my_list_without_zero = [x for x in my_list if x!=0 or x is False]
Since False is a single object, you can use is to check if a value is that specific object.
You could try this code:
def move_zeros(array):
for i in array:
if (type(i) is int or type(i) is float) and i == 0:
addr = array.index(i)
del(array[addr])
return array
This loops over all the items on the array, then checks if it is an integer or float and if it equals 0, then deletes it if it does. The reason False was getting removed is because False, when stored on the program, evaluates to 0.
You can try this:
def move_zeros(array):
array = [x for x in array if x is False or x!=0]
print(array)
move_zeros([0,1,None,2,False,1,0])
Or you can try this:
def move_zeros(array):
newarray = []
for x in array:
if x is False or x!=0:
newarray.append(x)
print(newarray)
move_zeros([0,1,None,2,False,1,0])
after running any of this you should get [1, None, 2, False, 1]

Python, check whether array elements are all same or not

I have an array, say [4 4 4 4 4], here the length is 5. In real case it could be 300. How to check whether all the elements are same, say in this case all are 4. If all elements have same value, the function return true, otherwise false. The element could be only interger and value could be either one of them: 0,1,2,3,4.
I could use a loop in Python as follows. But I am looking for a concise way or simple way to do that, say one line.
x= [4,4,4,4]
temp = x[0]
for ele in x:
if(temp != ele):
false
true
You can put elements into set() and then check if length of the set is equal to 1:
if len(set(x)) == 1:
print('All elements are same')
else:
print('Not same')
it might be more efficient not to iterate over the full list (as does the set constructor) but to stop at the first element that does not equal x0. all does that for you:
x = [4,4,4,4]
x0 = x[0]
print(all(item == x0 for item in x))
# True
this is basically the same version you had; only the loop will be much more efficient this way.
also note that true and false are not valid python identifiers. in python it is True and False.

Check if a certain sequence of numbers appears in an array. What's wrong with my thoughts?

I am learning the fundamentals of Python and doing some finger exercise. This one question is bogging me. I don't know where I did wrong. The question is this:
Given an array of ints, return True if the sequence of numbers 1, 2, 3 appears in the array somewhere.
My solution is:
def array123(nums):
arr1 = [1, 2, 3]
if len(nums) < 3:
return False
for i in range(len(nums)-2):
if nums[i:i+3] == arr1:
return True
else:
return False
I realize that this will give me False as long as the first 3 characters, so I change it to:
def array123(nums):
arr1 = [1, 2, 3]
if len(nums) < 3:
return False
for i in range(len(nums)-2):
print(nums[i:i+3])
if nums[i:i+3] == arr1:
return True
return False
This time it works. I am wondering do I have to move the "return False" out of the sub-code section of the last if?
Thanks.
When you write
if nums[i:i+3] == arr1:
return True
else:
return False
if the condition is not met, then the function stops executing and returns False.
Instead, you want the function to continue executing until it finds a triplet which satisfies that condition.
Yes.
In the first solution if the first 3 ints are not what you are looking for you would stop. That's not what you want. You want to keep going and try other locations.
When you moved it out of the loop you are returning false only after you've checked all positions and none of them matched.

Looping with Consecutive Elements

I'm a beginner in Python programming and I'm having some trouble with some list stuff.
So I want to write a function that returns a boolean value. The function will inform the user whether there is a duplicate consecutive pair of elements in the list he or she entered. By the way, I want to do this using only len(), range(), loops, if-statements, variables and arithmetic (no built in functions).
For example:
contains_consecutive_duplicates([1,3,3]) should return True
contains_consecutive_duplicates([3,2,3]) should return False
My code:
def contains_consecutive_duplicates(xs):
for i in xs:
if xs[i] == xs[i-1] or xs[i] == xs[i+1]:
return True
else:
return False
My logic to this was as follows: Each time the for loop would run, i would refer to some element in the list. If that element was such that the element before it or after it was equal to it, then the for loop would return true. If not, keep searching. If there are none, it would return false.
Now, I actually understand where the error is (I think). The problem is that I don't know how to solve it. I think the for loop is running into a problem at the beginning (when i is reffering to the 0th element). There is no element before the 0th element, hence the error:
"index out of range"
P.S: Is that how you return a boolean value?
Is there a better way to do this?
I would appreciate any help given! Thanks in advance!
#roeland pointed out the problem with assuming iterating the list directly would get you indices (if you want index and value, use enumerate). But in this case you don't actually need the index.
For the simple case (where it's known to be a container you can slice), you can just iterate with zip over offset slices:
def contains_consecutive_duplicates(xs):
return any(x == y for x, y in zip(xs, xs[1:]))
More general solutions can be made with itertools.groupby to handle the case where you can't slice, but this is simple and involves no imports.
Use of any in this case is just a short hand to slurp the generator expression and return True if any values are truthy. The long equivalent form is:
def contains_consecutive_duplicates(xs):
for x1, x2 in zip(xs, xs[1:]):
if x1 == x2:
return True
return False
Since your teacher apparently thinks built-ins are bad, but len and range aren't built-ins (they are), you can do this the dumb way:
def contains_consecutive_duplicates(xs):
for i in range(len(xs) - 1):
if xs[i] == xs[i+1]:
return True
return False
Which does the same work as ziping, just less efficiently (generating index integers and explicitly indexing is surprisingly expensive in Python relative to native iteration).
This should do:
>>> def contains_consecutive_duplicates(xs):
... for i, v in enumerate(xs[:-1]):
... if v == xs[i+1]:
... return True
... else:
... pass
... return False
>>> l1 = [1,3,3]
>>> l2 = [1,3,2]
>>> l3 = []
>>> l4 = [2]
>>> contains_consecutive_duplicates(l1)
True
>>> contains_consecutive_duplicates(l2)
False
>>> contains_consecutive_duplicates(l3)
False
>>> contains_consecutive_duplicates(l4)
False
>>>
By using only range, for and if statements, this can be done with:
def contains_consequtive_duplicates(xs):
for i in range(len(xs)-1):
if xs[i] == xs[i+1]: return True
return False
You access lists with their index and not by their value (which you are by using for i in list). Additionally, if you perform the check xs[i] == xs[i-1] this will not yield the result you want since x[-1] will check the end of the list so [3, 2, 3] will return True.
As a small demonstration:
if contains_consequtive_duplicates([1,3,3]): print "consecutive"
# Prints Consequtive
if contains_consequtive_duplicates([3, 2, 3]): print "consecutive"
# Prints nothing
Try this:
def contains_consecutive_duplicates(xs):
for i in xs:
if xs.indexOf(i)==len(xs):
break
if xs[i] == xs[i-1] or xs[i] == xs[i+1]:
return True
else:
return False
What you are doing is trying to do is evaluate xs[i+1], but that does not exist.
This should do the trick:
def contains_consecutive_duplicates(xs):
for i in range(1, len(xs) - 1):
if xs[i] == xs[i-1] or xs[i] == xs[i+1]:
return True
return False
It iterates through all values bar the first and last (created by the range function), returning (which ends the loop) if it finds a duplicate.
If it reaches the end and hasn't found a duplicate one must not exist, so it returns False.

any() function in Python

I am new to Python and was playing with it until I have a problem with any() function. According to the Python library, the code for it is:
def any(iterable):
for element in iterable:
if element:
return True
return False
I created a list: list = [-1, 1] and I expected:
print any(list) < 0
print any(x < 0 for x in list)
would print two True's and the two statements are equivalent. But instead Python printed
False
True
Why is the first statement False? How is it different from the second one?
any(list) returns a boolean value, based only on the contents of list. Both -1 and 1 are true values (they are not numeric 0), so any() returns True:
>>> lst = [1, -1]
>>> any(lst)
True
Boolean values in Python are a subclass of int, where True == 1 and False == 0, so True is not smaller than 0:
>>> True < 0
False
The statement any(list) is in no way equivalent to any(x < 0 for x in list) here. That expression uses a generator expression to test each element individually against 0, and there is indeed one value smaller than 0 in that list, -1:
>>> (x < 0 for x in lst)
<generator object <genexpr> at 0x1077769d8>
>>> list(x < 0 for x in lst)
[False, True]
so any() returns True as soon as it encounters the second value in the sequence produced.
Note: You should avoid using list as a variable name as that masks the built-in type. I used lst in my answer instead, so that I could use the list() callable to illustrate what the generator expression produces.
As stated in the docs, any(list) returns a boolean. You're comparing that boolean to the integer 0:
>>> any(list)
True
>>> True < 0
False

Categories

Resources