Python: del in for loop - python

Problem:
test1 = [1, 2, 3]
for i in range(len(test1)): #Want to do the below for every element in test1
print(test1[i])
if test1[i] % 2 == 0: #if the element meets certain conditions, it gets deleted
del test1[i]
#The thing in test1 gets deleted, but the range gets not updated,
#and i iterates nonetheless, so 1 element gets ignored,
#and in the end, I get "list index out of range"
(In the actual code, there are far more if statements, and the things in the array are not integers but objects?! of a class, so list comprehension is more - meh...)
I've come up with this approach:
test1 = [1, 2, 3]
i = 0
x = len(test1)
while i < x:
print(test1[i])
if test1[i] % 2 == 0:
del test1[i]
x -= 1
i -= 1
i += 1
But as you can see, this is 5 lines longer (and for every del, I have to add 2 lines...), ugly, and annoying. Is there any nicer way to do something like that? Like, a redo command, that goes straight to the for loop, updates the range, and lets you chose i, or something other?

Use a list comprehension
Something along the lines of:
list = [x for x in list if x % 2 == 1] # Keep anything that is not even
for thing in list:
print(thing)
This will create a new list with anything that matches the conditional.
Then print the new list
>>> test1 = [1,2,3]
>>> list = [x for x in test1 if x % 2 is 1]
>>> print(list)
[1, 3]

Have you tried using the reversed on the range?
test1 = [1, 2, 3]
for i in reversed(range(len(test1))):
print(test1[i])
if test1[i] % 2 == 0:
del test1[i]
>>> 3
>>> 2
>>> 1
print(test1)
>>> [1, 3]
This way, when you delete an element, the array get reduced but since you are going through the list in a reversed order, only the index of already processed elements get affected.
EDIT
After reading all the comments, I decided to run some little benchmark. I created a list of 1,000 elements and a list of 10,000,000 elements and tried the 3 ways:
#1 Deleting from list
#2 Creating a new list with list comprehension
#3 Creating a new list with classic for
For the list of 1,000 elements, the time does not really matter but for the 10,000,000 elements list, deleting from the list get pretty much impossible (a couple of hours vs half a second to create a new list).
Test with 1,000 element
>>> Test 0, 11th element: 7e-06 seconds
>>> Test 0, second last element: 2e-06 seconds
>>> Test 1: 0.00017 seconds
>>> Test 2: 0.000103 seconds
>>> Test 3: 0.000234 seconds
Test with 10,000,000 element
>>> Test 0, 11th element: 0.011888 seconds
>>> Test 0, second last element: 4e-06 seconds
>>> Test 1: Too long!!
>>> Test 2: 0.941158 seconds
>>> Test 3: 0.681262 seconds
In regards of this, I would strongly suggest creating a new list with the list comprehension or with a regular for loop.
Here is my code:
from datetime import datetime
from random import randint
# Create my test lists of 10 000 000 elements
test_0 = []
#nb_el = 10000000
nb_el = 1000
while (len(test_0) < nb_el):
test_0.append(randint(0, 100))
test_1 = test_0[:] # clone list1
test_2 = test_0[:] # clone list1
test_3 = test_0[:] # clone list1
# Test #0
# Remove the 11th element and second last element
d1 = datetime.now()
del test_0[10]
d2 = datetime.now()
print('Test 0, 11th element: ' +
str((d2-d1).microseconds / 1000000.0) + ' seconds')
d1 = datetime.now()
del test_0[nb_el-2]
d2 = datetime.now()
print('Test 0, second last element: '
+ str((d2-d1).microseconds / 1000000.0) + ' seconds')
# Test #1 | TOO SLOW TO BE RAN with 10 000 000 elements
# Delete every element where element is a multiple of 2
d1 = datetime.now()
for i in reversed(range(len(test_1))):
if test_1[i] % 2 == 0:
del test_1[i]
d2 = datetime.now()
print('Test 1: ' + str((d2-d1).microseconds / 1000000.0) + ' seconds')
# Test #2
# Create a new list with every element multiple of 2
# using list comprehension
d1 = datetime.now()
test_2_new = [x for x in test_2 if x % 2 == 0]
d2 = datetime.now()
print('Test 2: ' + str((d2-d1).microseconds / 1000000.0) + ' seconds')
# Test #3
# Create a new list with every element multiple of 2
# using for loop
d1 = datetime.now()
test_3_new = []
for i in range(len(test_3)):
if test_3[i] % 2 == 0:
test_3_new.append(test_3[i])
d2 = datetime.now()
print('Test 3: ' + str((d2-d1).microseconds / 1000000.0) + ' seconds')

