dynamic format the string from tuples of tuple using python - python

In the below, Scenario 1 is working fine in both (Code 1 & Code 2). But Scenario 2 is not working in Code 1.
My requirement is Tuple should keep on repeating itself until it fills all the formatting string in the Query dynamically. Because where clauses are not constant for all the queries.
Scenario 1#
query = SELECT * FROM test.order where total_price in {}
Tuple:
finTup=((125, 125, 125, 125),)
SELECT * FROM test.order where total_price in (125, 125, 125, 125)
Scenario 2#
query = SELECT * FROM test.order WHERE order_id IN {} AND product_id IN {}
Tuple:
finTup=((101, 105, 106, 107), (2, 2, 2, 2))
Code 1:
frt = 'finTup[{}]'
half = ''
val = ''
i = 0
le = len(finTup)
for i in range(le):
print(i)
print(eval(frt.format(i)))
if i == le -1:
half = half + frt.format(i)
val = val + " " + frt.format(i)
else:
half = half + frt.format(i)+', '
val = val + " " + frt.format(i)+', '
temp2 = query.format(eval(val))
Code 2:
if le == 1:
query = query.format(finTup[0])
elif le == 2:
query = query.format(finTup[0], finTup[1])
elif le == 3:
query = query.format(finTup[0], finTup[1], finTup[2])
elif le == 4:
query = query.format(finTup[0], finTup[1], finTup[2], finTup[3])
Error:
temp2 = query.format(eval(val))
IndexError: tuple index out of range
Please help me to fix this.

TL;DR
Hello, you have this error because you are trying to provide a single argument to the format function which expects two parameters (since you have 2 {}).
Note: Avoid using the eval function... That's pretty not recommended, even less in the current situation that you are. Everything can be done without having to evaluate a str.
In what follows, I only run the code for Scenario #2, i.e.
query2 = """SELECT * FROM test.order WHERE order_id IN {} AND product_id IN {}"""
finTup = ((101, 105, 106, 107), (2, 2, 2, 2))
Step 1: Debugging
Let's imagine you update your code for debugging as follows:
frt = 'finTup[{}]'
half = ''
val = ''
i = 0 # btw, you don't need to initialize i here
le = len(finTup)
for i in range(le):
if i == le -1:
half = half + frt.format(i)
val = val + " " + frt.format(i)
else:
half = half + frt.format(i)+', '
val = val + " " + frt.format(i)+', '
print val
print eval(val)
temp2 = query.format(eval(val))
Your output should be:
vars[0], vars[1]
((101, 105, 106, 107), (2, 2, 2, 2))
So, imagine you write python code from the above output, what you would do is:
query.format(((101, 105, 106, 107), (2, 2, 2, 2)))
You are actually providing a single parameter of type tuple with a two elements. But what's important here is that you provide format with a single parameter. The format function will try to access the second parameter, which you don't provide. It breaks.
Step 2: Fixing the code
Have a look at this SO article. It shows you how to use the * operator. It basically transforms a list or tuple into a sequence of items. Perfect for functions parameters and so forth.
So, applied to your current case:
temp2 = query.format(*eval(val))
And the trick is done.
Step 3: Optimization
Now, let's trash the Code 1 and use what we've learn with Code 2. We need to unpack the tuple of tuple into parameters to feed in to format. So, why not just do:
# I renamed `finTup` to `vars` since I don't know what it means
def get_query(query, vars):
return query.format(*vars)
It basically combines the logic of Code 1 and Code 2 into a single line.

Related

I don't know why it's an empty list? max()

# Plot the highest score in history
def draw_best(background):
ip = 'redis-16784.c89.us-east-1-3.ec2.cloud.redislabs.com'
r = redis.Redis(host=ip, password=1206, port=16784, db=0, decode_responses = True)
scores = [eval(i) for i in list(r.hgetall('2048').values())]
best_scores = max(scores)
scoreSurf = BasicFont01.render('Top scoreļ¼š{}'.format(best_scores), True, (0, 0, 0))
scoreRect = scoreSurf.get_rect()
scoreRect.width = math.floor((rate - 0.15) / 2 * screen.get_width())
scoreRect.height = math.floor((1 - rate2) / 3 * 2 * screen.get_height())
scoreRect.topright = (math.floor(0.9 * screen.get_width()), math.floor(0.05 * screen.get_height()))
py.draw.rect(screen, background, [scoreRect.topleft[0], scoreRect.topleft[1], scoreRect.width, scoreRect.height], 0)
screen.blit(scoreSurf, scoreRect)
I think the problem is in these two lines:
scores = [eval(i) for i in list(r.hgetall('2048').values())]
best_scores = max(scores)
The error it showed me was:
ValueError: max() arg is an empty sequence
Obviously, it seems like list(r.hgetall('2048').values()) is a blank sequence/list/array.
Check if it is really empty by defining a variable with the value list(r.hgetall('2048').values()) and then print it out to check.
There is a default keyword that may be helpful. It will return a value if the list is empty. It works as follows:
my_list = []
result = max(my_list, default=None)
print(result) # It will print "None"
You already think that the problem exists in those two lines, then what better way to solve this is to check if it is really in those two lines!

