update all items in a list using function in python - python

Could you please tell what i'm doing wrong and how to fix it.
Thanks
I have a function.
def out(some_list):
test_list = [1,2,3,4]
result = []
for i in some_list:
if i == 1:
test_list = [0,0,0,0]
else:
test_list = test_list
result.append(test_list)
return result
if we print it out it will return:
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
I need to return
[[0, 0, 0, 0], [1,2,3,4], [1,2,3,4], [1,2,3,4]]

This is because the list you are passing in this function has 1 as the value of the first element. For example:
out([1,2,3,4]) # ==> [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
Going through your code step by step:
test_list = [1,2,3,4]
result = []
for i in some_list: # The value of each element in some_list
if i == 1: # If the value is "1" set test_list: [0,0,0,0]
test_list = [0,0,0,0]
else:
test_list = test_list # Otherwise set test_list to itself (doing nothing)
result.append(test_list)
for i in some_list:
The for loop value of i is the value of the element you are on in some_list, it is not the index or position of the element we are on in the list (as it appears this question intends)
if i == 1:
test_list = [0,0,0,0]
If the value is 1, then test_list will be set to [0,0,0,0]. Once this is hit only the value [0,0,0,0] will be appended to result. So if the first element is 1 then you will only see the value [0,0,0,0] in the result, otherwise you will see [1,2,3,4] until the loop hits where the value in the list some_list is 1.
Here are some examples:
out([0,1,2,3]) # [[1, 2, 3, 4], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
out([1,2,3,4]) # [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
out([2,2,5,1]) # [[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [0, 0, 0, 0]]
Hopefully this makes it more clear why you are getting that result.
Edit
In terms of your updated question whats happening here is that when you call .append(fig) is simply a copy of the reference to the fig in memory. Basically whenever it changes all the copies that you appended will change as well. There are two ways you can handle this, first would be have the variable fig be defined in the scope of your loop, this way it's a new and different variable on each loop:
for i in test_list:
fig = [2, 1] # <== In the scope of the loop, so each fig is it's on variable
...
The second way is you could append fig[:], which means it will copy the array fig as a new array and pass that in for append:
for i in test_list:
if i == '0':
fig[0] = off
fig[1] = off
elif i == '1':
fig[0] = off
fig[1] = on
new_list.append(fig[:]) # <== Copy the array fig and append that value

It is because you set test_list = [0,0,0,0]
so even in test_list = test_list it is keeping the result from setting it to [0,0,0,0]
try using
def out(some_list):
test_list = [1,2,3,4]
result = []
for i in some_list:
if i == 1:
result.append([0,0,0,0])
else:
result.append(test_list)
return result

Related

if condition statment trigerred without condition being met for 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]

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)

why the new_list has to be put inside the for loop to prevent global change?