Assuming you want to print every number before removing them from the list.
myList = [1,2,3]
for n in test1:
print n
test1 = [n for n in myList if n % 2] # 0 = False by default in python. No need for == 0 / > 0.
if you absulutely want to use the code you wrote as it may be easier for you to understand:
test1 = [1, 2, 3]
for i in range(len(test1)): #Want to do the below for every element in test1
print(test1[i])
# Either check if the index is not greather than the length of the list.
if test1[i] % 2 == 0 and i <= len(test1): #if the element meets certain conditions, it gets deleted
# Or use try, except (Will be slow if your list is great in size as there will be many exceptions caught.
if test1[i] % 2 == 0:
try:
del test1[i]
except:
continue
#The thing in test1 gets deleted, but the range gets not updated,
#and i iterates nonetheless, so 1 element gets ignored,
#and in the end, I get "list index out of range"

Modifying an iterable while you're iterating over it can get you into lots of trouble. Try the following instead:
test1 = [1,2,3]
test2 = []
for i in test1:
print(i)
if i%2:
test2.append(i)
Then you have test2 as the finished version.
This could be written more compactly with a comprehension:
test1 = [1,2,3]
test2 = [i for i in test1 if i%2]
print(*test2, sep='\n')

Related

Trouble Comparing Items in List

