How to rearrange an Ordered Dictionary with a based on part of the key from a list

Here I am sharing two variants of solution for this.

1. For keys with same prefix, keep the order of initial OrderedDict

Here I am using list comprehension to iterate the order list and OrderDict. Based on comparison, we are passing list of tuples with desired order for creating OrderedDict object:

>>> from collections import OrderedDict
>>> old_OD = OrderedDict([('cat_1',1),
...             ('dog_1',2),
...             ('cat_2',3),
...             ('fish_1',4),
...             ('dog_2',5)])
>>> order = ['dog', 'cat', 'fish']

>>> new_OD = OrderedDict([(k,v) for o in order for k, v in old_OD.items() if k.startswith(o+'_')])
#                                              to match the prefix pattern of <key> + "_" ^ 

where new_OD will hold:

OrderedDict([('dog_1', 2), ('dog_2', 5), ('cat_1', 1), ('cat_2', 3), ('fish_1', 4)])

2. For keys with same prefix, perform lexicographical sorting of elements

We may modify the above solution using sorted and itertools.chain with nested list comprehension to achieve this as:

>>> from itertools import chain

>>> new_OD = OrderedDict(chain(*[sorted([(k,v) for k, v in old_OD.items() if k.startswith(o+'_')]) for o in order]))

where new_OD will hold:

OrderedDict([('dog_1', 2), ('dog_2', 5), ('cat_1', 1), ('cat_2', 3), ('fish_1', 4)])

You can build a dict that maps each item in order to its index, and then use the sorted function with a key function that maps the substring of the each key in old_OD that appears in the keys of the mapping dict to the corresponding index using the mapping dict:

keys = {k: i for i, k in enumerate(order)}
OrderedDict(sorted(old_OD.items(), key=lambda t: keys.get(next(i for i in t[0].split('_') if i in keys))))

This returns:

OrderedDict([('dog_1', 2), ('dog_2', 5), ('cat_1', 1), ('cat_2', 3), ('fish_1', 4)])

Tags:

Python