1

I am trying to sort a dictionary by value, which is a timestamp in the format H:MM:SS (eg "0:41:42") but the code below doesn't work as expected:

album_len = {
    'The Piper At The Gates Of Dawn': '0:41:50',
    'A Saucerful of Secrets': '0:39:23',
    'More': '0:44:53', 'Division Bell': '1:05:52',
    'The Wall': '1:17:46',
    'Dark side of the moon': '0:45:18',
    'Wish you were here': '0:44:17',
    'Animals': '0:41:42'
}
album_len = OrderedDict(sorted(album_len.items()))

This is the output I get:

OrderedDict([
    ('A Saucerful of Secrets', '0:39:23'),
    ('Animals', '0:41:42'),
    ('Dark side of the moon', '0:45:18'),
    ('Division Bell', '1:05:52'),
    ('More', '0:44:53'),
    ('The Piper At The Gates Of Dawn', '0:41:50'),
    ('The Wall', '1:17:46'),
    ('Wish you were here', '0:44:17')])

It's not supposed to be like that. The first element I expected to see is ('The Wall', '1:17:46'), the longest one.

How do I get the elements sorted the way I intended?

Paulo Amaral
  • 747
  • 1
  • 5
  • 24
roee
  • 39
  • 5

2 Answers2

3

Try converting each value to a datetime and using that as the key:

from collections import OrderedDict
from datetime import datetime


def convert_to_datetime(val):
    return datetime.strptime(val, "%H:%M:%S")


album_len = {'The Piper At The Gates Of Dawn': '0:41:50',
             'A Saucerful of Secrets': '0:39:23', 'More': '0:44:53',
             'Division Bell': '1:05:52', 'The Wall': '1:17:46',
             'Dark side of the moon': '0:45:18',
             'Wish you were here': '0:44:17', 'Animals': '0:41:42'}
album_len = OrderedDict(
    sorted(album_len.items(), key=lambda i: convert_to_datetime(i[1]))
)
print(album_len)

Output:

OrderedDict([('A Saucerful of Secrets', '0:39:23'), ('Animals', '0:41:42'),
             ('The Piper At The Gates Of Dawn', '0:41:50'),
             ('Wish you were here', '0:44:17'), ('More', '0:44:53'),
             ('Dark side of the moon', '0:45:18'), ('Division Bell', '1:05:52'),
             ('The Wall', '1:17:46')])

Or in descending order with reverse set to True:

album_len = OrderedDict(
    sorted(
        album_len.items(),
        key=lambda i: convert_to_datetime(i[1]),
        reverse=True
    )
)

Output:

OrderedDict([('The Wall', '1:17:46'), ('Division Bell', '1:05:52'),
             ('Dark side of the moon', '0:45:18'), ('More', '0:44:53'),
             ('Wish you were here', '0:44:17'),
             ('The Piper At The Gates Of Dawn', '0:41:50'),
             ('Animals', '0:41:42'), ('A Saucerful of Secrets', '0:39:23')])

Edit: If only insertion order needs maintained and the OrderedDict specific functions like move_to_end are not going to be used then a regular python dict also works here for Python3.7+.

Ascending:

album_len = dict(
    sorted(album_len.items(), key=lambda i: convert_to_datetime(i[1]))
)

Descending:

album_len = dict(
    sorted(album_len.items(), key=lambda i: convert_to_datetime(i[1]),
           reverse=True)
)
Henry Ecker
  • 34,399
  • 18
  • 41
  • 57
  • 1
    Great (so upvoted), but would suggest you can use regular dict for Python 3.7+ rather than needing OrderedDict. – DarrylG May 09 '21 at 18:53
1

This is a duplicate of the question: How do I sort a dictionary by value?"

>>> dict(sorted(album_len.items(), key=lambda item: item[1]))
{'A Saucerful of Secrets': '0:39:23',
 'Animals': '0:41:42',
 'The Piper At The Gates Of Dawn': '0:41:50',
 'Wish you were here': '0:44:17',
 'More': '0:44:53',
 'Dark side of the moon': '0:45:18',
 'Division Bell': '1:05:52',
 'The Wall': '1:17:46'}

Note: the time format is already lexicographically ordered, you don't need to convert to datetime.
See comment below of @DarrylG. He's totally right, therefore, the remark on the lexicographic order is valid as long as the duration does not exceed 9:59:59 except if hours are padded with a leading zero.

Corralien
  • 109,409
  • 8
  • 28
  • 52
  • 1
    **Note: the time format is already lexicographically ordered, you don't need to convert to datetime**--is this correct? Although this works for the data given, for more general data won't the time '11:44:17' be placed before '2:44:17' when it should be later? – DarrylG May 09 '21 at 19:20