if condition statment trigerred without condition being met for python - python

I'm solving a simple DSA problem and seem to grasp a general way to solve the question where Given an integer array called nums, I would move all 0's to the end of it while maintaining the relative order of the non-zero elements.
For example, nums = [0,1,0,3,12]
Then the expected output would be. [1,3,12,0,0]
My approach was as below.
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
count = 0
while count < len(nums):
if len(nums[count+1:]) == count:
return nums
elif nums[count] == 0:
nums.pop(nums[count])
nums.append(0)
else:
count += 1
print(nums)
I would expect this to work in my mind, but the output is not what I expected because stdout shows as
[1, 0, 3, 12, 0]
[1, 0, 3, 12, 0]
[0, 3, 12, 0, 0]
[0, 3, 12, 0, 0]
Now I understand why the firsto two prints show as such, because 0 was identifed in the first loop and popped
However I do not understand why in the third and forth iteration, the result shows as [0,3,12,0,0].
The if-conditiona says if nums[count]==0, then nums.pop[nums[count]].
How is the pop and append triggered when the condition is not met?
Am I missing something here?

I simplified in few lines:
def ceros_array(ar):
'''Given and array of integer return zeros at end of array'''
zeros = ar.count(0) # count zeros in array
new_array = [x for x in ar if x !=0] # new array with elements different that zero
for i in range(zeros): # cycle for zeros removed
new_array.append(0) # insert 0 at end of new array
return new_array
I tested with these arrays:
>>> ceros_array([1, 0, 3, 12, 0])
[1, 3, 12, 0, 0]
>>> ceros_array([0, 0, 3, 12, 0])
[3, 12, 0, 0, 0]
>>> ceros_array([0, 0, 3, 0, 0])
[3, 0, 0, 0, 0]
>>> ceros_array([0, 0, 0, 0, 1])
[1, 0, 0, 0, 0]
>>> ceros_array([0, 0, 0, 0, 0])
[0, 0, 0, 0, 0]

