Confused by using too many for loops, if and else statements - python

I am developing a plugin for the GIS software, QGIS. I created a QTableWidget and wish to extract values from it:
Problem is, I use a lot of for loops and if else statements which, up until the last few lines, seems to work fine. I can't seem to follow the logic now as the line print constraint_name only prints off the last value "Example_2". I could take it out of its corresponding else statement and then it will print all values correctly but I need to set it inside a condition:
qTable = self.dockwidget.tableWidget # QTableWidget
example_group = root.findGroup('Main group') # Group containing sub groups
all_items = []
gis_map = QgsMapLayerRegistry.instance().mapLayersByName( "Map" )[0] # Layer map in QGIS
idx = gis_map.fieldNameIndex("Rank") # Get "Rank" attribute field from gis_map
for row in range(qTable.rowCount()):
for col in [0]: # For first column "Constraint name"
constraint_item = qTable.item(row, col)
constraint_name = str(constraint_item.text())
for col in [1]: # For second column "Rank"
item = qTable.item(row, col)
item_string = str(item.text())
all_items.append(item_string)
for group in example_group.children(): # Search for specific group
if group.name() == "Sub group":
if len(set(all_items)) == 1: # If all items are the same
# If "Rank" field exists in layer map
if idx == -1:
print 'success'
else:
print 'fail'
else:
if idx == -1:
print constraint_name
else:
print 'fail'
Is there a way to tidy this up and still get the correct results?

