Python Forum
Easy way to sort a nested dict - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: General Coding Help (https://python-forum.io/forum-8.html)
+--- Thread: Easy way to sort a nested dict (/thread-14572.html)



Easy way to sort a nested dict - Alfalfa - Dec-07-2018

I want to convert a dict of audio metadata into an ordered dict, sorted by it's nested values (such as values of keys 'artist' or 'track'). What would be the fastest way to achieve this?

The database is organized like this:

#!/usr/bin/python3
from collections import OrderedDict

db = \
{
  "/data/Music/Daft Punk/Tron_ Legacy (Cd1)/1-17 Disc Wars.mp3": {
    "duration": "00:04:11",
    "artist": "Daft Punk",
    "album": "Tron: Legacy (Cd1)",
    "track": "17",
    "title": "Disc Wars"
  },
  "/data/Music/Keith Kouna/Les ann\u00e9es monsieur/07 - Godichons.mp3": {
    "duration": "00:05:09",
    "artist": "Keith Kouna",
    "album": "Les ann\u00e9es monsieur",
    "track": "7",
    "title": "Godichons"
  },
  "/data/Music/Daft Punk/Tron_ Legacy (Cd1)/1-07 Rinzler.mp3": {
    "duration": "00:02:17",
    "artist": "Daft Punk",
    "album": "Tron: Legacy (Cd1)",
    "track": "07",
    "title": "Rinzler"
  },
  "/data/Music/Keith Kouna/Les ann\u00e9es monsieur/10 - Le Tape.mp3": {
    "duration": "00:06:06",
    "artist": "Keith Kouna",
    "album": "Les ann\u00e9es monsieur",
    "track": "10",
    "title": "Le Tape"
  },
  "/data/Music/Keith Kouna/Les ann\u00e9es monsieur/09 - L'or.mp3": {
    "duration": "00:05:05",
    "artist": "Keith Kouna",
    "album": "Les ann\u00e9es monsieur",
    "track": "9",
    "title": "L'or"
  }
}

db = OrderedDict(sorted(db.items(), key=lambda t: t[0]))
for key in db:
    print(key)



RE: Easy way to sort a nested dict - buran - Dec-07-2018

# Create an object to compute John's weight in Kgs as well as age in years
from collections import OrderedDict
 
db = {
  "/data/Music/Daft Punk/Tron_ Legacy (Cd1)/1-17 Disc Wars.mp3": {
    "duration": "00:04:11",
    "artist": "Daft Punk",
    "album": "Tron: Legacy (Cd1)",
    "track": "17",
    "title": "Disc Wars"
  },
  "/data/Music/Keith Kouna/Les ann\u00e9es monsieur/07 - Godichons.mp3": {
    "duration": "00:05:09",
    "artist": "Keith Kouna",
    "album": "Les ann\u00e9es monsieur",
    "track": "7",
    "title": "Godichons"
  },
  "/data/Music/Daft Punk/Tron_ Legacy (Cd1)/1-07 Rinzler.mp3": {
    "duration": "00:02:17",
    "artist": "Daft Punk",
    "album": "Tron: Legacy (Cd1)",
    "track": "07",
    "title": "Rinzler"
  },
  "/data/Music/Keith Kouna/Les ann\u00e9es monsieur/10 - Le Tape.mp3": {
    "duration": "00:06:06",
    "artist": "Keith Kouna",
    "album": "Les ann\u00e9es monsieur",
    "track": "10",
    "title": "Le Tape"
  },
  "/data/Music/Keith Kouna/Les ann\u00e9es monsieur/09 - L'or.mp3": {
    "duration": "00:05:05",
    "artist": "Keith Kouna",
    "album": "Les ann\u00e9es monsieur",
    "track": "9",
    "title": "L'or"
  }
}
 
db = OrderedDict(sorted(db.items(), key=lambda item:(item[1]['artist'], '{:0>2}'.format(item[1]['track']))))
for key in db:
    print(key)
Output:
/data/Music/Daft Punk/Tron_ Legacy (Cd1)/1-07 Rinzler.mp3 /data/Music/Daft Punk/Tron_ Legacy (Cd1)/1-17 Disc Wars.mp3 /data/Music/Keith Kouna/Les annees monsieur/07 - Godichons.mp3 /data/Music/Keith Kouna/Les annees monsieur/09 - L'or.mp3 /data/Music/Keith Kouna/Les annees monsieur/10 - Le Tape.mp3 >>>



RE: Easy way to sort a nested dict - Gribouillis - Dec-07-2018

Buran's idea is very good. Here is a variation with some abstraction
#!/usr/bin/python3
from collections import OrderedDict
 
db = \
{
  "/data/Music/Daft Punk/Tron_ Legacy (Cd1)/1-17 Disc Wars.mp3": {
    "duration": "00:04:11",
    "artist": "Daft Punk",
    "album": "Tron: Legacy (Cd1)",
    "track": "17",
    "title": "Disc Wars"
  },
  "/data/Music/Keith Kouna/Les ann\u00e9es monsieur/07 - Godichons.mp3": {
    "duration": "00:05:09",
    "artist": "Keith Kouna",
    "album": "Les ann\u00e9es monsieur",
    "track": "7",
    "title": "Godichons"
  },
  "/data/Music/Daft Punk/Tron_ Legacy (Cd1)/1-07 Rinzler.mp3": {
    "duration": "00:02:17",
    "artist": "Daft Punk",
    "album": "Tron: Legacy (Cd1)",
    "track": "07",
    "title": "Rinzler"
  },
  "/data/Music/Keith Kouna/Les ann\u00e9es monsieur/10 - Le Tape.mp3": {
    "duration": "00:06:06",
    "artist": "Keith Kouna",
    "album": "Les ann\u00e9es monsieur",
    "track": "10",
    "title": "Le Tape"
  },
  "/data/Music/Keith Kouna/Les ann\u00e9es monsieur/09 - L'or.mp3": {
    "duration": "00:05:05",
    "artist": "Keith Kouna",
    "album": "Les ann\u00e9es monsieur",
    "track": "9",
    "title": "L'or"
  }
}
  
from functools import wraps

def dbsorter(func):
    @wraps(func)
    def wrapper(db, reverse=False):
        return OrderedDict(sorted(
            db.items(), reverse=bool(reverse), key=lambda item: func(*item)))
    return wrapper

@dbsorter
def by_artist_and_track(key, data):
    return data['artist'], int(data['track'])

sdb = by_artist_and_track(db)
for key in sdb:
    print(key)
print()

@dbsorter
def by_duration(key, data):
    h, m, s = (int(x) for x in data['duration'].split(':'))
    return (h, m, s)

sdb = by_duration(db, reverse=True)
for key in sdb:
    print(key)
Output:
/data/Music/Daft Punk/Tron_ Legacy (Cd1)/1-07 Rinzler.mp3 /data/Music/Daft Punk/Tron_ Legacy (Cd1)/1-17 Disc Wars.mp3 /data/Music/Keith Kouna/Les années monsieur/07 - Godichons.mp3 /data/Music/Keith Kouna/Les années monsieur/09 - L'or.mp3 /data/Music/Keith Kouna/Les années monsieur/10 - Le Tape.mp3 /data/Music/Keith Kouna/Les années monsieur/10 - Le Tape.mp3 /data/Music/Keith Kouna/Les années monsieur/07 - Godichons.mp3 /data/Music/Keith Kouna/Les années monsieur/09 - L'or.mp3 /data/Music/Daft Punk/Tron_ Legacy (Cd1)/1-17 Disc Wars.mp3 /data/Music/Daft Punk/Tron_ Legacy (Cd1)/1-07 Rinzler.mp3



RE: Easy way to sort a nested dict - Alfalfa - Dec-07-2018

Thanks to both of you. I forgot to mention that the sort parameter (artist, album, ...) and the sort order must be dynamically callables. Also, track can be an empty string "" and duration "?".

So I endend up doing it like this:

def _sortInt(string):
    try:
        if string.count(":"):
            string = string.split(":")
            return int(string[0])*3600 + int(string[1])*60 + int(string[2])
        else:
            return int(string)
    except ValueError:
        return 0

def _sortMetadata(db, column, reverse):
    if column == "track" or column == "duration":
        return OrderedDict(sorted(db.items(), key=lambda item:(_sortInt(item[1][column])), reverse=reverse))
    else:
        return OrderedDict(sorted(db.items(), key=lambda item:(item[1][column]), reverse=reverse))

db = _sortMetadata(db, "track", reverse=False)
db = _sortMetadata(db, "album", reverse=False)
db = _sortMetadata(db, "artist", reverse=False)
#db = _sortMetadata(db, "duration", reverse=False)
for key in db:
    print(key)