Inspired by dannisis's answer:
def shift1(nums: list[int]) -> list[int]:
"""Push all zeros in nums to the back (right) of the list."""
# Keep only non-zeroes
new_nums = [x for x in nums if x != 0]
# Append the correct number of zeroes
new_nums += [0] * nums.count(0)
return new_nums
for nums in [
[1, 0, 3, 12, 0],
[0, 0, 3, 12, 0],
[0, 0, 3, 0, 0],
[0, 0, 0, 0, 1],
[0, 0, 0, 0, 0],
]:
print(shift1(nums))
[1, 3, 12, 0, 0]
[3, 12, 0, 0, 0]
[3, 0, 0, 0, 0]
[1, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
Here's a more general, albeit less performant, solution:
def shift2(nums: list[int]):
_len = len(nums)
shift_ct = 0
for i in range(_len):
if nums[i] == 0:
shift_ct += 1
continue
else:
nums[i - shift_ct] = nums[i]
# "Back fill" end w/any zeroes
i = _len - shift_ct
while i < _len:
nums[i] = 0
i += 1
This mutates the list you pass in:
Nums = list[int] # input to function
Want = list[int] # what we expect in return
test_cases: list[tuple[Nums, Want]] = [
([0, 1], [1, 0]),
([0, 0, 1], [1, 0, 0]),
([0, 1, 2], [1, 2, 0]),
([0, 1, 0, 2], [1, 2, 0, 0]),
([0, 1, 0, 2, 0, 3], [1, 2, 3, 0, 0, 0]),
]
def test_shift2():
for nums, want in test_cases:
got = nums[:] # make copy to keep "input" separate from "result" in print-out, if test fails
shift2(got)
if got != want:
print(f"shift2({nums}) = {got}; want {want}")
I've run a number of timeit tests and profiled both, and I believe shift1 is slightly faster because even though it has two function calls (1 for the list comprehension, 1 for count()), it just has less lines to execute; shift2 only has the one function call (len()), but far more lines for the interpreter to step through.

You need to be very careful about changing structure of iterable objects WHILE iterating through them. Think about what you are doing. On your first loop, you hit 0 when i = 1, you pop that out and append it to the end. So now, all the elements from that point are shuffled up to an index that is one lower. The 2nd 0 was initially at index 2 but is shuffled up to index 1. You then ADD 1 to your index, meaning the next element is skipped and not processed.
Your best solution is to is to append non-zero elements to a second list and then fill with zeros:
def moveZeroes(nums) -> None:
new_list = []
zero_count = 0
for i in nums:
if i > 0:
new_list.append(i)
else:
zero_count += 1
new_list += [0] * zero_count
print(new_list)
moveZeroes([0, 1, 0, 3, 12])

I tested this and this works. (note that I used pop(count), not pop(nums[count]):
NOTE: After posting this answer, I noticed that it would fail if the initial list (my_numbers) starts with more than one zero. A quick and dirty fix for this would be to just call moveZeroes(my_numbers) twice.
def moveZeroes(nums):
count = 0
while count < len(nums):
if nums[count] == 0:
nums.pop(count)
nums.append(0)
count += 1
else:
count += 1
my_numbers = [0,1,0,2,12,0,4]
moveZeroes(my_numbers)
moveZeroes(my_numbers) # added as temporary fix for leading multiple zeroes
print(my_numbers) #prints [1,2,12,4,0,0,0]

Related

How to find all the permutations of a variable amount of 0's and 1's recursively (without using itertools or random)?

I'm trying to produce all permutations of a certain number of numbers (for example, 0s and 1s) for a variable number of positions. I will call the number of numbers ord (e.g. ord=2 for only 0s and 1s; ord=3 for 0s, 1s, and 2s) and the number of positions Num. Hence the number of permutations is ord**Num.
Note: I don't want to use itertools or any other types of built-in functions. I'm asking this out of curiosity, not just trying to find a solution.
For ord=2 and Num=3, the output, in any order, should be:
[[0,0,0],[0,0,1],[0,1,0],[0,1,1],[1,0,0],[1,0,1],[1,1,0],[1,1,1]]
This can be accomplished by:
ord = 2
mylist = []
for a in range(ord):
for b in range(ord):
for c in range(ord):
mylist.append([a,b,c])
For ord = 2 and Num = 4, the output should be:
[[0,0,0,0],[0,0,0,1],[0,0,1,0],[0,0,1,1],[0,1,0,0],[0,1,0,1],[0,1,1,0],[0,1,1,1],[1,0,0,0],[1,0,0,1],[1,0,1,0],[1,0,1,1],[1,1,0,0],[1,1,0,1],[1,1,1,0],[1,1,1,1]]
But then I would have to add another nested for loop:
ord = 2
mylist = []
for a in range(ord):
for b in range(ord):
for c in range(ord):
for d in range(ord):
mylist.append([a,b,c,d])
An obvious solution is to add 0s and 1s randomly to a list of length Num and then to accept that list if it hasn't already been added to mylist, but I want a solution that isn't quite so ridiculous.
This is the closest I've gotten so far to a real solution:
def myperms(elem, mylist):
for i in range(len(elem)-1,-1,-1):
while (elem[i] + 1) < ord:
elem = list(elem)
elem[i] += 1
if elem not in mylist:
mylist.append(elem)
if (elem[i] + 1) >= ord:
elem = list(elem)
elem[i] = 0
return mylist
Num = 3
ord = 2
TotsNum = ord**Num
mylist = []
elem = [0,]*Num
mylist.append(elem)
print(myperms(elem, mylist))
But this only gives:
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [1, 0, 0]]
I've tried calling the function within itself (recursion), but I haven't been able to figure out how to do it properly. Does anyone have any ideas about how to solve it recursively? Thank you!
Let's use a recursive solution:
def get_seq(ord, num):
val = [0]*num
N = num
def recurse(ord, num):
for i in range(ord):
val[N - num] = i
if num > 1:
yield from recurse(ord, num-1)
else:
yield val[:]
return recurse(ord, num)
print(list(get_seq(2, 4)))
Output:
[[0, 0, 0, 0],
[0, 0, 0, 1],
[0, 0, 1, 0],
[0, 0, 1, 1],
[0, 1, 0, 0],
[0, 1, 0, 1],
[0, 1, 1, 0],
[0, 1, 1, 1],
[1, 0, 0, 0],
[1, 0, 0, 1],
[1, 0, 1, 0],
[1, 0, 1, 1],
[1, 1, 0, 0],
[1, 1, 0, 1],
[1, 1, 1, 0],
[1, 1, 1, 1]]
For other inputs:
>>> list(get_seq(3, 2))
[[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]]
Use binary, which consists of 0s and 1s.
n = 4
all_permutations = []
for i in range(2**n):
permutation = []
string = bin(i)
string = string.split("0b")[1]
while len(string) != n:
string = f"0{string}"
for i in string:
permutation.append(int(i))
all_permutations.append(permutation)
print(all_permutations)

How to sum up all neighbouring nonzero value in the middle of a list in Python

I am beginner in Python. I need to solve an issue with summing up neighboring nonzero value in a list.
Say, I have list called
a = [2, 3, 0, 0, 1, 0, 3, 3, 1, 0, 0].
In a, there will be multiple instances where the elements are nonzero. For example, 2 and 3 are neigbouring so I want to sum them up so I would get 5.
Then, there another neighbouring elements which are 3, 3 and 1. This is where I face problem because i want to sum it up to 7.
But in the code that I attempted on, it still print out 4, which is the sum of a[7] and a[8]. Is there any way I can avoid this?
c =[]
for i in range(1, len(a)):
if a[i-1] != 0:
if a[i] != 0:
tot = a[i] + a[i-1]
c.append(tot)
if a[i+1] != 0:
tot = tot + a[i+1]
c.append(tot)
else:
tot = 0;
continue
You can use itertools.groupby and a comprehension:
>>> import itertools
>>> a = [2, 3, 0, 0, 1, 0, 3, 3, 1, 0, 0]
>>> [sum(v) for k, v in itertools.groupby(a, key=lambda x: x != 0) if k != 0]
[5, 1, 7]
You can also use a simple generator function:
def groups(d):
_sum = 0
for i in d:
if not i:
if _sum:
yield _sum
_sum = 0
else:
_sum += i
if _sum:
yield _sum
print(list(groups([2, 3, 0, 0, 1, 0, 3, 3, 1, 0, 0])))
Output:
[5, 1, 7]
As I understand, you want to calculate sub-sums of consecutive non-zero elements.
How about:
#!python3
from typing import List
def solve(arr: List[int]) -> List[int]:
ret = []
tmp = []
for elem in arr+[0]:
if elem != 0:
tmp.append(elem)
else:
if len(tmp):
ret.append(sum(tmp))
tmp = []
return ret
arr = [2, 3, 0, 0, 1, 0, 3, 3, 1, 0, 0]
assert(solve(arr) == [5, 1, 7])
The original list 'a' is [2, 3, 0, 0, 1, 0, 3, 3, 1, 0, 0] where the neighboring non-zero elements in list 'a' are highlighted in bold text.
a = [2, 3, 0, 0, 1, 0, 3, 3, 1, 0, 0]
def fun(a):
tot =[] # sub-list of list a
val=0
for i in a:
if i==0:
if val!=0:
tot.append(val)
val=0
else:
val+=i
return tot
print(fun(a))
# output [5, 1, 7]

List wrapping for finding distance between indices

I have a random generated list that could look like:
[1, 0, 0, 1, 1, 0, 1, 0, 0, 0]
I need to find all of the distance between the 1's including the ones that wrap around.
For an example the list above, the first 1 has a distance of 3 to the next 1. The second 1 has a distance of 1 to the following 1 and so on.
How do I find the distance for the last 1 in the list using wrap around to the first 1?
def calc_dist(loc_c):
first = []
#lst2 = []
count = 0
for i in range(len(loc_c)):
if loc_c[i] == 0:
count += 1
#lst2.append(0)
elif loc_c[i] == 1:
first.append(i)
count += 1
loc_c[i] = count
#lst2.append(loc_c[i])
#if loc_c[i] + count > len(loc_c):
# x = loc_c[first[0] + 11 % len(loc_c)]
# loc_c[i] = x
count = 0
return loc_c
My expected outcome should be [3, 1, 2, 4].
Store the index of the first 1 you first reference, then when you get to the last 1 you only have to add the index of the first plus the number of 0 elements after the last 1 to get that distance (so len(inputlist) - lastindex + firstindex).
The other distances are the difference between the preceding 1 value and the current index.
from typing import Any, Generator, Iterable
def distances(it: Iterable[Any]) -> Generator[int, None, None]:
"""Produce distances between true values in an iterable.
If the iterable is not endless, the final distance is that of the last
true value to the first as if the sequence of values looped round.
"""
first = prev = None
length = 0
for i, v in enumerate(it):
length += 1
if v:
if first is None:
first = i
else:
yield i - prev
prev = i
if first is not None:
yield length - prev + first
The above generator calculates distances as it loops over the sequence seq, yielding them one by one:
>>> for distance in distances([1, 0, 0, 1, 1, 0, 1, 0, 0, 0]):
... print(distance)
...
3
1
2
4
Just call list() on the generator if you must have list output:
>>> list(distances([1, 0, 0, 1, 1, 0, 1, 0, 0, 0]))
[3, 1, 2, 4]
If there are no 1 values, this results in zero distances yielded:
>>> list(distances([0, 0, 0]))
[]
and 1 1 value gives you 1 distance:
>>> list(distances([1, 0, 0]))
[3]
I've made the solution generic enough to be able to handle any iterable, even if infinite; this means you can use another generator to feed it too. If given an infinite iterable that produces at least some non-zero values, it'll just keep producing distances.
Nice and tidy:
def calc_dist(l):
idx = [i for i, v in enumerate(l) if v]
if not idx: return []
idx.append(len(l)+idx[0])
return [idx[i]-idx[i-1] for i in range(1,len(idx))]
print(calc_dist([1, 0, 0, 1, 1, 0, 1, 0, 0, 0]))
# [3, 1, 2, 4]
print(calc_dist([0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0]))
# [3, 1, 2, 7]
print(calc_dist([0, 0, 0, 0])
# []
You can use numpy:
import numpy as np
L = np.array([1, 0, 0, 1, 1, 0, 1, 0, 0, 0])
id = np.where(test == 1)[0]
# id = array([0, 3, 4, 6], dtype=int64)
res = [id[i]-id[i-1] for i in range(1, len(id))]
# [3, 1, 2]
# Last distance missing:
res.append(len(L)- id[-1])
res = [3, 1, 2, 4]
Note that the information you ask for is comprised above, but maybe the output format is wrong. You were not really specific...
Edit: How to convert list to an array since you generate random list
L = [1, 0, 0, 1, 1, 0, 1, 0, 0, 0]
np.asarray(L)
Edit2: How to check if there is no 1 in the list:
import numpy as np
L = np.array([1, 0, 0, 1, 1, 0, 1, 0, 0, 0])
id = np.where(test == 1)[0]
if len(id) == 0:
res = []
else:
res = [id[i]-id[i-1] for i in range(1, len(id))]
res.append(len(L)- id[-1])
OR:
try:
res = [id[i]-id[i-1] for i in range(1, len(id))]
res.append(len(L)- id[-1])
except:
res = []

How to remove mirrors reflections values of itertools.product function?

I create a cartesian product using the itertools.product function:
from itertools import product
a = list(map(list, itertools.product(list(range(2)), repeat=3)))
Output:
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
Then I get rid of mirrors reflections in the following way:
b = []
for k, v in enumerate(a):
if v[::-1] not in a[:k]:
b.append(v[::-1])
Output:
[[0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 1], [1, 1, 1]]
But can I get the same effect step by step without saving all the results of itertools.product in the list? For example, with the usual approach on the for loop:
for i in list(map(list, itertools.product(list(range(2)), repeat=3))):
# blah blah blah
Because ultimately I will use large cartesian products, at least repeat = 18. And that is why I have to give up the approach on the lists. Unless there is another way to do it? I will be grateful for any tips.
import itertools
l = (list(i) for i in itertools.product(tuple(range(2)), repeat=3) if tuple(reversed(i)) >= tuple(i))
print list(l)
Output:
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 1], [1, 1, 1]]
Here is an idea for a recursive algorithm to generate only the necessary combinations (as opposed to generate the whole Cartesian product and discarding the unnecessary ones):
def noReflections(n, k, current=None, idx=0, symmetric=True):
# n: number of distinct elements
# k: sequences length
# current: sequence being generated
# idx: current generated index
# symmetric: true if the chosen elements up to now are symmetric
assert n >= 0 and k >= 0
if n == 0 or k == 0:
return
if idx == 0:
current = k * [0]
if idx < k // 2:
# Choose the value for current position (idx) and symmetric (idx2)
idx2 = k - idx - 1
for i in range(n):
# Value for current position
current[idx] = i
# If all previously selected values were symmetric,
# the symmetric position must have a value equal or greater
# than the current; otherwise it can take any value.
first = i if symmetric else 0
for j in range(first, n):
# Value for symmetric position
current[idx2] = j
# Recursive call
# Only keep symmetric flag if previously selected values
# and the ones selected now are symmetric.
yield from noReflections(n, k, current, idx + 1, symmetric and (i == j))
elif idx == k // 2 and (k % 2 == 1):
# In middle position of odd-length sequence
# Make one sequence with each possible value
for i in range(n):
current[idx] = i
yield tuple(current)
else:
# Even-length sequence completed
yield tuple(current)
print(list(noReflections(2, 3)))
>>> [(0, 0, 0), (0, 1, 0), (0, 0, 1), (0, 1, 1), (1, 0, 1), (1, 1, 1)]
I'm not sure this should perform better than the other answer though, because of the recursion and so on (in a couple of quick tests bot performed similarly in my machine).

