Python recursive string concatenation - python

I'm using a python class to generate a HTML table used in a calendar daily view. To generate the table I recursively call my function until it reaches the desired end. I looks like so:
# By default time=[0][0] and end_time=[0][24]
---------------------------------------------
def makeRows(self, time, end_time): # time/end_time = [min][hour]
row = ['<tr><td>']
if time[1] == end_time[1]: # Base case
# String format
if time[0] == 0:
row.append('%s:00' % time[1])
else:
row.append('%s:%s' % (time[1], time[0]))
row.append('</td></tr>')
return format_html(''.join(row))
else: # Recursive case
if time[0] == 0 and time[1] == 0: # First row
row.append('0:00')
row.append('</td><td rowspan="97"><div class="day_event_container"></div></td></tr>')
else:
# String format
if time[0] == 0:
row.append('%s:00' % time[1])
else:
row.append('%s:%s' % (time[1], time[0]))
row.append('</td></tr>')
return format_html(''.join(row)+self.makeRows(self.increaseTime(time), end_time))
def increaseTime(self, time):
hour = time[1]
minute = time[0]
if minute == 0:
minute+= 30
else:
hour += 1
minute = 0
return [minute, hour]
I recently changed all my string concatenations from += to ''.join() but the only ''naive'' concatenation is in my recursive call.
The function is usually called 48 times to generate rows from 0:00 -> 24:00
Is the ''naive'' concatenation in my recursive call really that costly for 48 function calls?
How would I replace my ''naive'' concatenation if it actually is very costly?
I tried doing
return format_html(''.join(row).join(self.makeRows(self.increaseTime(time), end_time)))
but that throws Exception Type: MemoryError and I'm guessing that it just overflows the heap or stack or wherever its stored.
Lastly, I'm quite new to python and I'm 99% sure that this is very far from the pythonic way of doing things. So is there a more pythonic way to generate a daily calendar view from 0:00 to 24:00?

Seems to me like you are trying to replace -
format_html(''.join(row)+self.makeRows(self.increaseTime(time), end_time))
With
format_html(''.join(row).join(self.makeRows(self.increaseTime(time), end_time)))
But that is not correct, both return different things. Lets take a very simple example to explain -
>>> ''.join('Bye').join('Hello')
'HByeeByelByelByeo'
>>> ''.join('Bye') + 'Hello'
'ByeHello'
I think you should keep your current usage of + concatenation operator instead of trying to make it perform better , unless you are working with a very large list of strings to be concatenated, which does not seem to be that case.

Related

sorting according to time sequence [duplicate]

