This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 6 years ago.
Executing the following sample code to remove elements from list:
l = ['A', 'B', 'C', 'D']
for x in l:
print(x, l)
if x == 'A' or x == 'B':
l.remove(x)
print(l)
The output in both Python 2.x and Python 3.x is:
$ python3 test.py
A ['A', 'B', 'C', 'D']
C ['B', 'C', 'D']
D ['B', 'C', 'D']
['B', 'C', 'D']
The expected output should be:
['C', 'D']
Why is Python behaving this way ?
What is the safest way to remove elements from a list ?
The problem is that when you have deleted 'A', 'B' becomes you first element, and i is "pointing" on the second, which is C. Thus, the check is launched for C and you skipped the check for 'B'
To do what you meant to do, you want to write the following:
a = ['A', 'B', 'C', 'D']
i = 0
while i < len(a):
if a[i] == 'A' or a[i] == 'B':
del a[i]
else:
i += 1
print(a)
This way if you delete the element your loop is currently looking at, you are looking at the element with the same number, which is the right element to look at after you delete it and element shift. I.e.:
a b c d
^
(deleted)
a c d
^
On the other hand, if you do not remove the current element, you just proceed to the next one
That is not the optimal way to delete all occurrences of 'A' or 'B' in Python though
Related
This question already has answers here:
Efficient way to rotate a list in python
(27 answers)
Closed 1 year ago.
here is the code I'm working with but it keeps moving the element backwards once not multiple times
def backwards(input_list):
total = 0
while total < 3:
total +=1
return input_list[1:] + input_list[:1]
example : a = from ['a', 'b', 'c', 'd'] to ['b', 'c', 'd', 'a'] but multiple times
Your issue is that you return on the first iteration of the loop so it runs only once. you can fix it like this:
def backwards(input_list):
total = 0
while total < 3:
total +=1
input_list = input_list[1:] + input_list[:1]
return input_list
but we can improve that:
def backwards(input_list):
total = 0
while total < 3:
total +=1
input_list.append(input_list.pop(0))
return input_list
You can use np.roll() for this. For example:
np.roll(['a', 'b', 'c', 'd'], -1).tolist()
will return:
['b', 'c', 'd', 'a']
I'm not 100% sure what you're trying to achieve, but you might like:
a[::-1]
Which would return your list, but "backwards" using slicing:
['d', 'c', 'b', 'a']
This question already has answers here:
Unpack list into middle of a tuple
(3 answers)
Closed 4 years ago.
How would I create a list element from function call?
Not sure if this is possible, but I've tried to create a list element from a function when i create the list as i'm not sure of the elements up until runtime
So I have tried this:
>>>> def make_list_element():
return 'd, e'
If i then try to create a list and call the function at the same time :
>>>> a = ['a', 'b', 'c', make_list_element().split(", ")]
And I get:
>>> a
>>> ['a', 'b', 'c', ['d', 'e']]
How could I achieve this:
>>> a
>>> ['a', 'b', 'c', 'd', 'e']
Preferably in the same statement as I create the list.
Many thanks
In Python3, you can simply unpack the returned list like so:
a = ['a', 'b', 'c', *make_list_element().split(", ") ]
If you're on Python2, you will have to concatenate or extend the list:
a = ['a', 'b', 'c'] + make_list_element().split(", ")
or
a = ['a', 'b', 'c']
a.extend(make_list_element().split(", "))
The question asked:
Use list comprehensions to generate a list with only the lowercase letters in my_list. Print the result list.
['a', 'A', 'b', 'B', 'c', 'C', 'd', 'D']
My code:
my_list = ['a', 'A', 'b', 'B', 'c', 'C', 'd', 'D']
hi = ([ char for char in range(len(my_list)) if char%2 == 0])
print(hi)
I tried it out, but got integers as answers and not the strings I wanted.
Note: several answers here assume that what you want is to select the values in the list that are lowercase. This answer assumes that that was an example and that the thing you're trying to do is to select the values in the list that occur at every other list index. (This seems to me to be the correct interpretation, because that's what the implementation in the question appears to be trying to do.) I'm not sure who misunderstood the question here, but since the question can be interpreted multiple ways, I think the question is probably at fault here. Until the question is clarified, I think it should be placed on hold.
The simplest and fastest way to do this is with a slice:
print(my_list[::2]) # Slice the whole list, with step=2
To replicate the logic you're describing, where you want to take the values with indexes that are modulo 2, then you need to generate both the indexes and the values for your list in the comprehension, and use one for the filtering and the other for the result:
hi = [ch for ix, ch in enumerate(my_list) if ix % 2 == 0]
Python strings have islower method. Also, you can directly iterate over the list, no need to check its length or the parity of the indexes.
my_list = ['a', 'A', 'b', 'B', 'c', 'C', 'd', 'D']
hi = [char for char in my_list if char.islower()]
print(hi)
# ['a', 'b', 'c', d']
Your list comprehension:
[char for char in range(len(my_list)) if char%2 == 0]
Will produce integers instead of characters. This is because range(len(my_list)) gives you indices. You instead need to get the characters.
This can be done using enumerate():
[char for i, char in enumerate(my_list) if i % 2 == 0]
Or a less pythonic approach, using just indexing my_list:
[my_list[i] for i in range(len(my_list)) if i % 2 == 0]
You can also just filter out the lowercase letters with str.islower():
[char for char in my_list if char.islower()]
Which avoids having to use indices altogether.
You can use list comprehension as following where you iterate over your individual elements and check if it is a lower case using .islower()
my_list = ['a', 'A', 'b', 'B', 'c', 'C', 'd', 'D']
lower = [i for i in my_list if i.islower()]
# ['a', 'b', 'c', 'd']
my_list = ['a', 'A', 'b', 'B', 'c', 'C', 'd', 'D']
res = [ char for char in my_list if ord(char)>=97]
using islower() function
l = ['a', 'A', 'b', 'B', 'c', 'C', 'd', 'D']
result = [el for el in l if el.islower()]
To add a range(len(my_list)) that create the following range(0, 8)
and char, in this case, is an integer and you create a list of integers.
To generate a list with only the lowercase letters use 'islower' method
hi = ([ char for char in my_list if char.islower()])
This question already has answers here:
Wrapping around on a list when list index is out of range
(3 answers)
Closed 5 years ago.
Title may be confusing but I don't know how to express myself any better. What I have is a list that looks something like this:
myList = ['a', 'b', 'c', 'd', 'e', 'f']
what I want to do is a for loop that, for example, starts at index 3, so 'd' and then goes to the end but at the end of the list insted of finishing goes back to the beginning, goes through 'a','b','c' and then finishes at 'd'. I tried doing it like this with a while loop:
index = 3
while index != 2:
if index == len(a):
index = 0
else:
pass
print(a[index])
index += 1
This sorta worked but it will never print out 'c' but if I put 3 as index the loop will never even start. Is there a more elegant solution to do something like this?
You can use the modulus operator against the length of the string
def offset_print(s, offset):
for i in range(len(s)):
print(s[(i+offset) % len(s)])
Example
>>> offset_print('hello', 2)
l
l
o
h
e
So, definitely like #CoryKramer's approach, but this one doesn't require you to calculate the length of the iterator:
def run_offset(lst, fn, offset=0):
base = []
for item in lst:
if offset > 0:
# append the original list until you're no longer dealing with an offset
base.append(item)
offset -= 1
continue
# just yield the current value
yield item
# iterate through the indexes 0... offset
for item in base:
yield item
> list(run_offset('abcd', print, 2))
['c', 'd', 'a', 'b']
Not the most efficient -- but if your list is short and you just want pure simplicity, you can shift your list;
shifted = my_list[n:] + my_list[:n]
for item in shifted:
# do stuff here
... Or use a deque;
>>> import collections
>>> dq = collections.deque(['a', 'b', 'c', 'd'])
>>> dq
deque(['a', 'b', 'c', 'd'])
>>> dq.rotate(2)
>>> dq
deque(['c', 'd', 'a', 'b'])
Using itertools.cycle also works here:
from itertools import cycle
def offset_print(s, offset):
it = cycle(s)
items = [next(it) for i in range(len(s) + offset)]
for x in items[offset:]:
print(x)
Which Outputs:
>>> offset_print(['a', 'b', 'c', 'd', 'e', 'f'], 2)
c
d
e
f
a
b
This question already has answers here:
python : list index out of range error while iteratively popping elements
(12 answers)
Closed 6 years ago.
I am trying to remove duplicates from a list. I am trying to do that with below code.
>>> X
['a', 'b', 'c', 'd', 'e', 'f', 'a', 'b']
>>> for i in range(X_length) :
... j=i+1
... if X[i] == X[j] :
... X.pop([j])
But I am getting
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
IndexError: list index out of range
Please help.
When you start to remove items from a list, it changes in size. So, the ith index may no longer exist after certain removals:
>>> x = ['a', 'b', 'c', 'd', 'e']
>>> x[4]
'e'
>>> x.pop()
'e'
>>> x[4]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
A simpler way to remove duplicate items is to convert your list to a set, which can only contain unique items. If you must have it as a list, you can convert it back to a list: list(set(X)). However, order is not preserved here.
If you want to remove consecutive duplicates, consider using a new array to store items that are not duplicates:
unique_x = []
for i in range(len(x) - 1):
if x[i] != x[i+1]:
unique_x.append(x[i])
unique_x.append(x[-1])
Note that our range bound is len(x) - 1 because otherwise, we would exceed the array bounds when using x[i+1].
#Rushy's answer is great and probably what I would recommend.
That said, if you want to remove consecutive duplicates and you want to do it in-place (by modifying the list rather than creating a second one), one common technique is to work your way backwards through the list:
def remove_consecutive_duplicates(lst):
for i in range(len(lst) - 1, 1, -1):
if lst[i] == lst[i-1]:
lst.pop(i)
x = ['a', 'b', 'b', 'c', 'd', 'd', 'd', 'e', 'f', 'f']
remove_consecutive_duplicates(x)
print(x) # ['a', 'b', 'c', 'd', 'e', 'f']
By starting at the end of the list and moving backwards, you avoid the problem of running off the end of the list because you've shortened it.
E.g. if you start with 'aabc' and move forwards, you'll use the indexes 0, 1, 2, and 3.
0
|
aabc
(Found a duplicate, so remove that element.)
1
|
abc
2
|
abc
3
|
abc <-- Error! You ran off the end of the list.
Going backwards, you'll use the indexes 3, 2, 1, and 0:
3
|
aabc
2
|
aabc
1
|
aabc
(Found a duplicate so remove that element.)
0
|
abc <-- No problem here!
In the last iteration of your list the value of j will be set to i + 1 which will be the length or 8 in this case. You then try to access X[j], but j is beyond the end of the list.
Instead, simply convert the list to a set:
>>> set(X)
{'e', 'f', 'd', 'c', 'a', 'b'}
unless you need to preserve order, in which case you'll need to look elsewhere for an ordered set.
It is generally not advised to mutate a sequence while iterating it since the sequence will be constantly changing. Here are some other approaches:
Given:
X = ['a', 'b', 'c', 'd', 'e', 'f', 'a', 'b']
If you are only interested in removing duplicates from a list (and order does not matter), you can use a set:
list(set(X))
['a', 'c', 'b', 'e', 'd', 'f']
If you want to maintain order and remove duplicates anywhere in the list, you can iterate while making a new list:
X_new = []
for i in X:
if i not in X_new:
X_new.append(i)
X_new
# Out: ['a', 'b', 'c', 'd', 'e', 'f']
If you would like to remove consecutive duplicates, consider #smarx's answer.