Removing things from Python list during for loop - python

Here is my code:
toBe =[]
#Check if there is any get request
if request.GET.items() != []:
for x in DB:
try:
#This is removing the PMT that is spcific to the name
if request.GET['pmtName'] != "None":
if not request.GET['pmtName'] in x['tags']:
print x['title'], x['tags']
toBe.append(x)
continue
#This is removing the peak stuff
if int(request.GET['peakMin']):
if int(request.GET['peakMin']) < int(x['charge_peak']):
toBe.append(x)
continue
if int(request.GET['peakMax']):
if int(request.GET['peakMax']) > int(x['charge_peak']):
toBe.append(x)
continue
if int(request.GET['widthMin']):
if int(request.GET['widthMin']) < int(x['charge_width']):
toBe.append(x)
continue
if int(request.GET['widthMax']):
if int(request.GET['widthMax']) > int(x['charge_width']):
toBe.append(x)
continue
except:
pass
#TODO: Stupid hack, this needs to be fixed
for x in toBe:
DB.remove(x)
del toBe
Essentially I want to remove the item and then skip to the next one. The problem with this is that when that happens, it messes up the order of the list and skips some. Anyone know a work around for this? Or maybe just a different way of doing this?
thanks

for x in DB[:]: makes a copy of the list DB, so you can iterate over it while modifying the original. Care -- memory-intensive and slow.
A nicer way would be to make another layer over the list which yields only some of the values, and then iterate over that when you need it later. You can do that with a generator:
def db_view( DB ):
for x in DB:
#This is removing the PMT that is spcific to the name
if request.GET.get( 'pmtName', None ) not in x['tags']:
print x['title'], x['tags']
continue
#This is removing the peak stuff
if int(request.GET['peakMin']):
if int(request.GET['peakMin']) < int(x['charge_peak']):
continue
if int(request.GET['peakMax']):
if int(request.GET['peakMax']) > int(x['charge_peak']):
continue
if int(request.GET['widthMin']):
if int(request.GET['widthMin']) < int(x['charge_width']):
continue
if int(request.GET['widthMax']):
if int(request.GET['widthMax']) > int(x['charge_width']):
continue
yield x
which you would use like
for x in db_view( DB ):
# Do stuff

The answer I usually see for questions of this sort if to loop over the list backwards. Here's how I do it in one of my programs:
for i in range(len(my_list)-1,-1,-1):
# do something
This works even if I add items to the list. On http://desk.stinkpot.org:8080/tricks/index.php/2006/08/read-a-list-backwards-in-python/ they say you can use the syntax "for i in list[::-1]:" instead. I have not tried doing it that way.

You're running over the same interpolation of request.GET for each value of x. Instead you could build a reusable list of filtering functions once.
For example something like:
if request.GET:
filters = []
if 'pmtName' in request.GET:
n = request.GET['pmtName']
filters.append(lambda x: n not in x['tags'])
if 'peakMin' in request.GET and request.GET['peakMin'].isdigit():
n = int(request.GET['peakMin'])
filters.append(lambda x: n < int(x['charge_peak']))
if 'peakMax' in request.GET and request.GET['peakMax'].isdigit():
n = int(request.GET['peakMax'])
filters.append(lambda x: n > int(x['charge_peak']))
if 'widthMin' in request.GET and request.GET['widthMin'].isdigit():
n = int(request.GET['widthMin'])
filters.append(lambda x: n < int(x['charge_width']))
if 'widthMax' in request.GET and request.GET['widthMax'].isdigit():
n = int(request.GET['widthMax'])
filters.append(lambda x: n > int(x['charge_width']))
Then you can apply this list of functions to select the members of DB to remove:
remove_these = [ x for x in DB if any(f(x) for f in filters)]
for item in remove_these:
DB.remove(item)
Or create a generator that will return the values of DB that all of the filters fail on:
filtered_DB = ( x for x in DB if all(not f(x) for f in filters))