I have some code that inputs the current market price into a list, and then compares the last two values in that list (in theory). When the market goes up, it prints to the terminal market is up, when down, market is down. However, my code is always printing market is down (I have checked and the market is indeed up between scrapes). What am I doing wrong? I have a sneaking suspicion I don't understand list indexing.
Code:
tickers = ['^N225']
for ticker in tickers:
ticker_yahoo = yf.Ticker(ticker)
data = ticker_yahoo.history()
last_quote = data['Close'].iloc[-1]
L = float(last_quote)
M = trunc(L)
f = open('market.txt', 'a')
f.write(str(L))
f.write('\n')
f.write(str(L))
print(M)
list = []
list.append(M)
N = list[-1]
O = list[0]
if N > O:
print("Market is up")
else:
print("Market is down")
time.sleep(10)```
May be I'm missing something,
It looks like you are appending a float (M) to an empty list (list)
so list[0] and list[-1] are both M.
If M is a list (?)
using "append" insert a single element so you generate a list of list with a single list element
list = []
other = [1,2,3,4]
list.append(other) # [[1, 2, 3, 4]]
print(list[0]) # [1,2,3,4]
print(list[-1]) # [1,2,3,4]
# you should prefer
list = []
other = [1,2,3,4]
list.extend(other)
print(list[0]) # 1
print(list[-1]) # 4
# expected ?
print(list[-2]) # 3
simply why not making comparison on M items (if M is a list)

How can I write a for loop in a more elaborative and efficient and pythonic way

I want to write this for loop in a more elaborative and in a efficient way...is there a way to do that
for index in indices:
x = seq[lowerIndex:(index+1)] #lower index is set to 0 so it will take the first broken string and also the last string
digests.append(x) #it will add the pieces here in to a given list format
print("length: "+ str(len(x)) + " range: " + str(lowerIndex + 1) + "-" + str(index+1)) #This will print the length of each piece and how long each piece is.
print(x) #this will print the piece or fragments
lowerIndex = index + 1 # this refers to the piece after the first piece
You can try:
# new indices contains the beginning index of all segments
indices = [i+1 for i in indices]
indices.insert(0, 0)
for from, to in zip(indices, indices[1:]): # pair 2 consecutive indices
x = seq[from:to]
digest.append(x)
print(f'length: {to-from} range: {from+1} - {to}') # use string format
print(x)
For example:
>>> seq = 'abcdefghijk'
>>> indices = [0, 3, 5, 6, 9]
---------- (result)
length: 3 range: 1 - 3
abc
length: 2 range 4 - 5
de
length: 1 range 6 - 6
f
length: 3 range 7 - 9
ghi

Funny behaviour of my recursive function

t = 8
string = "1 2 3 4 3 3 2 1"
string.replace(" ","")
string2 = [x for x in string]
print string2
for n in range(t-1):
string2.remove(' ')
print string2
def remover(ca):
newca = []
print len(ca)
if len(ca) == 1:
return ca
else:
for i in ca:
newca.append(int(i) - int(min(ca)))
for x in newca:
if x == 0:
newca.remove(0)
print newca
return remover(newca)
print (remover(string2))
It's supposed to be a program that takes in a list of numbers, and for every number in the list it subtracts from it, the min(list). It works fine for the first few iterations but not towards the end. I've added print statements here and there to help out.
EDIT:
t = 8
string = "1 2 3 4 3 3 2 1"
string = string.replace(" ","")
string2 = [x for x in string]
print len(string2)
def remover(ca):
newca = []
if len(ca) == 1: return()
else:
for i in ca:
newca.append(int(i) - int(min(ca)))
while 0 in newca:
newca.remove(0)
print len(newca)
return remover(newca)
print (remover(string2))
for x in newca:
if x == 0:
newca.remove(0)
Iterating over a list and removing things from it at the same time can lead to strange and unexpected behvaior. Try using a while loop instead.
while 0 in newca:
newca.remove(0)
Or a list comprehension:
newca = [item for item in newca if item != 0]
Or create yet another temporary list:
newnewca = []
for x in newca:
if x != 0:
newnewca.append(x)
print newnewca
return remover(newnewca)
(Not a real answer, JFYI:)
Your program can be waaay shorter if you decompose it into proper parts.
def aboveMin(items):
min_value = min(items) # only calculate it once
return differenceWith(min_value, items)
def differenceWith(min_value, items):
result = []
for value in items:
result.append(value - min_value)
return result
The above pattern can, as usual, be replaced with a comprehension:
def differenceWith(min_value, items):
return [value - min_value for value in items]
Try it:
>>> print aboveMin([1, 2, 3, 4, 5])
[0, 1, 2, 3, 4]
Note how no item is ever removed, and that data are generally not mutated at all. This approach helps reason about programs a lot; try it.
So IF I've understood the description of what you expect,
I believe the script below would result in something closer to your goal.
Logic:
split will return an array composed of each "number" provided to raw_input, while even if you used the output of replace, you'd end up with a very long number (you took out the spaces that separated each number from one another), and your actual split of string splits it in single digits number, which does not match your described intent
you should test that each input provided is an integer
as you already do a print in your function, no need for it to return anything
avoid adding zeros to your new array, just test first
string = raw_input()
array = string.split()
intarray = []
for x in array:
try:
intarray.append(int(x))
except:
pass
def remover(arrayofint):
newarray = []
minimum = min(arrayofint)
for i in array:
if i > minimum:
newarray.append(i - minimum)
if len(newarray) > 0:
print newarray
remover(newarray)
remover(intarray)

How can I use string formatting to assign unique variable?

I've got a list and i've managed to turn the list into strings. Now I want to assign a variable to each item in the list by using string formatting to append a 1 onto the end of the variable.
listOne = ['33.325556', '59.8149016457', '51.1289412359']
itemsInListOne = int(len(listOne))
num = 4
varIncrement = 0
while itemsInListOne < num:
for i in listOne:
print a = ('%dfinalCoords{0}') % (varIncrement+1)
print (str(listOne).strip('[]'))
break
I get the following error: SyntaxError: invalid syntax
How can I fix this and assign a new variable in the format:
a0 = 33.325556
a1 = 59.8149016457 etc.
Your current code has a few issues:
listOne = ['33.325556', '59.8149016457', '51.1289412359']
itemsInListOne = int(len(listOne)) # len will always be an int
num = 4 # magic number - why 4?
varIncrement = 0
while itemsInListOne < num: # why test, given the break?
for i in listOne:
print a = ('%dfinalCoords{0}') % (varIncrement+1) # see below
print (str(listOne).strip('[]')) # prints list once for each item in list
break # why break on first iteration
One line in particular is giving you trouble:
print a = ('%dfinalCoords{0}') % (varIncrement+1)
This:
simultaneously tries to print and assign a = (hence the SyntaxError);
mixes two different types of string formatting ('%d' and '{0}'); and
never actually increments varIncrement, so you will always get '1finalCoords{0}' anyway.
I would suggest the following:
listOne = ['33.325556', '59.8149016457', '51.1289412359']
a = list(map(float, listOne)) # convert to actual floats
You can easily access or edit individual values by index, e.g.
# edit one value
a[0] = 33.34
# print all values
for coord in a:
print(coord)
# double every value
for index, coord in enumerate(a):
a[index] = coord * 2
Looking at your previous question, it seems that you probably want pairs of coordinates from two lists, which can also be done with a simple list of 2-tuples:
listOne = ['33.325556', '59.8149016457', '51.1289412359']
listTwo = ['2.5929778', '1.57945488999', '8.57262235411']
coord_pairs = zip(map(float, listOne), map(float, listTwo))
Which gives:
coord_pairs == [(33.325556, 2.5929778),
(59.8149016457, 1.57945488999),
(51.1289412359, 8.57262235411)]

formatting list to convert into string

Here is my question
count += 1
num = 0
num = num + 1
obs = obs_%d%(count)
mag = mag_%d%(count)
while num < 4:
obsforsim = obs + mag
mylist.append(obsforsim)
for index in mylist:
print index
The above code gives the following results
obs1 = mag1
obs2 = mag2
obs3 = mag3
and so on.
obsforrbd = parentV = {0},format(index)
cmds.dynExpression(nPartilce1,s = obsforrbd,c = 1)
However when i run the code above it only gives me
parentV = obs3 = mag3
not the whole list,it only gives me the last element of the list why is that..??
Thanks.
I'm having difficulty interpreting your question, so I'm just going to base this on the question title.
Let's say you have a list of items (they could be anything, numbers, strings, characters, etc)
myList = [1,2,3,4,"abcd"]
If you do something like:
for i in myList:
print(i)
you will get:
1
2
3
4
"abcd"
If you want to convert this to a string:
myString = ' '.join(myList)
should have:
print(myString)
>"1 2 3 4 abcd"
Now for some explanation:
' ' is a string in python, and strings have certain methods associated with them (functions that can be applied to strings). In this instance, we're calling the .join() method. This method takes a list as an argument, and extracts each element of the list, converts it to a string representation and 'joins' it based on ' ' as a separator. If you wanted a comma separated list representation, just replace ' ' with ','.
I think your indentations wrong ... it should be
while num < 4:
obsforsim = obs + mag
mylist.append(obsforsim)
for index in mylist:
but Im not sure if thats your problem or not
the reason it did not work before is
while num < 4:
obsforsim = obs + mag
#does all loops before here
mylist.append(obsforsim) #appends only last
The usual pythonic way to spit out a list of numbered items would be either the range function:
results = []
for item in range(1, 4):
results.append("obs%i = mag_%i" % (item, item))
> ['obs1 = mag_1', 'obs2 = mag_2', 'ob3= mag_3']
and so on (note in this example you have to pass in the item variable twice to get it to register twice.
If that's to be formatted into something like an expression you could use
'\n'.join(results)
as in the other example to create a single string with the obs = mag pairs on their own lines.
Finally, you can do all that in one line with a list comprehension.
'\n'.join([ "obs%i = mag_%i" % (item, item) for item in range (1, 4)])
As other people have pointed out, while loops are dangerous - its easier to use range

Categories