How to call a function on list to return incremental bits range

I am trying to write a function that returns from and to bits in [from:to] format.
I am not quite sure how exactly it can be done (recursively?). The expected output is in incremental range of bits. Here is the piece of code to start with,
cntlist = [5,1,4,3,1]
def find_size(cnt):
if cnt>1:
a = "[%s:%s]" % (cnt-1, cnt-cnt)
left = cnt-1
right = cnt-cnt
if cnt==1:
a = "[%s]" % (cnt)
left = a
right = a
return a, left, right
newlist = list(map(find_size, cntlist))
print(newlist)
Output:
[('[4:0]', 4, 0), ('[1]', '[1]', '[1]'), ('[3:0]', 3, 0), ('[2:0]', 2, 0), ('[1]', '[1]', '[1]')]
Expected output:
['[4:0]', '[5]', '[9:6]', '[12:10]', '[13]']
Note: If size is 1 in cntlist, the range will have only one element which will be +1 to previous range's left number.
IIUC, a simple loop should work:
def bitrange(cntlst):
out = []
total = 0
for i in cntlst:
prev = total
total += i
if i == 1:
out.append(f'[{total-1}]')
else:
out.append(f'[{total-1}:{prev}]')
return out
bitrange([5,1,4,3,1])
output:
['[4:0]', '[5]', '[9:6]', '[12:10]', '[13]']

Using the result from a function in a new one (plus more)