Related

Python function/lists

how do i create a function that adds something to an empty list, but if the value already exists on that list it cant be added, and has to go to another empty list. I tried this but this doesn't takes into account if a value other than x is already on the list
List = []
leftover = []
x = 2
def add(x):
if myUniqueList[0:] == []:
myUniqueList.append(x)
print("True")
print(myUniqueList)
elif myUniqueList[0:] == [x]:
leftover.append(x)
print("False")
print(leftover)
add(x)
This may work. By using the if x not in syntax, we can easily check to see if the number is in the existing list.
If it is not in the list, we then append it. If it is in the list, we append it to the leftover list.
mylist = []
leftover = []
x = 2
def add(x):
if x not in mylist:
mylist.append(x)
print('True')
print(mylist)
else:
leftover.append(x)
print('False')
print(leftover)
add(x)
Set vs List:
There are some performance ramifications if you are using massive sequences of numbers. In which case, storing the numbers in a Python set() will greatly speed up any lookups to see if the number already exists. This is due to the way that Python stores numbers in sets vs lists.
myset = set()
leftover = []
x = 2
def add(x):
if x not in mylist:
mylist.add(x) # sets use .add() instead of .append()
print('True')
print(myset)
print(list(myset)) # IF you need to print out a list, you can
# convert sets to lists by encapsulating
# the myset with the list() factory function
# BUT if you do this every cycle you prolly
# start to lose the performance benefits.
else:
leftover.append(x)
print('False')
print(leftover)
add(x)
List = []
leftover = []
x = 2
def add(x):
if x in myUniqueList:
print("False")
print(leftover)
leftover.append(x)
else:
myUniqueList.append(x)
print("True")
print(myUniqueList)
add(x)

Recursively Generating a List of n choose k combinations in Python - BUT return a list

I'm attempting to generate all n choose k combinations of a list (not checking for uniqueness) recursively by following the strategy of either include or not include an element for each recursive call. I can definitely print out the combinations but I for the life of me cannot figure out how to return the correct list in Python. Here are some attempts below:
class getCombinationsClass:
def __init__(self,array,k):
#initialize empty array
self.new_array = []
for i in xrange(k):
self.new_array.append(0)
self.final = []
self.combinationUtil(array,0,self.new_array,0,k)
def combinationUtil(self,array,array_index,current_combo, current_combo_index,k):
if current_combo_index == k:
self.final.append(current_combo)
return
if array_index >= len(array):
return
current_combo[current_combo_index] = array[array_index]
#if current item included
self.combinationUtil(array,array_index+1,current_combo,current_combo_index+1,k)
#if current item not included
self.combinationUtil(array,array_index+1,current_combo,current_combo_index,k)
In the above example I tried to append the result to an external list which didn't seem to work. I also tried implementing this by recursively constructing a list which is finally returned:
def getCombinations(array,k):
#initialize empty array
new_array = []
for i in xrange(k):
new_array.append(0)
return getCombinationsUtil(array,0,new_array,0,k)
def getCombinationsUtil(array,array_index,current_combo, current_combo_index,k):
if current_combo_index == k:
return [current_combo]
if array_index >= len(array):
return []
current_combo[current_combo_index] = array[array_index]
#if current item included & not included
return getCombinationsUtil(array,array_index+1,current_combo,current_combo_index+1,k) + getCombinationsUtil(array,array_index+1,current_combo,current_combo_index,k)
When I tested this out for the list [1,2,3] and k = 2, for both implementations, I kept getting back the result [[3,3],[3,3],[3,3]]. However, if I actually print out the 'current_combo' variable within the inner (current_combo_index == k) if statement, the correct combinations print out. What gives? I am misunderstanding something to do with variable scope or Python lists?
The second method goes wrong because the line
return [current_combo]
returns a reference to current_combo. At the end of the program, all the combinations returned are references to the same current_combo.
You can fix this by making a copy of the current_combo by changing the line to:
return [current_combo[:]]
The first method fails for the same reason, you need to change:
self.final.append(current_combo)
to
self.final.append(current_combo[:])
Check this out: itertools.combinations. You can take a look at the implementation as well.