I'm just starting out with Python and wondering how I would go about sorting this
list from the earliest time to the latest.
('5:00PM','2:00PM','7:00AM','8:45PM','12:00PM')
Any help is appreciated.
In python3 with standard library only:
import time
hours = ('5:00PM','2:00PM','7:00AM','8:45PM','12:00PM')
format = '%I:%M%p'
time_hours = [time.strptime(t, format) for t in hours]
result = [time.strftime(format, h) for h in sorted(time_hours)]
assert result == ['07:00AM', '12:00PM', '02:00PM', '05:00PM', '08:45PM']
I recommend that you install the PyPi DateTime package and use those facilities for whatever manipulation you desire. The problem at hand would look something like:
stamps = ('5:00PM','2:00PM','7:00AM','8:45PM','12:00PM')
DT_stamps = [DateTime(s) for s in stamps]
DT_stamps.sort()
Implementation details are left as an exercise for the student. :-)
If the times are always going to be in that format, you could split the times into subsections.
x = "12:30PM"
# Use python's string slicing to split on the last two characters
time, day_half = x[:-2], x[-2:]
# Use python's string.split() function to get the difference between hours and minutes
# Because "11" < "2" for strings, we need to convert them to integers
hour, minute = [int(t) for t in time.split(":")]
# Get the remainder because 12 should actually be 0
hour = hour % 12
# Output it as a tuple, which sorts based on each element from left to right
sortable = (day_half, hour, minute)
#: ("PM", 12, 30)
To wrap it all up, use something like:
def sortable_time(time_str):
time, day_half = time_str[:-2], time_str[-2:]
hour, minute = [int(t) for t in time.split(":")]
hour = hour % 12
return day_half, hour, minute
# When sorting, use `key` to define the method we're sorting with
# (The returned list however, will be filled with the original strings)
result = sorted(your_time_list, key=sortable_time)
#: ['7:00AM', '12:00PM', '2:00PM', '5:00PM', '8:45PM']
If you're not guaranteed to have the two letters at the end, or the colon in the middle, you're best off using a library like what is suggested by Prune.
What you're showing isn't a list of times, it's a tuple of strings. Tuples are immutable and can't be sorted, only mutable collections like lists can be. So first you need to convert your tuple to a list:
times = ['5:00PM','2:00PM','7:00AM','8:45PM','12:00PM']
You could try sorting this list now, but the strings won't sort the way you expect. Instead, you need to create a custom sort function that will temporarily convert the values in the list to struct_time objects and sort using those.
import time
time_format = '%I:%M%p' # match hours, minutes and AM/PM
def compare_as_time(time_str1, time_str2):
# parse time strings to time objects
time1 = time.strptime(time_str1, time_format)
time2 = time.strptime(time_str2, time_format)
# return comparison, sort expects -1, 1 or 0 to determine order
if time1 < time2:
return -1
elif time1 > time2:
return 1
else:
return 0
Now you can call sorted() and pass in your list and your custom comparison function and you'll get a list of strings back, sorted by the time in those strings:
sorted_times = sorted(times, compare_as_time)
Note for Python 3: The previous example assumes Python 2. If you're using Python 3, you'll need to convert the comparison function to a key function. This can be done using functools.cmp_to_key() as follows:
form functools import cmp_to_key
sorted_times = sorted(times, key=cmp_to_key(compare_as_time))

How to sort a list of times

I'm just starting out with Python and wondering how I would go about sorting this
list from the earliest time to the latest.
('5:00PM','2:00PM','7:00AM','8:45PM','12:00PM')
Any help is appreciated.
In python3 with standard library only:
import time
hours = ('5:00PM','2:00PM','7:00AM','8:45PM','12:00PM')
format = '%I:%M%p'
time_hours = [time.strptime(t, format) for t in hours]
result = [time.strftime(format, h) for h in sorted(time_hours)]
assert result == ['07:00AM', '12:00PM', '02:00PM', '05:00PM', '08:45PM']
I recommend that you install the PyPi DateTime package and use those facilities for whatever manipulation you desire. The problem at hand would look something like:
stamps = ('5:00PM','2:00PM','7:00AM','8:45PM','12:00PM')
DT_stamps = [DateTime(s) for s in stamps]
DT_stamps.sort()
Implementation details are left as an exercise for the student. :-)
If the times are always going to be in that format, you could split the times into subsections.
x = "12:30PM"
# Use python's string slicing to split on the last two characters
time, day_half = x[:-2], x[-2:]
# Use python's string.split() function to get the difference between hours and minutes
# Because "11" < "2" for strings, we need to convert them to integers
hour, minute = [int(t) for t in time.split(":")]
# Get the remainder because 12 should actually be 0
hour = hour % 12
# Output it as a tuple, which sorts based on each element from left to right
sortable = (day_half, hour, minute)
#: ("PM", 12, 30)
To wrap it all up, use something like:
def sortable_time(time_str):
time, day_half = time_str[:-2], time_str[-2:]
hour, minute = [int(t) for t in time.split(":")]
hour = hour % 12
return day_half, hour, minute
# When sorting, use `key` to define the method we're sorting with
# (The returned list however, will be filled with the original strings)
result = sorted(your_time_list, key=sortable_time)
#: ['7:00AM', '12:00PM', '2:00PM', '5:00PM', '8:45PM']
If you're not guaranteed to have the two letters at the end, or the colon in the middle, you're best off using a library like what is suggested by Prune.
What you're showing isn't a list of times, it's a tuple of strings. Tuples are immutable and can't be sorted, only mutable collections like lists can be. So first you need to convert your tuple to a list:
times = ['5:00PM','2:00PM','7:00AM','8:45PM','12:00PM']
You could try sorting this list now, but the strings won't sort the way you expect. Instead, you need to create a custom sort function that will temporarily convert the values in the list to struct_time objects and sort using those.
import time
time_format = '%I:%M%p' # match hours, minutes and AM/PM
def compare_as_time(time_str1, time_str2):
# parse time strings to time objects
time1 = time.strptime(time_str1, time_format)
time2 = time.strptime(time_str2, time_format)
# return comparison, sort expects -1, 1 or 0 to determine order
if time1 < time2:
return -1
elif time1 > time2:
return 1
else:
return 0
Now you can call sorted() and pass in your list and your custom comparison function and you'll get a list of strings back, sorted by the time in those strings:
sorted_times = sorted(times, compare_as_time)
Note for Python 3: The previous example assumes Python 2. If you're using Python 3, you'll need to convert the comparison function to a key function. This can be done using functools.cmp_to_key() as follows:
form functools import cmp_to_key
sorted_times = sorted(times, key=cmp_to_key(compare_as_time))