My sincere thanks to the commenters who directed me to a much more efficient solution, here is the working code which works (I'm sure it can be refined further):
qTable = self.dockwidget.tableWidget
example_group = root.findGroup('Main group')
all_items = []
gis_map = QgsMapLayerRegistry.instance().mapLayersByName( "Map" )[0]
idx = gis_map.fieldNameIndex("Rank")
for row in range(qTable.rowCount()):
constraint_item = qTable.item(row, 0)
constraint_name = str(constraint_item.text())
item = qTable.item(row, 1)
item_string = str(item.text())
all_items.append(item_string)
for group in example_group.children():
if group.name() == "Sub group":
if idx == -1:
if len(set(all_items)) == 1:
print 'success'
else:
print 'fail'
else:
print constraint_name

Related

zip or forloop two different number of lenth in python

Let me expalain what the real problem is.
i am having two strings :
str1="QKHE=K"
str2="Qkhek" #this string donot have '=' inthe same index 'k' is here.
i wanted to match the two strings and the desire result should be like
result="Qkhe=k"#case sensitive
i tried my function :
def matchCap(cap1=None,cap2=None):
if cap1==None or cap2==None:
return cap1
else:
cap1=list(cap1)
cap2=list(cap2)
dictionary = (zip(cap1, cap2))
print(list(dictionary))
final_cap=[]
for key, value in dictionary:
if key=='#' or key=='=':
final_cap.append(key)
else:
final_cap.append(value)
final_cap=''.join(final_cap)
return final_cap
But the zip function not workingwell because of different lengths of list.
so basically i am having two string ..amm like two captcha answers . Gave captcha example for the case sensitive .
i want the result captcha answer is Qkhe=k
One thing you can do is, when you encounter # or = in the string, you can append both the key and value, like this...
def matchCap(cap1=None,cap2=None):
if cap1==None or cap2==None:
return cap1
else:
cap1, cap2 = list(cap1), list(cap2)
dictionary = (zip(cap1, cap2))
final_cap = []
for key, value in dictionary:
if key in ('#', '='):
final_cap.append(key)
final_cap.append(value)
else:
final_cap.append(value)
final_cap = ''.join(final_cap)
return final_cap
print(matchCap("QKHE=K","Qkhek"))
Output:-
Qkhe=k
str1="QKHE=K"
str2="Qkhek"
def matchCap(cap1=None,cap2=None):
if cap1==None or cap2==None:
return cap1
else:
cap1=list(cap1)
cap2=list(cap2)
final_cap=[]
cap_2_i = 0
for cap_1 in range(len(cap1)):
key = cap1[cap_1]
value = cap2[cap_2_i]
if key=='#' or key=='=':
final_cap.append(key)
else:
final_cap.append(value)
cap_2_i += 1
final_cap=''.join(final_cap)
return final_cap
matchCap(str1, str2)
Result: 'Qkhe=k'
You don't need to use zip, just for iterates through it.
Define a variable i that represents the index position of the value from cap2, and i is accumulated each time a value is taken from cap2.
def matchCap(cap1=None, cap2=None):
if cap1 is None or cap2 is None:
return cap1
else:
total_len = len(cap2)
i = 0
final_cap = []
for index, value in enumerate(cap1):
if value == '#' or value == '=':
final_cap.append(value)
else:
final_cap.append(cap2[i])
i += 1
if i >= total_len:
break
final_cap = ''.join(final_cap)
return final_cap
print(matchCap("QKHE=K", "Qkhek"))
# Qkhe=k

How can I exclude something if a specific string is provided in the output?

So I wrote this code with the help of Stack Overflow users that transfers points to an individual based on whether "++" or "--" appears behind an individual.
def get_name(input):
return input.replace("+", "").replace("-", "")
def keep(actions: list):
g = {}
for s in actions:
if '->' in s:
names = s.split('->')
g[names[1]] = g[names[0]]
g[names[0]] = 0
else:
name = get_name(s)
if name not in g:
g[name] = 0
if "++" in s:
g[name] += 1
if "--" in s:
g[name] -= 1
return {x:g[x] for x in g if g[x] != 0}
print(keep(["Jim++", "John--", "Jeff++", "Jim++", "John--", "John->Jeff",
"Jeff--", "June++", "Home->House"]))
So most of the program is alright, however, when I put "Home->House" into it, it returns a KeyError. I kinda understand why it does that, but I'm clueless as to how to fix that...
I tried browsing the internet for solutions but all they recommended was to use .get(), which doesn't really help me solve the issue.
How can I make my output just neglect if like an element doesn't have "++" or "--" in it... like how can I make sure if an input is just "Jim" instead of "Jim++" or "Jim--", the function would just neglect it...
So in my example, if the input for keep is
["Jim++", "John--", "Jeff++", "Jim++", "John--", "John->Jeff", "Jeff--", "June++", "Home->House "]
the output would be
{'Jeff': -2, 'June': 1, 'Jim': 2}
instead of KeyError
You get KeyError because g[names[1]] = g[names[0]] is trying to access element in dictionary that isn't there. You get the same issue with simple print(keep(["John->Jeff"])), because there are no ++ or -- actions executed first to initialise those keys ("John" and "Jeff" in g
Based on your desired output you want to ignore such actions that are for non existing keys.
Add if names[1] in g and names[0] in g: into your keep implementation i.e.
Edit: also g[names[1]] = g[names[0]] needs to change to g[names[1]] += g[names[0]] to product correct outcome.
def get_name(input):
return input.replace("+", "").replace("-", "")
def keep(actions: list):
g = {}
for s in actions:
if "->" in s:
names = s.split("->")
if names[1] in g and names[0] in g:
g[names[1]] += g[names[0]]
g[names[0]] = 0
else:
name = get_name(s)
if name not in g:
g[name] = 0
if "++" in s:
g[name] += 1
if "--" in s:
g[name] -= 1
return {x: g[x] for x in g if g[x] != 0}
In your code, when you get Home->House, it's the first appearence of both the keys. That's why you get KeyError when you try to execute g[names[1] = g[names[0]]: g[names[0]] is g['Home'], but this entry doesn't exist in your dict. You can simply solve swapping the order of the lines:
if '->' in s:
names = s.split('->')
g[names[0]] = 0
g[names[1]] = g[names[0]]
To neglect strings which haven't the "++" or the "--", you can simply add an if before performing get_name(s):
else:
if '++' in s or '--' in s:
name = get_name(s)
if name not in g:
g[name] = 0
if "++" in s:
g[name] += 1
if "--" in s:
g[name] -= 1
This code returns the expected output
Your code actually does ignore examples which do not include "++" or "--" in the else-section of your function.
The KeyError occurs because neither "Home" nor "House" appear in the List of input strings before you try to move the entry of "Home" into the entry of "House". Because the keys "Home" and "House" are not in your dictionary g you get a KeyError.
Below is a solution which does what you want.
def get_name(input):
return input.replace("+", "").replace("-", "")
def keep(actions: list):
g = {}
for s in actions:
if '->' in s:
names = s.split('->')
if names[0] in g.keys() and names[1] in g.keys():
g[names[1]] = g[names[0]] + g[names[1]]
g[names[0]] = 0
else:
name = get_name(s)
if name not in g:
g[name] = 0
if "++" in s:
g[name] += 1
if "--" in s:
g[name] -= 1
return {x:g[x] for x in g if g[x] != 0}
print(keep(["Jim++", "John--", "Jeff++", "Jim++", "John", "John--", "John->Jeff",
"Jeff--", "June++", "Home->House"]))
I added a check to make sure the keys exist in dict d when the input string includes a "->".
In order to get the output you indicated in your question, which includes 'Jeff': -2, you also need to make sure to add the value of the original key to the value of the new key. This is done in line
g[names[1]] = g[names[0]] + g[names[1]]
Otherwise the output will say 'Jeff': -3 as the input string "Jeff++" will be ignored.

creating a list in python

I have an input list using which upon applying some if else logic while trying to save the output in a list. I'm when trying to check the type of it, found it to be "class list". I need to use this list and convert it to data frame in the next step so that i can have some terra data query written on top of it.
Also please note that upon writing the above piece of code in a single Jupiter window and when i go to the next Jupiter window and try query using the list index, i am always getting the last value in the list.
Need help in having the output set to a List, instead of Class List. also how to convert the list/Class list to a DataFrame?.
data = ['login', 'signup', 'account']
for i in range(len(data)):
source = []
if data[i] == 'login':
table = "sales.login_table"
elif data[i] == 'signup':
table = "sales.signup_table"
elif data[i] == 'account':
table = 'sales.account'
elif data[i] == 'addcc':
table = "sales.addcc"
elif data[i] == 'consolidatedfunding':
table = 'sales.consolidatedfunding'
elif data[i] == 'deposit':
table = 'sales.deposit'
elif data[i] == 'holdsassessment':
table = 'sales.holdsassessment'
elif data[i] == 'onboardinggc':
table = 'sales.onboardinggc'
source.append(table)
print(source)
print(source)
output:
['sales.login_table']
['sales.signup_table']
['sales.account']
print(type(source))
output :
<class 'list'>
You need to declare source = [] outside of for-loop, otherwise, in every iteration it'll be declare as empty list.
source = []
for i in range(len(data)):
if data[i] == 'login':
table = "sales.login_table"
elif data[i] == 'signup':
table = "sales.signup_table"
elif data[i] == 'account':
table = 'sales.account'
elif data[i] == 'addcc':
table = "sales.addcc"
elif data[i] == 'consolidatedfunding':
table = 'sales.consolidatedfunding'
elif data[i] == 'deposit':
table = 'sales.deposit'
elif data[i] == 'holdsassessment':
table = 'sales.holdsassessment'
elif data[i] == 'onboardinggc':
table = 'sales.onboardinggc'
source.append(table)
print(source)

Python: Failing to break out of while loop with "SyntaxError: 'break' outside loop"

I am developing a breadth-first-search algorithm for a factorization problem and am running into an interesting/confusing bug when attempting to break out of a while loop. If you run the code below, it will fail inside the "construct_path" method, stating :
File "main.py", line 96
break
SyntaxError: 'break' outside loop
but I am inside of a while loop! If anyone could give me some advice on this issue, I would really appreciate it. Thanks in advance.
from numpy import random
import itertools
import Queue
#Finding multiples, BFS problem
#Given input of list with unique integers 0 - 9 and n = range(0,1000000), calculate smallest multiple of n and unique combination of values in the list
#Example : Input : list = {0,1,2} , n = 3,
# output = 12
# Input : list = {0,1,2} , n = 50
# Output = 200
class Problem:
def __init__(self):
self.n = random.randint(0,10000000)
listSize = random.randint(1,9)
mainSet = set()
self.mainList = []
while True:
toAdd = random.randint(0,9)
if(toAdd not in self.mainList):
self.mainList.append(toAdd)
if(len(self.mainList) == listSize):
break
def get_start_state(self):
s = ''.join(map(str, self.mainList))
return int(s)
def is_goal(self, state):
return True
def get_sucessors(self):
print "Getting successors"
def breadth_first_search(problem):
# a FIFO open_set
open_set = Queue.Queue()
# an empty set to maintain visited nodes
closed_set = set()
# a dictionary to maintain meta information (used for path formation)
meta = dict() # key -> (parent state, action to reach child)
# initialize
start = problem.get_start_state()
meta[start] = (None, None)
open_set.put(start)
while not open_set.empty():
parent_state = open_set.get()
print "{} {}".format("parent_state is ", parent_state)
if problem.is_goal(parent_state):
return construct_path(parent_state, meta)
for (child_state, action) in problem.get_successors(parent_state):
if child_state in closed_set:
continue
if child_state not in open_set:
meta[child_state] = (parent_state, action)
open_set.put(child_state)
closed_set.add(parent_state)
#collect path to desired answer
def construct_path(state, meta):
action_list = list()
while True:
row = meta[state]
if (len(row) == 2):
state = row[0]
action = row[1]
action_list.append(action)
else:
break
return action_list.reverse()
x = Problem()
breadth_first_search(x)
Could be that you have a mix of tabs and spaces so that the break in line 96 looks like it is indented to be below action_list.append(action) but effectively it is below the while. That would explain the error at least.
It is just a guess. But it could be like this, using a visible tabwidth of 4 in the editor:
→ while True:
→ → row = meta[state]
if (len(row) == 2):
state = row[0]
action = row[1]
action_list.append(action)
else:
break
To the Python interpreter this looks like this (because it assumes a tabwidth of 8):
→ while True:
→ → row = meta[state]
if (len(row) == 2):
state = row[0]
action = row[1]
action_list.append(action)
else:
break
This is still valid but obviously means a different thing and would put your break outside of the while loop.

How to disable multiple line select Bokeh DataTable

How can I make the datatable widget support only single line selection? (disable multiple line select).
I came up with a hack/workaround using events
import functools
def table_select_callback(source, attrname, old, new):
# the lists of indicies are in random order, sort them
new = sorted(new)
old = sorted(old)
if len(new) == 0:
# they selected 0 elements, return
return
elif len(new) == 1:
# they selected 1 element, return
return
elif len(old) == 0:
# this shouldn't be possible but it's JS. #YOLO
source.selected.indices = [new[0]]
source.selected.indices = [new[0]] if old[0] != new[0] else [new[-1]]
# table_source is your instance of ColumnDataSource or equiv
table_source.selected.on_change(
"indices",
functools.partial(
table_select_callback, table_source
),
)

Categories

Resources