Python tuple not populating

I must be missing something here when I try to populate a tuple in a for loop.
...more code above...
colItems = objSWbemServices.ExecQuery(queryString)
#print type(colItems)
Is the above line needed?
# print the results
for item in colItems:
logTuple = (item.SourceName, item.Type, item.TimeGenerated, item.Message)
logTuple.sort(sortByTime)
return logTuple
Would the above code enter those fields into a tuple?
Below is the code to sort, I haven't been able to test it yet though.
def sortByTime(t1, t2):
if t1[2] < t2[2]:
return -1
elif t1[2] > t2[2]:
return 1
else:
return 0
Thanks for the help.
I'm not familiar with ExecQuery or the structures you're using, but I do know that in your for loop you're rewriting logTuple each time the body of the loop is executed. This should do the trick:
logTuples = []
for item in colItems:
logTuples.append( (item.SourceName, item.Type, item.TimeGenerated, item.Message) )
logTuples.sort(key=operator.itemgetter(2)) #credit: Thomas Jung
return logTuples
What you probably meant was to add the tuple to the list (and not to set logTuple to the last created tuple in the for loop):
for item in colItems:
log = (item.SourceName, item.Type, item.TimeGenerated, item.Message)
logTuple.append(log)
The sorting can be done with:
logTuples.sort(key=operator.itemgetter(2))

Instead of continue, rerun function

I'm wondering how to do the following in Python.
If I have a function with a for loop, it is possible to with an if statement to skip certain numbers.
This is an implementation of fisher-yates d got from activestate.com.
import random
def shuffle(ary):
a=len(ary)
b=a-1
for d in range(b,0,-1):
e=random.randint(0,d)
if e == d:
continue
ary[d],ary[e]=ary[e],ary[d]
return ary
Now continue simply goes to the next value for d. How can I, instead of doing continue, rerun the function with the original parameter ary?
Note that the function is just some example code, I'm curious on how to do this in general.
Also, maintaining a copy of the array might not be possible if the list is big, so thats not really a solution imo.
This is a common recursive pattern. However, your case is a little different than usual because here you need to make a copy of your input list to use when you recurse if the shuffling fails.:
import random
def shuffle(ary):
initial = ary[:]
a=len(ary)
b=a-1
for d in range(b,0,-1):
e=random.randint(0,d)
if e == d:
return shuffle(initial)
ary[d],ary[e]=ary[e],ary[d]
return ary
ary = [1,2,3,4,5,6]
print shuffle(ary)
Also note that Wikipedia gives a (non-recursive) python implementation of the very similar Sattolo's algorithm.
from random import randrange
def sattoloCycle(items):
i = len(items)
while i > 1:
i = i - 1
j = randrange(i) # 0 <= j <= i-1
items[j], items[i] = items[i], items[j]
return
If I read the article correctly, to re-acquire Fisher-Yates, you'd just do one simple change:
from random import randrange
def FisherYates(items):
i = len(items)
while i > 1:
i = i - 1
j = randrange(i+1) # 0 <= j <= i
items[j], items[i] = items[i], items[j]
return
def function(list):
len(list)-1
for i in range(len(list)-1,0,-1):
e= randint(0,i)
while e > i:
e= randint(0,i)
"do something to the list"
return array
?
def function(list):
for i in (a for a in range(len(list)-1,0,-1) if randint(0,a) > a):
#do something with list
#do something else with remainder.
Not exactly what you asked for. Just wanted to remind you of this possibility.
you can copy the parameter to a temp variable. then call the function with the temp variable and use return;
def function(list):
listCopy = list;
len(list)-1
for i in range(len(list)-1,0,-1):
e= randint(0,i)
if e > i:
return function(listCopy)
else
"do something with the list"
return array