I am trying to do the following:
1) calculate the amount of the same numbers in the data list. eg : there are three numbers between and including 10 and 20.
2) represent the value for each number range with the same number of '#'. eg: there are 3 numbers between 10 and 20 = ###.
Ideally ending in having the two values represented next to each other.
Unfortunately I really can't figure out step two and any help would really be appreciated.
My code is below:
def count_range_in_list(li, min, max):
ctr = 0
for x in li:
if min <= x <= max:
ctr += 1
return ctr
def amountOfHashes(count_range_in_list,ctr):
ctr = count_range_in_list()
if ctr == 1:
print ('#')
elif ctr == 2:
print ('##')
elif ctr == 3:
print ('###')
elif ctr == 4:
print ('####')
elif ctr == 5:
print ('#####')
elif ctr == 6:
print ('######')
elif ctr == 7:
print ('#######')
elif ctr == 8:
print ('########')
elif ctr == 9:
print ('#########')
elif ctr == 10:
print ('##########')
data = [90,30,13,67,85,87,50,45,51,72,64,69,59,17,22,23,44,25,16,67,85,87,50,45,51]
print(count_range_in_list(data, 0, 10),amountOfHashes)
print(count_range_in_list(data, 10, 20),amountOfHashes)
print(count_range_in_list(data, 20, 30),amountOfHashes)
print(count_range_in_list(data, 30, 40),amountOfHashes)
print(count_range_in_list(data, 40, 50),amountOfHashes)
print(count_range_in_list(data, 50, 60),amountOfHashes)
print(count_range_in_list(data, 60, 70),amountOfHashes)
print(count_range_in_list(data, 70, 80),amountOfHashes)
print(count_range_in_list(data, 80, 90),amountOfHashes)
print(count_range_in_list(data, 90, 100),amountOfHashes)
I'll start by clearing out some doubts you seem to have.
First, how to use the value of a function inside another one:
You don't need to pass the reference of a method to another here. What I mean is, in amountOfHashes(count_range_in_list,ctr) you can just drop count_range_in_list as a parameter, and just define it like amountOfHashes(ctr). Or better yet, use snake case in the method name instead of camel case, so you end up with amount_of_hashes(ctr). Even if you had to execute count_range_in_list inside amount_of_hashes, Python is smart enough to let you do that without having to pass the function reference, since both methods are inside the same file already.
And why do you only need ctr? Well, count_range_in_list already returns a counter, so that's all we need. One parameter, named ctr. In doing so, to "use the result from a function in a new one", we could:
def amount_of_hashes(ctr):
...
# now, passing the value of count_range_in_list in amount_of_hashes
amount_of_hashes(count_range_in_list(data, 10, 20))
You've figured out step 1) quite well already, so we can go to step 2) right away.
In Python it's good to think of iterative processes such as yours dynamically rather than in hard coded ways. That is, creating methods to check the same condition with a tiny difference between them, such as the ones in amountOfHashes, can be avoided in this fashion:
# Method name changed for preference. Use the name that best fits you
def counter_hashes(ctr):
# A '#' for each item in a range with the length of our counter
if ctr == 0:
return 'N/A'
return ''.join(['#' for each in range(ctr)])
But as noted by Roland Smith, you can take a string and multiply it by a number - that'll do exactly what you think: repeat the string multiple times.
>>> 3*'#'
###
So you don't even need my counter_hashes above, you can just ctr*'#' and that's it. But for consistency, I'll change counter_hashes with this new finding:
def counter_hashes(ctr):
# will still return 'N/A' when ctr = 0
return ctr*'#' or 'N/A'
For organization purposes, since you have a specific need (printing the hashes and the hash count) you may then want to format right what comes into print, you could make a specific method for the printing, that calls both counter_hashes and count_Range_in_list, and gives you a cleaner result afterwards:
def hash_range(data, min, max):
ctr = count_range_in_list(data, min, max)
hashes = counter_hashes(ctr)
print(f'{hashes} | {ctr} items in range')
The use and output of this would then become:
>>> data = [90,30,13,67,85,87,50,45,51,72,64,69,59,17,22,23,44,25,16,67,85,87,50,45,51]
>>> hash_range(data, 0, 10)
N/A | 0 items in range
>>> hash_range(data, 10, 20)
### | 3 items in range
>>> hash_range(data, 20, 30)
#### | 4 items in range
And so on. If you just want to print things right away, without the hash_range method above, it's simpler but more lengthy/repetitive if you want a oneliner:
>>> ctr = count_range_in_list(data, 10, 20)
>>> print(counter_hashes(ctr), ctr)
### 3
Why not just do it like this:
Python 3.x:
def amount_of_hashes(ctr):
while ctr > 0:
print('#', end = '')
ctr = ctr-1
Python 2.x:
def amount_of_hashes(ctr):
while ctr > 0:
print '#',
ctr = ctr-1
Counting the number in a list can be done like this:
def count_range_in_list(li, mini, maxi):
return len([i for i in li if mini <= i <= maxi])
Then making a number of hashes is even simpler. Just multiply a string containing the hash sign with a number.
print(ount_range_in_list(data, 0, 10)*'#')
Example in IPython:
In [1]: data = [90,30,13,67,85,87,50,45,51,72,64,69,59,17,22,23,44,25,16,67,85,87,50,45,51]
In [2]: def count_range_in_list(li, mini, maxi):
...: return len([i for i in li if mini <= i <= maxi])
...:
In [3]: print(count_range_in_list(data, 0, 10)*'#')
In [4]: print(count_range_in_list(data, 10, 20)*'#')
###
In [5]: print(count_range_in_list(data, 20, 30)*'#')
####
There are many ways to do this. One way is to use a for loop with range:
# Most basic
def count_range_in_list(li, min, max):
ctr = 0
hashes = ""
for x in li:
if min <= x <= max:
ctr += 1
hashes += "#"
print("There are {0} numbers = {1}".format(ctr, hashes))
# more declarative
def count_range_in_list(li, min, max):
nums = [x for x in li if min <= x <= max]
hashes = "".join(["#" for n in nums])
print("There are {0} numbers = {1}".format(len(nums), hashes))

Minimum number of steps to reach a given number

