Home > Mobile >  Sort list of lists based on date and first element
Sort list of lists based on date and first element

Time:10-09

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

  1. 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.

  2. 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 datetimes.

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