Create longestPossible(longest_possible in python) helper function that takes 1 integer argument which is a maximum length of a song in seconds

Am kind of new to coding,please help me out with this one with explanations:
songs is an array of objects which are formatted as follows:
{artist: 'Artist', title: 'Title String', playback: '04:30'}
You can expect playback value to be formatted exactly like above.
Output should be a title of the longest song from the database that matches the criteria of not being longer than specified time. If there's no songs matching criteria in the database, return false.
Either you could change playback, so that instead of a string, it's an integer (for instance, the length of the song in seconds) which you convert to a string for display, and test from there, or, during the test, you could take playback and convert it to its length in seconds, like so:
def songLength(playback):
seconds = playback.split(':')
lengthOfSong = int(seconds[0]) * 60 + int(seconds[1])
return lengthOfSong
This will give the following result:
>>> playback = '04:30'
>>> songLength(playback)
270
I'm not as familiar with the particular data structure you're using, but if you can iterate over these, you could do something like this:
def longestPossible(array, maxLength):
longest = 0
songName = ''
for song in array:
lenSong = songLength(song.playback) # I'm formatting song's playback like this because I'm not sure how you're going to be accessing it.
if maxLength >= lenSong and (maxLength - lenSong) < (maxLength - longest):
longest = lenSong
songName = song.title
if longest != 0:
return songName
else:
return '' # Empty strings will evaluate to False.
I haven't tested this, but I think this should at least get you on the right track. There are more Pythonic ways of doing this, so never stop improving your code. Good luck!

GET Request Flask