I need to calculate the minimum number of ways to reach a value, x, from value n, by adding/subtracting a list of values, l, to n.
For example: Value n = 100, value X = 45
List, l,: 50,6,1
The best way to do this is to say:
100-50-6+1 = 45
I want a programme to work this out for any value of x and n given list, l
I am really struggling to outline how I would write this.
I am confused about how to overcome the following issues:
How to inform the programme if I should attempt an addition or
subtraction and how many times this should be done. For example I
might need to subtract, then add, then subtract again to reach a
solution
How do I include enough for/while loops to ensure I can provide a
solution for all possible input values
Has anyone come across an issue like this before and have any ideas how I could outline the code for such a solution (I am using Python if it helps direct me towards learning about particular functions available that could assist me)
Thanks
This is my attempt so far but I am stuck
inputA = ""
while inputA == "":
inputA = input("""Please enter two numbers, separated by a comma.
The first value should indicate the number of jugs:
The second value should indicate the volume to be measured
""")
itemList = list(inputA.split(","))
valueToMeasure = int(itemList[1])
inputB = ""
while inputB == "":
inputB = input("Plese enter the volumes for the {} jug(s) listed: ".format((itemList[0])))
if len(inputB.split(",")) != int(itemList[0]):
inputB = ""
TargetVolume = itemList[1]
jugSizes = inputB.split(",")
print("Calculating: smallest number of steps to get", TargetVolume, "ml using jugs of sizes:", jugSizes)
jugSizes.sort()
jugSizes.reverse()
largestJug = int(jugSizes[0])
ratioTable = {}
for item in jugSizes:
firstVal = int(jugSizes[0])
itemV = int(item)
valueToAssign = firstVal/itemV
ratioTable[int(item)] = int(valueToAssign)
taskPossible = True
if valueToMeasure > largestJug:
print ("Impossible task")
taskPossible = False
newList = jugSizes
if taskPossible == True:
for item in jugSizes:
if item < TargetVolume: break
newList = newList[1:]
newDict = {}
for itemA in ratioTable:
if int(itemA) < int(item):
newDict[itemA]= ratioTable[itemA]
print ("Do work with these numbers:", newDict)
This is how I would approach the problem if I understand correctly.
X = 45
largest_jug = measured = 100
jug_sizes = [50, 6, 1]
steps = []
jug_to_use = 0
while measured != X:
if jug_to_use < len(jug_sizes) - 1: # we have smaller jugs in reserve
error_with_large_jug = min([abs(measured - jug_sizes[jug_to_use] - X), abs(measured + jug_sizes[jug_to_use] - X)])
error_with_small_jug = min([abs(measured - jug_sizes[jug_to_use + 1] - X), abs(measured + jug_sizes[jug_to_use + 1] - X)])
if error_with_small_jug < error_with_large_jug:
jug_to_use += 1
if measured > X:
measured -= jug_sizes[jug_to_use]
steps.append(('-', jug_sizes[jug_to_use]))
else:
measured += jug_sizes[jug_to_use]
steps.append(('+', jug_sizes[jug_to_use]))
print(steps)
Yielding
[('-', 50), ('-', 6), ('+', 1)]
It basically starts by using the largest jug, until it's in range of the next size and so on. We can test it with randomly sized jugs of [30, 7, 1] and see it again results in an accurate answer of [('-', 30), ('-', 30), ('+', 7), ('-', 1), ('-', 1)].
Important notes:
jug_sizes should be ordered largest to smallest
This solution assumes the X can be reached with the numbers provided in jug_sizes (otherwise it will infinitely loop)
This doesn't take into account that a jug size can make the target unreachable (i.e. [50, 12, 5] where the 12 size should be skipped, otherwise the solution is unreachable
This assumes every jug should be used (related to above point)
I'm sure you could figure out solutions for all these problems based on your specific circumstances though

Abreviations in a NFA, python

I'm trying to create a method were abreviations skips from one point to another.
I've created a NFA with the current edges
EDGES = [
(0, 'h', 1),
(1,'a',2),
(2,'z', 3),
(3,'a',4),
(4, 'r', 5),
(5, 'd', 6)
)]
Example of what I'm trying to accomplish
nrec("h-rd", nfa, 1) should return accept
nrec is the method that process the string for the NFA and checking wether it accepts or rejects.
def nrec(tape, nfa, trace=0):
"""Recognize in linear time similarly to transform NFA to DFA """
char = "-"
index = 0
states = [nfa.start]
while True:
if trace > 0: print " Tape:", tape[index:], " States:", states
if index == len(tape): # End of input reached
successtates = [s for s in states
if s in nfa.finals]
# If this is nonempty return True, otherwise False.
return len(successtates)> 0
elif len(states) == 0:
# Not reached end of string, but no states.
return False
elif char is tape[index]:
# the add on method to take in abreviations by sign: -
else:
# Calculate the new states.
states = set([e[2] for e in nfa.edges
if e[0] in states and
tape[index] == e[1]
])
# Move one step in the string
index += 1
I need to add a method that takes abreviations into the account. I'm not quite sure on how I can go skip from one state to another.
This is what is in the class NFA:
def __init__(self,start=None, finals=None, edges=None):
"""Read in an automaton from python shell"""
self.start = start
self.edges = edges
self.finals = finals
self.abrs = {}
I tought about using the abrs, but i constatly get's error when trying to define my own abrs such as
nfa = NFA(
start = 0,
finals = [6],
abrs = {0:4, 2:5},
edges=[
(0,'h', 1),
(1,'a', 2),
(2,'z', 3),
(3,'a', 4),
(4,'r', 5),
(5,'d', 6)
])
I recive error "TypeError: init() got an unexpected keyword argument 'abrs'"
Why I am reciveing that error?
for the modification i tought I'd do something like this
elif char is tape[index]:
#get the next char in tape tape[index+1] so
#for loop this.char with abrs states and then continue from that point.
smart choice or any better solutions?
The error is caused by __init__ not accepting the abrs keyword parameter as it is defined.
def __init__(self,start=None, finals=None, edges=None):
You'd need abrs=None (or another value) to make it a keyword argument or abrs to make it a required argument.

Categories