Python: is there a C-like for loop available?

Can I do something like this in Python?
for (i = 0; i < 10; i++):
if someCondition:
i+=1
print i
I need to be able to skip some values based on a condition
EDIT: All the solutions so far suggest pruning the initial range in one way or another, based on an already known condition. This is not useful for me, so let me explain what I want to do.
I want to manually (i.e. no getopt) parse some cmd line args, where each 'keyword' has a certain number of parameters, something like this:
for i in range(0,len(argv)):
arg = argv[i]
if arg == '--flag1':
opt1 = argv[i+1]
i+=1
continue
if arg == '--anotherFlag':
optX = argv[i+1]
optY = argv[i+2]
optZ = argv[i+3]
i+=3
continue
...
Yes, this is how I would do it
>>> for i in xrange(0, 10):
... if i == 4:
... continue
... print i,
...
0 1 2 3 5 6 7 8 9
EDIT
Based on the update to your original question... I would suggest you take a look at optparse
for (i = 0; i < 10; i++)
if someCondition:
i+=1
print i
In python would be written as
i = 0
while i < 10
if someCondition
i += 1
print i
i += 1
there you go, that is how to write a c for loop in python.
There are two things you could do to solve your problem:
require comma-separated arguments which are going to be grouped into the following option value, you could use getopt, or any other module then.
or do more fragile own processing:
sys.argv.pop()
cmd = {}
while sys.argv:
arg = sys.argv.pop(0)
if arg == '--arg1':
cmd[arg] = sys.argv.pop(0), sys.argv.pop(0)
elif:
pass
print(cmd)
Strange way:
for x in (x for x in xrange(10) if someCondition):
print str(x)
You should use continue to skip a value, in both C and Python.
for i in range(10):
if someCondition:
continue
print(i)
You probably don't actually need the indices, you probably need the actual items. A better solution would probably be like this:
sequence = 'whatever'
for item in sequence:
if some_condition:
continue
do_stuff_with(item)
You could first turn the argv list into a generator:
def g(my_list):
for item in my_list:
yield item
You could then step through the items, invoking the generator as required:
my_gen = g(sys.argv[1:]):
while True:
try:
arg = my_gen.next()
if arg == "--flag1":
optX = my_gen.next()
opyY = my_gen.next()
--do something
elif arg == "--flag2":
optX = my_gen.next()
optY = my_gen.next()
optZ = my_gen.next()
--do something else
...
except StopIteration:
break
You can ensure that an index is incremented within a try...finally block. This solve the common problem of wanting to continue to the next index without having to copy/past i += 1 everywhere. Which is one of the main advantages the C-like for loop offers.
The main disadvantage to using a try...finally is having to indent your code once more. but if you have a while loop with many continue conditions its probably worth it.
Example
This example demonstrates that i still gets incremented in the finally block, even with continue being called. If i is not incremented its value will remain even forever, and the while loop will become infinite.
i = 0
while i < 10:
try:
print(i)
if i % 2 == 0:
continue
finally:
i += 1
without it you would have to increment i just before calling continue.
i = 0
while i < 10:
print(i)
if i % 2 == 0:
i += 1 # duplicate code
continue
i += 1
for i in xrange(0, 10):
if i % 3 == 0
continue
print i
Will only values which aren't divisible by 3.
If you need to iterate over something, and need an index, use enumerate()
for i, arg in enumerate(argv):
...
which does the same as the questioner's
for i in range(0,len(argv)):
arg = argv[i]
Your problem seems to be that you should loop not raw parameters but parsed parameters. I would suggest you to consider to change your decision not to use standard module (like the others).
increament = 4 #say
for i in range(n):
#write your code here
n=n+increment
this might be the simple solution to the problem if you just want to iterate through the array by skipping 4 members

Categories