#python #sorting
Вопрос:
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']]
Сценарий должен
- Отсортируйте одну и ту же группу (например, сначала отсортируйте группу «a», затем группу «b», «c», «d») по времени. Более поздние времена и даты имеют более высокие ранги.
- Обновите «ранг» в каждом подмассиве
Ожидаемый результат:
[['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']]
Это текущий сценарий, который я написал
import operator
result = sorted(ArrayData, key=operator.itemgetter(2), reverse=True)
print(result)
Могу я узнать, как его улучшить?
Комментарии:
1. Вы все еще сортируете даты на основе необработанных строк, вам нужно их проанализировать
2. Почему
['d', 'ssss', '11-04-20', 'Rank:1']
первый в выводе?3. Обновите «ранг» в каждом подмассиве — можете ли вы это объяснить? (На данный момент — мое решение увеличивает ранг на 1)
4. Это довольно ясно при проверке желаемого результата: «ранг» соответствует тому, насколько недавняя дата в каждой группе. Более поздние даты имеют более высокие ранги, но каждая группа ранжируется только с другими участниками группы.
5. @ddjohn, да, это то, чего я хочу
Ответ №1:
Обратите внимание, что это преобразует ваши строки даты и времени в datetime.datetime
объекты. Это может быть или не быть желательным, но, по крайней мере, рекомендуется, если вы планируете выполнять какие-либо другие операции, связанные с этими датами. Если вы действительно хотите, чтобы они были строками, см. прокомментированную строку кода.
Обратите также внимание, что я предполагаю, что ваши даты таковы dd/mm/yyyy
. Если они mm/dd/yyyy
вместо этого, вам нужно будет включить %d
и %m
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
ДЕМОНСТРАЦИЯ:
>>> 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']]
Комментарии:
1. Что делать, если нужно учитывать как время (час/минуту), так и дату?
2. @liuxu смотрите мою правку
Ответ №2:
Другие решения казались мне слишком сложными. Если вы хотите, вы можете использовать сравнение строк, не прибегая к datetime
s.
Вот пользовательское решение, которое я собрал вместе, которое, похоже, дает желаемый результат:
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)
Комментарии:
1. Подсписки уже сгруппированы по первому элементу через ваш
defaultdict
. Я не думаю, что возвратx[0]
изsort_fn()
необходим, такsort_fn()
как уже выполняется только для данных, сгруппированных по первому элементу. Я бы также посоветовалfor _, data
, если вы не планируете использовать переменнуюgroup
итератора в любом месте тела цикла.2. ах, хороший улов по обоим пунктам! в ближайшее время я обновлю ответ с этими исправлениями.