How to sort a list of times - python

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))

Related

Convert Time data type python

I am trying to transform a time variable that uses strings and integers in one column into a more friendly format. For example my time variable looks like: 30m 32s
I want a variable in format 30:32
or a variable with number of seconds. Thanks.
I'm not sure I fully understood the question, but the following will split your string on the space then separate out the individual minutes / seconds.
def formatTime(time: str):
minutes, seconds = time.split(' ') # Split on the space character
minutes_val = minutes.split('m')[0] # Separate the minutes value
seconds_val = seconds.split('s')[0] # Separate the seconds value
return f"{minutes_val}:{seconds_val}" # Return the formatted string
UPDATE: as Pranav said in his comment, this won't work for minutes or seconds > 59. So, If you have this limitation, you can use this solution
You can do this using datetime module using the following code
from datetime import datetime
time_str = "30m 32s"
time_obj = datetime.strptime(time_str, "%Mm %Ss")
time_in_new_format = time_obj.strftime("%M:%S") # Equals 30:32
Now let's explain it line by line.
In the following line, we create a datetime object from the input string and tell the format is "%Mm %Ss" as %M means minutes and %S means seconds.
time_obj = datetime.strptime(time_str, "%Mm %Ss")
In the following line, we change the datetime object back to a string in the format "%M:%S".
time_in_new_format = time_obj.strftime("%M:%S")
You can check all formats here https://docs.python.org/3/library/time.html#time.strftime

How to get a set of all time between range of start and end time?

I want to get all the time between start time and end time in python, so I was using for loop with range function.
There are 2 variables, a and b which have time in %H:%M:%S format.
These 2 variables are start and end time and I want to print all the time between the start and end time.
import datetime
from datetime import datetime
import time
a = '20:15:16'
b = '20:32:55'
a = datetime.strptime(a,'%H:%M:%S').time()
b = datetime.strptime(b,'%H:%M:%S').time()
for i in range(a,b):
print(i)
For this I am getting an error - datetime.time' object cannot be interpreted as an integer.
I want to print all the time between a and b.
There are infinite moments between two times. I think you might be asking, "How can I print a timestamp for every second or every minute between A and B?"
I don't think you want to be using the range function. The error you are seeing is because range expects integers as input, not whole datetime objects.
Here is something that may do what you want:
from datetime import datetime, timedelta
# Define our times
str_A = '20:15:16'
str_B = '20:32:55'
# Create our datetime objects
A = datetime.strptime(str_A,'%H:%M:%S')
B = datetime.strptime(str_B,'%H:%M:%S')
# Create a loop where you print a time, starting with time A
# and then increase the time stamp by some value, in this case,
# 1 minute, until you reach time B
tmp = a
while tmp <= b:
print(tmp.time())
tmp = tmp + timedelta(minutes=1)
Please notice the line,
print(tmp.time())
where we only extract the time part when we need it, leaving the object as a datetime object for easy manipulation.
I used this question for reference:
What is the standard way to add N seconds to datetime.time in Python?
So this question is really adorable. There is something about reading, 'I need to print "all the time" between these two times' that gives me joy.

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))

Python recursive string concatenation

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.

Creating and naming files with a while function

i'm trying to create files, one for each day of the year, and I was thinking I would use while or for for this. but it doesn't seem to work since I'm mixing numbers and letters.
def CreateFile():
date = 101
#this is supposed to be 0101 (first of januar, but since i can't start with a 0 this had to be the other option)
while date <= 131:
name = (date)+'.txt'
date += 1
CreateFile()
You can't concatenate strings and integers:
name = date + '.txt' # TypeError
but you can use str.format to create the filename:
name = "{0}.txt".format(date)
Using str.format also allows you to force four digits, including the leading zero:
>>> "{0:04d}.txt".format(101)
'0101.txt'
(see the documentation for more on formatting options).
Finally, given that you know how many times you will loop, I would recommend a for loop with range here, to avoid initialising and incrementing date manually:
for date in range(101, 132):
name = "{0:04d}.txt".format(date)
...

Categories