This question already has answers here:
How to find the last occurrence of an item in a Python list
(15 answers)
Closed 2 years ago.
I've got this piece of code that modifies a list based on a condition:
lst = [0, 0, 0, 2, 1]
for i, x in enumerate(reversed(lst)):
if x == 0:
lst[-i-1] = 9
break
Is there a way I can do this in a better, more optimised way and without a while loop?
If the list starts with a sequence of 0's, and there are no other 0's in it, you can use list.count to find the position of the final 0 (it will be one less than the count of 0's in the list) and replace it with 9:
lst = [0, 0, 0, 2, 1]
lst[lst.count(0)-1] = 9
print(lst)
Output:
[0, 0, 9, 2, 1]
It appears you just want the starting lst modified by replacing the first element from the end that's a 0 with a 9.
How about this:
lst = [0, 0, 0, 2, 1]
lst[-(list(reversed(lst)).index(0)+1)] = 9
Sadly, you need the list() in there to have the .index(), since the result from reversed() (a list_reverseiterator) doesn't have that method.
Or if you're all about brevity:
lst[-lst[::-1].index(0)-1] = 9
I don't think that helps readability though. (Edit: removed initial -1::-1, the first -1 is superfluous as #Nick correctly pointed out)
Related
I have two lists:
a = [1,4,5]
x = [0,0,0,0,0,0,0,0,0,0]
I am stuck on making it so that it could look like this:
x= [0,1,0,0,1,1,0,0,0,0,0]
So that the number 1 is added to list x based on the value of list a, where the list values in a determine the position of where 1 should go. I am not sure if that makes much sense but it would help a lot.
You can use a for loop to iterate through list a and set the indexes of list x of its values to 1. So for example:
a = [1, 4, 5]
x = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
for index in a: # Iterating through the list a
x[index] = 1 # Setting the values to 1
print(x)
Alternative way,
using numpy fast and efficient
np_x = np.array(x)
np_x[a]=1
This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 3 years ago.
Why does not every 0 get removed from the list?
list = [0,0,5,3,0,8,0,4]
for num in list:
if num == 0:
list.remove(0)
print(list)
Because you are mutating the list while looping over it, and that is almost always a bad idea. Here's a demonstration of what happens:
list = [0,0,5,3,0,8,0,4]
for num in list:
print(num, list)
if num == 0:
list.remove(0)
print(list)
Output
0 [0, 0, 5, 3, 0, 8, 0, 4]
5 [0, 5, 3, 0, 8, 0, 4]
3 [0, 5, 3, 0, 8, 0, 4]
0 [0, 5, 3, 0, 8, 0, 4]
0 [5, 3, 0, 8, 0, 4]
[5, 3, 8, 0, 4]
And here's a relevant section from the documenation, which tells that this is bad idea:
Note: There is a subtlety when the sequence is being modified by the
loop (this can only occur for mutable sequences, e.g. lists). An
internal counter is used to keep track of which item is used next, and
this is incremented on each iteration. When this counter has reached
the length of the sequence the loop terminates. This means that if the
suite deletes the current (or a previous) item from the sequence, the
next item will be skipped (since it gets the index of the current item
which has already been treated). Likewise, if the suite inserts an
item in the sequence before the current item, the current item will be
treated again the next time through the loop. This can lead to nasty
bugs that can be avoided by making a temporary copy using a slice of
the whole sequence, e.g.,
for x in a[:]:
if x < 0: a.remove(x)
Source
You can use a very simple list comprehension to make this work:
list_ = [0,0,5,3,0,8,0,4] # list shadows the built-in name, don't use it
list_ = [num for num in list_ if num != 0]
print(list_)
Output:
[5, 3, 8, 4]
This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 3 years ago.
I have a list in python and every time an element satisfies a certain condition I remove the element. The problem is that the for cycle seems to be skipping some elements. I think it's because the list is moved to the left after a delete. So how to properly remove items in a list? This is my code
list = [0, 0, 0, 1, 1, 0 ,0, 1, 0]
for elem in list:
if elem == 0:
list.remove(elem)
print(list)
and this is the result [1, 1, 0, 1, 0]
You shouldn't delete or add elements to a list whilst iterating over it. Here are some alternatives.
Method 1: Create a new filtered list.
[x for x in my_list if x != 0]
Method 2: If the items are booleans, you can use filter.
list(filter(lambda x: x, my_list)) # or lambda x: x != 0 depending on intent.
Method 3: Create an index of items to be deleted and then subsequently remove them.
idx = [n for n, x in enumerate(my_list) if x == 0]
for i in reversed(idx):
my_list.pop(i)
Method 4: Per the solution of Alex Martelli in the related question
my_list[:] = [x for x in my_list if x != 0]
This assigns to a list slice that matches the my_list variable name, thereby preserving any existing references to this variable.
You can't use .remove() because that removes the first occurrence of the given value, but you need to remove items by their position in the list.
Also you'll want to iterate the positions in reverse order, so removing an item doesn't shift the positions of the remaining (earlier) list items.
for position in reversed(range(len(mylist))):
if mylist[position] == 0:
del mylist[position]
use filter
lst = [0, 0, 0, 1, 1, 0 ,0, 1, 0]
lst = list(filter(lambda elem: elem != 0, lst))
print(lst)
also avoid defining the term list as this is already being used by the built in list type, otherwise you'll prevent yourself from using terms like list(...) inside of this scope.
alternatively you can use a list comprehension
lst = [0, 0, 0, 1, 1, 0 ,0, 1, 0]
lst = [elem for elem in lst if elem != 0]
print(lst)
There are more 'pythonic' ways to solve this problem using a lambda function. But to solve this using just looping it becomes easier to understand:
list = [0, 0, 0, 1, 1, 0 ,0, 1, 0]
i = 0
length = len(list)
while (i < len(list)):
if list[i] == 0:
list.remove(list[i])
i -= 1
length -= 1
else:
i += 1
print(list)
By decreasing i by 1 inside the if statement we can ensure we don't miss any elements. Also to prevent out of bounds looping we must also decrease the length used in the while condition.
You can use this if you always want to remove 0's from your list:
[elem for elem in list if elem]
This obviously gives you a new list without making changes to your existing list.
As you said, when the item is deleted, the list moves. Since the for loop uses the index within the loop, it gets the index wrong and the wrong item gets removed.
A simple way around this, if you don't wish to rewrite a lot of code is to traverse the list backwards
list = [0, 0, 0, 1, 1, 0 ,0, 1, 0]
for elem in reversed(list):
if elem == 0:
list.remove(elem)
print(list)
This question already has answers here:
Some built-in to pad a list in python
(14 answers)
Closed 3 years ago.
As the question stated, I have one list contains x elements.
For example the x is 4:
first_data = [1, 2, 3, 4]
I want to construct another list with a fix size of 8. This list will contain whatever first_data have. If the first_data do not have 8 elements, the rest of the elements will be 0.
Example:
final_data = [1, 2, 3, 4, 0, 0 ,0, 0]
So far I have tried different methods but it didn't work.
How can I achieve this? Thanks and appreciate if any helps!
You could do the following, making use of max and len:
size = 8
final_data = first_data + [0] * max(0, size - len(first_data))
final_data
# [1, 2, 3, 4, 0, 0, 0, 0]
While this is very explicit, you can be more concise and use how list handles multiplication with negative numbers (e.g. [0] * -3 == []):
final_data = first_data + [0] * (size - len(first_data))
If you want to trim first_data down in case it is longer than size, just use first_data[:size] instead of first_data in any of the above examples.
Simple.You can do it like this.
array = [0]*8
arr = [1, 2, 3, 4]
array[:len(arr)] = arr
Let l be your list
You can initialise l like so:
fixed_size = 8
l = [0] * fixed_size
first, you need to know how many zeros are missing. to do so, compare the desired length to what you have:
from typing import List
def pad_with_zeros( initial: List, desired_len: int):
zeros_to_add = desired_len - len(initial)
if zeros_to_add > 0:
padding = [0] * zeros_to_add
return initial + padding
else:
# How do you want to handle the case when the initial list is longer than the desired output?
pass
Then we create a new list of zeros, and extend the initial list using '+'. You will need to handle the case when initial list needs zero padding, or even is bigger than the desired fixed length.
This question already has answers here:
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 6 years ago.
I can't explain the behavior of this code:
n=[[0,0],[0,0]]
m=n.copy()
for i in range(len(m)):
m[i]+=[0]
The output I get is (not what I expected):
>>> m
[[0, 0, 0], [0, 0, 0]]
>>> n
[[0, 0, 0], [0, 0, 0]]
If I type instead:
n=[[0,0],[0,0]]
m=n.copy()
for i in range(len(m)):
m[i]=m[i]+[0]
I get the correct output (which is what I originally expected):
>>> m
[[0, 0, 0], [0, 0, 0]]
>>> n
[[0, 0], [0, 0]]
So, it looks like if I use the "+=" shortcut the two matrices "m" and "n" become aliases. Can somebody please explain why this is happening?.
n.copy() creates a shallow copy, so n[i] and m[i] are already pointing to the same object (although m and n are different).
For lists, x += y is not quite the same thing as x = x + y - the former mutates x directly (it's equivalent to x.extend(y)) while the latter assigns x to a new value.
These two facts combined explain the behaviour.
The difference here is that some_list += some_iterable is effectively the same thing as some_list.extend(some_iterable).
some_list = some_list + [something_else] actually creates a new list out of some_list and [something_else] concatenated together and then assigns that new list back on the left hand side of the = operator.
When you think of it this way, and with the knowledge that after copying, m[idx] is n[idx] for all 0 <= idx < len(m)1, it's easier to see why the += version changes show up in both m and n.
1list.copy() makes a shallow copy -- which means that it only copies references.