ArrayData = [['a', 'ad', '02/10/2021 7:39:19 am', 'Rank:1'],
['b', 'db', '02/10/2021 6:25:20 am', 'Rank:2'],
['a', 'sd', '02/10/2021 5:39:19 am', 'Rank:3'],
['b', 'esas', '02/10/2021 6:25:20 am', 'Rank:1'],
['a', 'aser', '02/10/2021 9:39:19 am', 'Rank:2'],
['d', 'ssss', '02/10/2021 11:39:19 am', 'Rank:1']]
The script should
Sort the same group (eg. sort group 'a' first, followed by group 'b', 'c', 'd') base on the time. More recent times and dates have higher ranks.
Update the "rank" in each subarray
Expected output:
[['d', 'ssss', '02/10/2021 11:39:19 am', 'Rank:1'],
['b', 'esas', '03/10/2021 6:25:20 am', 'Rank:2'],
['b', 'db', '02/10/2021 6:25:20 am', 'Rank:1'],
['a', 'aser', '02/10/2021 9:39:19 am', 'Rank:3'],
['a', 'ad', '02/10/2021 7:39:19 am', 'Rank:2'],
['a', 'sd', '02/10/2021 5:39:19 am', 'Rank:1']]
This is the current script I wrote
import operator
result = sorted(ArrayData, key=operator.itemgetter(2), reverse=True)
print(result)
May I know how to improve it?
CodePudding user response:
Note that this converts your datetime strings to datetime.datetime
objects. This may or may not be desirable, but is at least recommended if you plan on doing any other operations involving those dates. If you really want them as strings, see the commented line of code.
Note also that I'm assuming that your dates are dd/mm/yyyy
. If they are mm/dd/yyyy
instead, you'll need to switch the %d
and %m
in DATETIME_FORMAT
.
import datetime
import itertools
from operator import itemgetter as get
# Assumes day/month/year, switch %d and %m if not
DATETIME_FORMAT = "%d/%m/%Y %I:%M:%S %p"
def parse_datetimes(data: list) -> list:
result = []
for first, second, timestamp, rank in data:
timestamp = datetime.datetime.strptime(timestamp, DATETIME_FORMAT)
result.append([first, second, timestamp, rank])
return result
def custom_sort(data: list) -> list:
# Convert datetime strings to datetime objects, then sort by first element
sorted_data = sorted(parse_datetimes(data), key=get(0), reverse=True)
# Re-rank each group sorted by date
result = []
for _, group in itertools.groupby(sorted_data, key=get(0)):
ranked_group = []
sorted_group = sorted(group, key=get(2))
for rank, (*item, _) in enumerate(sorted_group, 1):
# item[2] = item[2].strftime(DATETIME_FORMAT)
ranked_group.append([*item, f"Rank:{rank}"])
result.extend(ranked_group[::-1])
return result
Demo:
>>> custom_sort(ArrayData)
[['d', 'ssss', datetime.datetime(2021, 10, 2, 11, 39, 19), 'Rank:1'],
['b', 'esas', datetime.datetime(2021, 10, 2, 6, 25, 20), 'Rank:2'],
['b', 'db', datetime.datetime(2021, 10, 2, 6, 25, 20), 'Rank:1'],
['a', 'aser', datetime.datetime(2021, 10, 2, 9, 39, 19), 'Rank:3'],
['a', 'ad', datetime.datetime(2021, 10, 2, 7, 39, 19), 'Rank:2'],
['a', 'sd', datetime.datetime(2021, 10, 2, 5, 39, 19), 'Rank:1']]
CodePudding user response:
The other solutions seemed too complicated to me. If you want you can use string comparison without resorting to datetime
s.
Here's a custom solution I rigged together that seems to give the desired output:
from collections import defaultdict
from pprint import pprint
from typing import DefaultDict, List
array_data = [['d', 'ssss', '11-04-20', 'Rank:1'],
['a', 'ad', '10-13-20', 'Rank:1'],
['b', 'db', '12-13-20', 'Rank:2'],
['a', 'sd', '05-13-20', 'Rank:3'],
['b', 'esas', '12-14-20', 'Rank:1'],
['a', 'aser', '12-13-20', 'Rank:2']]
final_array = []
group_to_data: DefaultDict[str, List[List[str]]] = defaultdict(list)
for data in array_data:
group_to_data[data[0]].append(data)
def sort_fn(x):
"""Sort by group, then by date"""
month, day, year = x[2].split('-')
return f'{year}{month}{day}'
for _, data in sorted(group_to_data.items(), reverse=True):
# sorts sub-list for each group
data.sort(key=sort_fn)
# iterating over data in reverse order, since that's how we want it in
# final result
for i in range(len(data) - 1, -1, -1):
new_rank = f"Rank:{i 1}"
item = data[i]
item[-1] = new_rank
final_array.append(item)
pprint(final_array)