Find the maximum time value in the list containing time tuples in the format ("hour", "min", "AM / PM")

I have a list of tuples that represent different times

timeList = [('4', '12', 'PM'), ('8', '23', 'PM'), ('4', '03', 'AM'), ('1', '34', 'AM'), ('12', '48', 'PM'), ('4', '13', 'AM'), ('11', '09', 'AM'), ('3', '12', 'PM'), ('4', '10', 'PM')] 

I want to return max from the list, after some searches, I realized that I can use the key in max to search by AM or PM in the first place.
print(max(timeList, key = operator.itemgetter(2)))

When I run this, I get the wrong maximum ('4', '12', 'PM') I thought about it and not only makes no sense, given that 8:23 should be maximum, but I also realized that 12 : 48 will probably return max since it is PM, and also technically over 8 in my search.

As the saying goes, how can I get this maximum to find the latest possible time, since the formatting of the list cannot be changed.

+5
source share
6 answers

Just identify the appropriate key function. You want int(hour) , int(minute) and 'PM' to sort lexicographically higher than "AM" , but you should consider it first. In addition, you need to take the module for 12 hours, so that 12 will sort less than other numbers within pm / am :

 In [39]: timeList = [('4', '12', 'PM'), ('8', '23', 'PM'), ('4', '03', 'AM'), ('1', '34', 'AM'), ...: ('12', '48', 'PM'), ('4', '13', 'AM'), ('11', '09', 'AM'), ('3', '12', 'PM'), ...: ('4', '10', 'PM')] In [40]: def key(t): ...: h, m, z = t ...: return z, int(h)%12, int(m) ...: In [41]: max(timeList,key=key) Out[41]: ('8', '23', 'PM') 

But the most important thing is to actually use datetime.time objects, instead of pretending to be a tuple of strings, is a good way to store time.

So something like:

 In [49]: def to_time(t): ...: h, m, z = t ...: h, m = int(h)%12, int(m) ...: if z == "PM": ...: h += 12 ...: return datetime.time(h, m) ...: In [50]: real_time_list = list(map(to_time, timeList)) In [51]: real_time_list Out[51]: [datetime.time(16, 12), datetime.time(20, 23), datetime.time(4, 3), datetime.time(1, 34), datetime.time(12, 48), datetime.time(4, 13), datetime.time(11, 9), datetime.time(15, 12), datetime.time(16, 10)] In [52]: list(map(str, real_time_list)) Out[52]: ['16:12:00', '20:23:00', '04:03:00', '01:34:00', '12:48:00', '04:13:00', '11:09:00', '15:12:00', '16:10:00'] 

Notice now max "just works":

 In [54]: t = max(real_time_list) In [55]: print(t) 20:23:00 

And if you need a nice line to print, just do the formatting at this point:

 In [56]: print(t.strftime("%I:%M %p")) 08:23 PM 
+5
source

Why not add structure to your data?

 from datetime import datetime max(datetime.strptime(''.join(x), '%I%M%p') for x in timeList) # datetime.datetime(1900, 1, 1, 20, 23) # ie 8.23pm 

As long as you say that β€œlist formatting should not be changed,” this is what all decisions implicitly do to make comparisons.

+4
source
Parameter

key with the max function is used to notify max at what value you want to perform the maximum operation. itemgetter(2) selects the value in the second index, and lexicographically β€œPM” is the highest value in the list in index 2 (lexicographically β€œPM”> β€œAM”). You can use the lambda function to calculate the maximum on the tuple at indices 0 and 1 as:

 >>> timeList = [('4', '12', 'PM'), ('8', '23', 'PM'), ('4', '03', 'AM'), ('1', '34', 'AM'), ('12', '48', 'PM'), ('4', '13', 'AM'), ('11', '09', 'AM'), ('3', '12', 'PM'), ('4', '10', 'PM')] # type-casting it to `int` to avoid incorrect result # due lexicographical comparision of `str` >>> max(timeList, key=lambda x: (x[2], int(x[0]), int(x[1]))) ('12', '48', 'PM') # ^ ^ ^ Third priority to `int` value of minute # ^ ^ Second priority to int value of `hour` # ^ First priority to lexicographically sort on `AM`/`PM` 

OR, you are doing a comparison on datetime.datetime like:

 >>> from datetime import datetime >>> max(timeList, key=lambda x: datetime.strptime('{}:{}{}'.format(*x), '%I:%M%p')) ('8', '23', 'PM') 

I think you should first create a datetime.datetime list instead of tuples .

+2
source

Adding to solutions that you can also sort with datetime:

 from datetime import datetime timeList = [('4', '12', 'PM'), ('8', '23', 'PM'), ('4', '03', 'AM'), ('1', '34', 'AM'), ('12', '48', 'PM'), ('4', '13', 'AM'), ('11', '09', 'AM'), ('3', '12', 'PM'), ('4', '10', 'PM')] sorted(timeList, key=lambda x: datetime.strptime(''.join(x), '%I%M%p'))[-1] 

Return:

 ('8', '23', 'PM') 
+1
source

This is really elegantly implemented in pandas, which allows MultiIndex, which we can then sort and think of:

 import numpy as np import pandas as pd timeList = [('4','12','PM'), ('8','23','PM'), ('4','03','AM'), ('1','34','AM'), ('12','48','PM'), ('4','13','AM'), ('11','09','AM'), ('3','12','PM'), ('4','10','PM')] timeDf = pd.DataFrame(timeList, columns=['hr','min','meridiem']) timeDf.set_index(['meridiem','hr','min'], inplace=True, drop=True) #timeDf['value'] = np.random.randint(1,10, timeDf.shape[0]) # np.nan timeDf.sort_index(level=0, ascending=False, inplace=True) # sort by meridiem, then the remaining cols (alphanumeric string comparison) timeDf.index[0] # ('PM', '8', '23') 

Notes:

  • If you want to keep hr,min,meridiem as columns in your df, use set_index(..., drop=False)
  • as AntonvBR points out, if the timestamp also includes the time zone, then we could no longer use the simple view in several different (string) fields; we want to calculate the base datetime, and then use this as a sort key.
0
source

looks like a timeList. perhaps it makes sense to disassemble it as such?

  max([datetime.strptime("{}:{} {}".format(t[0],t[1],t[2]),'%I:%M %p') for t in timeList]).strftime("%H:%M") 
-1
source

Source: https://habr.com/ru/post/1275387/