How to remove an specific item from a list - Python

I´m a begginer in Python and I want to remove a specific item from a list, the removal criterion is that when an item contains the three firsts elements null it must be removed from the main list. This is what I´m trying to do:
a = [[0, 0, 0, 1],[0, 0, 1, 1],[1, 1, 1, 1]]
temp = []
for i in range(len(a)):
for j in range(len(a[i])):
if a[j][0] + a[j][1] + a[j][2] == 0:
temp = a.pop(j)
print temp
else:
print a
j += 1
i += 1
And I´m getting the following error:
if a[j][0] + a[j][1] + a[j][2] == 0:
IndexError: list index out of range
I have no idea what I´m doing wrong...
Thanks!
You could use a list comprehension to filter out sublists
>>> a = [[0, 0, 0, 1],[0, 0, 1, 1],[1, 1, 1, 1]]
>>> a = [i for i in a if any(i[:3])]
>>> a
[[0, 0, 1, 1], [1, 1, 1, 1]]
This basically works by using slicing to get elements [0] through [2], then uses any to check whether there are non-zero values.
All you need is this (range in this case is unnecessary):
a = [[0, 0, 0, 1],[0, 0, 1, 1],[1, 1, 1, 1]]
newList = []
for i in a:
if i[0] + i[1] + i[2] != 0:
newList.append(i)
print newList
if you want to avoid the any() method call you can just use a conditional check with the in operator.
a = [[0, 0, 0, 1],[0, 0, 1, 1],[1, 1, 1, 1]]
b = [i for i in a if 1 in i[:3]]
# [[0, 0, 1, 1], [1, 1, 1, 1]]

Categories

Resources