I apologize for the poor title. Wasn't sure exactly how to word my question.
I have code below which uses a list of tuples called propadd. The if statement tests the tuples for matching conditions. If the match matches to only 1 tuple from the list of tuples, it executes the exact same code as that in the if statement so as to assign this matching tuple to variable v in order to update the cursor rows with values from this matching tuple. I would like to know if there's a way to get rid of the assignment of the exact same code to v after the if statement. Is it possible to assign the list to v in the if statement while checking for the length of the matches? This is part of a larger amount of code that follows this methodology. I believe that doing this will make my code faster.
if len([item for item in propadd if item[0]==row1[8] and harversine(custx,custy,item[2],item[3])<1500]) == 1:
v=[item for item in propadd if item[0]==row1[8] and harversine(custx,custy,item[2],item[3])<1500]
row1[1]=v[0][1]
row1[2]=v[0][2]
elif len([item for item in custadd if item[0]==row1[4]]) == 1:
k=[item for item in custadd if item[0]==row1[4]]
row1[1]=k[0][1]
row1[2]=k[0][2]
elif len([item for item in numlist if re.search(r"^[0-9]+(?=\s)",row1[0]) is not None and item[0]==re.search(r"^[0-9]+(?=\s)",row1[0]).group()]) == 1
m=[item for item in numlist if re.search(r"^[0-9]+(?=\s)",row1[0]) is not None and item[0]==re.search(r"^[0-9]+(?=\s)",row1[0]).group()]
row1[1]=m[0][1]
row1[2]=m[0][2]
It will make your code slightly faster, and what is much more important, more readable and less error prone. Whether the list you create passes the test len(...) == 1 or not, it is computed. So why not just compute it once? Of course you will have to replace elif with else-if:
# Compute v
v = [item for item in propadd if item[0]==row1[8] and harversine(custx,custy,item[2],item[3])<1500]
if len(v) == 1:
row1[1]=v[0][1]
row1[2]=v[0][2]
else:
# If v fails, compute k
k = [item for item in custadd if item[0]==row1[4]]
if len(k) == 1:
row1[1]=k[0][1]
row1[2]=k[0][2]
else:
# If k fails, compute m
m = [item for item in numlist if re.search(r"^[0-9]+(?=\s)",row1[0]) is not None and item[0]==re.search(r"^[0-9]+(?=\s)",row1[0]).group()]
if len(m) == 1:
row1[1]=m[0][1]
row1[2]=m[0][2]
Coming from C, this is much more cumbersome than if(v = (....)) { } else .... However, there is another way to do this. You can use the fact that each expression in the list comprehensions is a generator:
v = (item for item in propadd if item[0]==row1[8] and harversine(custx,custy,item[2],item[3])<1500)
k = (item for item in custadd if item[0]==row1[4])
m = (item for item in numlist if re.search(r"^[0-9]+(?=\s)",row1[0]) is not None and item[0]==re.search(r"^[0-9]+(?=\s)",row1[0]).group())
for gen in (v, k, m):
l = list(gen)
if len(l) == 1:
row1[1] = l[0][1]
row1[2] = l[0][2]
break
In this case, the expressions v, k, m are generators, which are objects that are lazily evalutated iterables. They are not actually computing the list. You can go through each one and assign the one that matches when it is found, ignoring the others. The list is not computed until the statement l = list(gen). I think the second approach is much more Pythonic because it uses a single for loop no matter how many conditions you have, instead of a sequence of else statements marching off the page.
Related
I have a list of numbers and I want to check if a definite sequence exists in that list. For example, for [1,1,2,2,3,3,3,4,5,5,6] how can I determine if the sequence 3-4-5 exists?
I tried using nested loops, but ran into a problem with break/continue. I am now reducing the list to its unique elements and seeing if my sequence exists:
ls = [1,1,2,2,3,3,3,4,5,5,6]
uniq_ls = []
for item in ls:
if item not in uniql:
uniq_ls.append(item)
for ii, item in enumerate(uniq_ls):
if item == 3:
if uniq_ls[ii+1] == 4:
if uniq_ls[ii+2] == 5:
print('yes, sequence exists')
Above method works, but I want to do this without reducing the original list to its unique elements.
How can I do this?
As pointed out by some folks, this method will work only if the original list is sorted.
It will not work for a list like [1,1,1,3,2,2,4,3,3,4,4,4,4,5,3,2,1]
I need it to work for a unsorted list like this one.
You can use this function:
def f(lst, seq):
for n in range(len(lst)-len(seq)+1):
if lst[n:n+len(seq)] == seq:
return True
return False
print(f([1,1,2,2,3,3,3,4,5,5,6], [3,4,5]))
Output:
True
You can also turn that into a one-liner:
def f(lst, seq):
return any(lst[n:n+len(seq)] == seq for n in range(len(lst)-len(seq)+1))
I worked out a solution to this when the list is unsorted. I am not sure if this is the most elegant, pythonic way -
List L1 is [1,1,1,3,2,2,4,3,3,4,4,4,4,5,3,2,1]
I will have an empty list L2 = [] and add first item of list to it.
Then I will append L2 with item from L1 if previous item is not the same as current item
So L2 eventually should look like [1,3,2,4,3,4,5,3,2,1]
From this list it will be easy to find a sequence 3-4-5.
New code:
l1 = [1,3,2,4,3,4,5,3,2,1]
l_uni = []
for ii, item in enumerate(l1):
if ii==0:
l_uni.append(item)
elif l1[ii] != l1[ii-1]:
l_uni.append(item)
for jj in range(0,len(l_uni)):
try:
if l_uni[jj] == 3:
if l_uni[jj+1] == 4:
if l_uni[jj+2] == 5:
print('found sequence')
break
except IndexError:
print('no sequence')
def back_interleave(first, second):
if first == [] and second == []:
return []
elif first == []:
return second[::-1]
elif second == []:
return first[::-1]
else:
newlist = []
for i in range(len(first)-1, 0,-1):
newlist.append(first[i])
newlist.append(second[i])
for j in range(len(second)-len(first)-1,0,-1):
newlist.append(second[i])
return newlist
can anybody tells me what's wrong with my code towards this question.
I'm not exactly sure what's wrong with your code, but the second and third if-statements appear to use built-in list reversing functionality which the original problem forbids.
What I would do is determine the length of the longer list, then iterate through both lists backwards.
def back_interleave(first, second):
newlist = []
# You want to iterate through the length of the longer list
length = max(len(first), len(second))
for x in range(length):
# start appending elements from the back of the list
index = -1*(x+1)
if x < len(first):
newlist.append(first[index])
if x < len(second):
newlist.append(second[index])
return newlist
The problem in your code is when you use the range function, the stop value is exclusive i.e., the index 0 is becoming exclusive in your case. And also in the j loop the values at index i are being stored instead of the values at index j.
#CyanideTesla has given the code that works pretty well for your problem
For a list I want to recursively count the number of elements that are not v.
My code so far looks like:
def number_not(thelist, v):
"""Returns: number of elements in thelist that are NOT v.
Precondition: thelist is a list of ints
v is an int"""
total = 0
if thelist is []:
return total
elif thelist[0] is v:
print "is v"
total += 0
print total
return number_not(thelist[1:],v)
elif thelist[0] is not v:
print "is not v"
total += 1
print total
return number_not(thelist[1:],v)
return total
It will print the total for each of the individual numbers, but not the final total. For example, for list = [1,2,2,2,1], it will print:
is not v
1
is v
0
is v
0
is v
0
is not v
1
But then my code gets a traceback(list index out of range) error because it keeps going. How do I make it so that it recurses only for the length of the list and that it return the proper total, which for the example is 2
All the code is fine, just the termination condition you have added is not correct,
it should be if not thelist:
Change your code to check the empty list, if thelist is []: to the above.
While the other answers here solve the problem, I don't think they addressed the core issue. The problem is:
if thelist is []:
Because that conditional statement is not what you think it is. For example:
In [2]: [] is []
Out[2]: False
is tests for identity, not equality as you seem to be assuming in your code. It checks the id of an object which is a unique integer which is similar to a memory address in C. Take this example:
In [3]: id([])
Out[3]: 140402932955720
In [4]: id([])
Out[4]: 140402923983176
Each [] is a new object with a unique id, so your base condition would never be triggered. For comparison of list instances you should use == or len (possibly with isinstance if you need to make sure its a list).
As an aside... is is appropriate for comparing constants/builtins like:
1 is 1
type([]) is list
False is False
This is useful when checking something that may be False or an int. If the variable is 0 and you check if not var, False and 0 are both Falsey and would both evaluate to True so you would want to check if var is False and if var is 0 assuming they had different meaning in your code.
Your base case is that the list is empty. Then you want to return 0, right?
Your recursive case requires you to split up your list into the first element and the rest. Check the head of the list -- is it not equal to what you're searching for?
def counter(needle, haystack):
if not haystack:
return 0
# Splits the list into the first element and the remaining elements as a list.
head, *rest = haystack
if head != needle:
return counter(needle, rest) + 1
else:
return counter(needle, rest)
repl.it.
I am being tasked with designing a python function that returns the index of a given item inside a given list. It is called binary_sort(l, item) where l is a list(unsorted or sorted), and item is the item you're looking for the index of.
Here's what I have so far, but it can only handle sorted lists
def binary_search(l, item, issorted=False):
templist = list(l)
templist.sort()
if l == templist:
issorted = True
i = 0
j = len(l)-1
if item in l:
while i != j + 1:
m = (i + j)//2
if l[m] < item:
i = m + 1
else:
j = m - 1
if 0 <= i < len(l) and l[i] == item:
return(i)
else:
return(None)
How can I modify this so it will return the index of a value in an unsorted list if it is given an unsorted list and a value as parameters?
Binary Search (you probably misnamed it - the algorithm above is not called "Binary Sort") - requires ordered sequences to work.
It simply can't work on an unordered sequence, since is the ordering that allows it to throw away at least half of the items in each search step.
On the other hand, since you are allowed to use the list.sorted method, that seems to be the way to go: calling l.sort() will sort your target list before starting the search operations, and the algorithm will work.
In a side note, avoid in a program to call anything just l - it maybe a nice name for a list for someone with a background in Mathematics and used to do things on paper - but on the screen, l is hard to disinguish from 1 and makes for poor source code reading. Good names for this case could be sequence lst, or data. (list should be avoided as well, since it would override the Python built-in with the same name).
I have a list of lists. Using itertools, I am basically doing
for result in product([A,B],[C,D],[E,F,G]):
# test each result
and the result is the desired product, with each result containing one element from each of the lists. My code tests each of the results element-by-element, looking for the first (and best) 'good' one. There can be a very very large number to test.
Let's say I'm testing the first result 'ACE'. Let's say when I test the second element 'C' I find that 'ACE' is a bad result. There is no need to test 'ACF' or 'ACG'. I would want to skip from the failed ACE directly to trying ADE. Anyway to do this without just throwing the unwanted results on the floor?
If I was implementing this with nested for loops, I would be trying to manipulate the for loop indexes inside the loop and that would not be very nice ... but I do want to skip testing a lot of results. Can I skip ahead efficiently in itertools?
itertools is not the best way to go with the concern you have.
If you just have 3 sets to combine, just loop over and when you fail, break the loops. (If you code is complex, set a variable and break right outside.
for i1 in [A, B]:
for i2 in [C, D]:
for i3 in [E, F, G]:
if not test(i1, i2, i3):
break
However, if the number of sets that you have is variable, then use a recursive function (backtrack):
inp_sets = ([A,B],[C,D],[E,F,G])
max_col = len(inp_sets)
def generate(col_index, current_set):
if col_index == max_col:
if test(current_set):
return current_set
else:
return None
else:
found = False
for item in inp_sets[col_index]:
res = generate(col_index+1, current_set + [item]):
if res:
return res
elif (col_index == max_col - 1):
# Here we are skipping the rest of the checks for last column
# Change the condition if you want to skip for more columns
return None
result = generate(0, [])