Would any one tell me the difference between these two blocks? I have hard time figuring out why the second one only changes the nested list locally, while the first changes it globally. it seems to me they do the same thing.
my_list = [ ]
new_list = [0, 0, 0 ]## outside the loop
for index in range(5):
my_list.append(new_list)
my_list[0][1] = 5
print(my_list)
## result
[[0, 5, 0], [0, 5, 0], [0, 5, 0], [0, 5, 0], [0, 5, 0]]
my_list = [ ]
for index in range(5):
new_list = [0, 0, 0 ] ## inside the loop
my_list.append(new_list)
my_list[0][1] = 5
print(my_list)
## result
[[0, 5, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
Because inside the for loop, new_list get's redefined, effectively refering to a different list object for every iteration.
Printing the ids for the second case reveals this:
print(*map(id, my_list)) # Notice the different ids
140609203176456 140609194670088 140609194608840 140609212158216 140609194670152
Outside the loop, you append the same list object, the same reference. Changes will be visible througout all references.
Printing the ids for this case shows the same id (i.e, same list) is present:
print(*map(id, my_list)) # same id.
140609194670088 140609194670088 140609194670088 140609194670088 140609194670088

Inserting a value to a sublist in a list. PYTHON

This piece of code is from a tic tac toe game I am making. I have to insert a value into a sublist in a list. This is my attempt at doing just that, but it does not work (Python 3.5.1). Is it possible to use insert() to insert a value into a sublist, if so, how do you do it?
game_board_lst = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
play1_row = int(input("Player 1: What row? "))
play1_col = int(input("Player 1: What column? "))
game_board_lst.insert((play1_row - 1)(play1_col - 1), 1)
Use the subscript operator lst[index].
Since you have a list of lists, you would use lst[row][col]:
>>> game_board_lst = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> game_board_lst[1][1]=3
>>> game_board_lst
[[0, 0, 0], [0, 3, 0], [0, 0, 0]]
Based on your example, I think you would want to modify an existing value, such as:
game_board_lst[play1_row-1][play1_col-1] = 1

Nested List Indices [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 9 years ago.
I have experienced some problem by using a nested list in Python in the code shown bleow.
Basically, I have a 2D list contains all 0 values, I want to update the list value in a loop.
However, Python does not produce the result I want. Is there something that I misunderstand about range() and Python list indices?
some_list = 4 * [(4 * [0])]
for i in range(3):
for j in range(3):
some_list[i+1][j+1] = 1
for i in range(4):
print(some_list[i])
The results I expected are:
[0, 0, 0, 0]
[0, 1, 1, 1]
[0, 1, 1, 1]
[0, 1, 1, 1]
But the actual results from Python are:
[0, 1, 1, 1]
[0, 1, 1, 1]
[0, 1, 1, 1]
[0, 1, 1, 1]
What's going on here?
The problem is caused by the fact that python chooses to pass lists around by reference.
Normally variables are passed "by value", so they operate independently:
>>> a = 1
>>> b = a
>>> a = 2
>>> print b
1
But since lists might get pretty large, rather than shifting the whole list around memory, Python chooses to just use a reference ('pointer' in C terms). If you assign one to another variable, you assign just the reference to it. This means that you can have two variables pointing to the same list in memory:
>>> a = [1]
>>> b = a
>>> a[0] = 2
>>> print b
[2]
So, in your first line of code you have 4 * [0]. Now [0] is a pointer to the value 0 in memory, and when you multiply it, you get four pointers to the same place in memory. BUT when you change one of the values then Python knows that the pointer needs to change to point to the new value:
>>> a = 4 * [0]
>>> a
[0, 0, 0, 0]
>>> [id(v) for v in a]
[33302480, 33302480, 33302480, 33302480]
>>> a[0] = 1
>>> a
[1, 0, 0, 0]
The problem comes when you multiply this list - you get four copies of the list pointer. Now when you change one of the values in one list, all four change together:
>>> a[0][0] = 1
>>> a
[[1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0]]
The solution is to avoid the second multiplication. A loop does the job:
>>> some_list = [(4 * [0]) for _ in range(4)]
Actually all the objects in your list are same, so changing one changes others too:
In [151]: some_list = 4 * [(4 * [0])]
In [152]: [id(x) for x in some_list]
Out[152]: [148641452, 148641452, 148641452, 148641452]
In [160]: some_list[0][1]=5 #you think you changed the list at index 0 here
In [161]: some_list
Out[161]: [[0, 5, 0, 0], [0, 5, 0, 0], [0, 5, 0, 0], [0, 5, 0, 0]] #but all lists are changed
Create your list this way:
In [156]: some_list=[[0]*4 for _ in range(4)]
In [157]: some_list
Out[157]: [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
In [158]: [id(x) for x in some_list]
Out[158]: [148255436, 148695180, 148258380, 148255852]
In [163]: some_list[0][1]=5
In [164]: some_list
Out[164]: [[0, 5, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] #works fine in this case

Categories

Resources