Home > Net >  Python, sort dict based on external list
Python, sort dict based on external list

Time:10-12

I have to sort a dict like:

jobs = {'elem_05': {'id': 'fifth'},
        'elem_03': {'id': 'third'},
        'elem_01': {'id': 'first'},
        'elem_00': {'id': 'zeroth'},
        'elem_04': {'id': 'fourth'},
        'elem_02': {'id': 'second'}}

based on the "id" elements, whose order can be found in a list:

sorting_list = ['zeroth', 'first', 'second', 'third', 'fourth', 'fifth']

The trivial way to do this is to use:

tmp = {}
for x in sorting_list:
    for k, v in jobs.items():
        if v["id"] == x:
            tmp.update({k: v})

but I was trying to figure out a more efficient and pythonic way to do this. I've been trying sorted and lambda functions as key, but I'm not familiar with that yet, so I was unsuccessful so far.

CodePudding user response:

There is a way to sort the dict using lambda as a sorting key:

jobs = {'elem_05': {'id': 'fifth'},
    'elem_03': {'id': 'third'},
    'elem_01': {'id': 'first'},
    'elem_00': {'id': 'zeroth'},
    'elem_04': {'id': 'fourth'},
    'elem_02': {'id': 'second'}}

sorting_list = ['zeroth', 'first', 'second', 'third', 'fourth', 'fifth']

sorted_jobs = dict(sorted(jobs.items(), key=lambda x: sorting_list.index(x[1]['id'])))

print(sorted_jobs)

This outputs

{'elem_00': {'id': 'zeroth'}, 'elem_01': {'id': 'first'}, 'elem_02': {'id': 'second'}, 'elem_03': {'id': 'third'}, 'elem_04': {'id': 'fourth'}, 'elem_05': {'id': 'fifth'}}

I have a feeling the sorted expression could be cleaner but I didn't get it to work any other way.

CodePudding user response:

You can use OrderedDict:

from collections import OrderedDict

sorted_jobs = OrderedDict([(el, jobs[key]['id']) for el, key in zip(sorting_list, jobs.keys())])

This creates an OrderedDict object which is pretty similar to dict, and can be converted to dict using dict(sorted_jobs).

CodePudding user response:

Similar to what is already posted, but with error checking in case id doesn't appear in sorting_list

sorting_list = ['zeroth', 'first', 'second', 'third', 'fourth', 'fifth']

jobs = {'elem_05': {'id': 'fifth'},
        'elem_03': {'id': 'third'},
        'elem_01': {'id': 'first'},
        'elem_00': {'id': 'zeroth'},
        'elem_04': {'id': 'fourth'},
        'elem_02': {'id': 'second'}}


def custom_order(item):
    try:
        return sorting_list.index(item[1]["id"])
    except ValueError:
        return len(sorting_list)


jobs_sorted = {k: v for k, v in sorted(jobs.items(), key=custom_order)}
print(jobs_sorted)

CodePudding user response:

I would use a dictionary as key for sorted:

order = {k:i for i,k in enumerate(sorting_list)}
# {'zeroth': 0, 'first': 1, 'second': 2, 'third': 3, 'fourth': 4, 'fifth': 5}

out = dict(sorted(jobs.items(), key=lambda x: order.get(x[1].get('id'))))

output:

{'elem_00': {'id': 'zeroth'},
 'elem_01': {'id': 'first'},
 'elem_02': {'id': 'second'},
 'elem_03': {'id': 'third'},
 'elem_04': {'id': 'fourth'},
 'elem_05': {'id': 'fifth'}}

CodePudding user response:

The sorted function costs O(n log n) in average time complexity. For better efficiency you can create a reverse mapping that maps each ID to the corresponding dict entry:

mapping = {d['id']: (k, d) for k, d in jobs.items()}

so that you can then construct a new dict by mapping sorting_list with the ID mapping above:

dict(map(mapping.get, sorting_list))

which, with your sample input, returns:

{'elem_00': {'id': 'zeroth'}, 'elem_01': {'id': 'first'}, 'elem_02': {'id': 'second'}, 'elem_03': {'id': 'third'}, 'elem_04': {'id': 'fourth'}, 'elem_05': {'id': 'fifth'}}

With this approach you'd only spend O(n) in average time complexity.

Demo: https://replit.com/@blhsing/WorseChartreuseFonts

  • Related