I have written something that works, but I am 100% sure that there is an even more efficient and faster way of doing what I did.
The code that I have written, essentially uses OpenBayes' library and creates a network with its nodes, relationships between nodes, and the probabilities and distributions associated with each of the nodes. Now, I was creating a GET request using Flask, in order to process the conditional probabilities by simply sending the request.
I will send some evidence (given values), and set the node in which I want its probability (observed value). Mathematically it looks like this:
Observed Value = O and Evidence = En, where n > 1
P( O | E1, E2, ..., En)
My final goal would be to have a client/server ping the server hosting this code(with the right parameters) and constantly give me the final values of the observed probability, given the evidence (which could be 1 or more values). The code I have written so far for the GET request portion is:
#app.route('/evidence/evidence=<evidence>&observed=<obv>', methods=['GET'])
def get_evidence(evidence, obv):
# Take <evidence> and <obv> split them up. For example:
# 'cloudy1rain0sprinkler1' to 'cloudy1', 'rain0' and 'sprinkler1', all in a nice list.
analyzeEvidence, observedNode = evidence.upper().strip(), obv.upper().strip()
string, count, newCount, listOfEvidence = "", 0, 0, {}
counter = sum(character.isdigit() for character in analyzeEvidence)
# This portion is to set up all the evidences.
for y in xrange(0, counter):
string, newCount = "", count
for x in xrange(newCount, len(analyzeEvidence)):
count += 1
if analyzeEvidence[x].isalpha() == True:
string += str(analyzeEvidence[x])
elif analyzeEvidence[x].isdigit() == True and string in allNodes:
if int(analyzeEvidence[x]) == 1 or int(analyzeEvidence[x]) == 0:
listOfEvidence[string] = int(analyzeEvidence[x])
break
else: abort(400)
break
else: abort(400)
net.SetObs(listOfEvidence) # This would set the evidence like this: {"CLOUDY": 1, "RAIN":0}
# This portion is to set up one single observed value
string = ""
for x in xrange(0, len(observedNode)):
if observedNode[x].isalpha() == True:
string += str(observedNode[x])
if string == "WETGRASS":
string = "WET GRASS"
elif observedNode[x].isdigit() == True and string in allNodes:
if int(observedNode[x]) == 1 or int(observedNode[x]) == 0:
observedValue = int(observedNode[x])
observedNode = string
break
else: abort(400)
else: abort(400)
return str(net.Marginalise(observedNode)[observedValue]) # Output returned is the value like: 0.7452
Given my code, is there any way to optimize it? Also, Is there a better way of passing these parameters that doesn't take so many lines like my code does? I was planning on setting fixed key parameters, but because my number of evidence can change per request, I thought this would be one way in doing so.
You can easily split your evidence input into a list of strings with this:
import re
# 'cloudy1rain0sprinkler1' => ['cloudy1', 'rain0' and 'sprinkler1'].
evidence_dict = {}
input_evidence = 'cloudy1rain0sprinkler1'
# looks for a sequence of alphabets followed by any number of digits
evidence_list = re.findall('([a-z]+\d+)', input_evidence.lower())
for evidence in evidence_list:
name, val, _ = re.split('(\d+)', evidence)
if name in allNodes:
evidence_dict[name] = val
# evidence_dict = {'cloudy': 1, 'rain': 0, 'sprinkler': 1}
You should be able to do something similar with the observations.
I would suggest you use an HTTP POST. That way you can send a JSON object which will already have the separation of variable names and values done for you, all you'll have to do is check that the variable names sent are valid in allNodes. It will also allow your variable list to grow somewhat arbitrarily.

Making string series in Python

I have a problem in Python I simply can't wrap my head around, even though it's fairly simple (I think).
I'm trying to make "string series". I don't really know what it's called, but it goes like this:
I want a function that makes strings that run in series, so that every time the functions get called it "counts" up once.
I have a list with "a-z0-9._-" (a to z, 0 to 9, dot, underscore, dash). And the first string I should receive from my method is aaaa, next time I call it, it should return aaab, next time aaac etc. until I reach ----
Also the length of the string is fixed for the script, but should be fairly easy to change.
(Before you look at my code, I would like to apologize if my code doesn't adhere to conventions; I started coding Python some days ago so I'm still a noob).
What I've got:
Generating my list of available characters
chars = []
for i in range(26):
chars.append(str(chr(i + 97)))
for i in range(10):
chars.append(str(i))
chars.append('.')
chars.append('_')
chars.append('-')
Getting the next string in the sequence
iterationCount = 0
nameLen = 3
charCounter = 1
def getString():
global charCounter, iterationCount
name = ''
for i in range(nameLen):
name += chars[((charCounter + (iterationCount % (nameLen - i) )) % len(chars))]
charCounter += 1
iterationCount += 1
return name
And it's the getString() function that needs to be fixed, specifically the way name gets build.
I have this feeling that it's possible by using the right "modulu hack" in the index, but I can't make it work as intended!
What you try to do can be done very easily using generators and itertools.product:
import itertools
def getString(length=4, characters='abcdefghijklmnopqrstuvwxyz0123456789._-'):
for s in itertools.product(characters, repeat=length):
yield ''.join(s)
for s in getString():
print(s)
aaaa
aaab
aaac
aaad
aaae
aaaf
...

Categories