Saravanan K
Saravanan K

Reputation: 684

Reordering Dictionary based on specific order

I have a dictionary in the below key, value format:

PairDict= {(19, 6): 13, (2, 29): 10, (38, 8): 20, (38, 5): 5} 

The key is a tuple with two elements, and the value represent number of cycle.

I would need to reorder the dictionary into a list based on a given sort order of the first element of the tuple e.g

SortOrder= [(38, 25), (19, 13), (2, 10)]

Where, the first element of the tuple in SortOrder above represent the first element of the tuple key in PairDict. The second element of the tuple in SortOrder represent the sum of number of cycle for the first element in PairDict

If the PairDict has more than one tuple with the same first element, its sort order could in anyway, e.g. 38 need to be sorted first, in PictDict there are two tuples starting with 38, that is (38, 8) and (38, 5). Its order can be either [(38, 8): 20, (38, 5): 5] or [(38, 5): 5, (38, 8): 20].

The expected output of the ordering PairDict based on SortOrder can be,

SortedPairDict= [((38, 8), 20), ((38, 5), 5), ((19, 6), 13), ((2, 29), 10)], 

or

SortedPairDict= [((38, 5), 5), ((38, 8), 20), ((19, 6), 13), ((2, 29), 10)],

Tried to search, the closest example I could get is,

Refer: reordering list of dicts arbitrarily in python

But having problem to realize my code with that example as my key is a tuple.

Can you please point me if there is any examples I can read on this or guide me how is the best way can handle this.

Appreciate your help. Merry Christmas :-)

Thanks.

Update : timeit Analysis Amber and Ashwini's Solution

PairDict= {(19, 8): 13, (2, 29): 10, (38, 8): 20, (2, 18): 10, (43, 8): 20, (5, 13): 15, (6, 21): 9, (7, 25): 11, (8, 19): 15, (44, 4): 20, (0, 10): 9, (0, 36): 9, (21, 6): 19, (30, 4): 17, (24, 0): 11, (3, 14): 21, (6, 12): 9, (7, 20): 11, (20, 7): 10, (5, 23): 15, (11, 5): 15, (37, 6): 13, (34, 8): 20, (18, 2): 10, (0, 24): 9, (12, 6): 13, (8, 38): 15, (39, 5): 18, (42, 5): 17, (26, 7): 22, (10, 0): 9, (31, 0): 14, (3, 27): 21, (6, 37): 9, (36, 0): 17, (0, 33): 9, (41, 0): 18, (5, 39): 15, (5, 42): 15, (6, 40): 9, (5, 11): 15, (9, 3): 21, (25, 7): 13, (7, 28): 11, (3, 9): 21, (29, 2): 4, (32, 4): 22, (0, 41): 9, (40, 6): 10, (28, 7): 20, (6, 15): 9, (17, 4): 23, (23, 5): 18, (16, 5): 23, (8, 34): 15, (5, 16): 15, (8, 22): 15, (8, 43): 15, (4, 17): 19, (13, 5): 23, (4, 30): 19, (7, 26): 11, (15, 6): 13, (4, 44): 19, (22, 8): 18, (0, 31): 9, (35, 5): 23, (14, 3): 20, (33, 0): 12, (5, 35): 15, (27, 3): 16, (4, 32): 19}

SortOrder= [(5, 105), (4, 76), (8, 75), (3, 63), (0, 54), (6, 45), (7, 44), (13, 23), (16, 23), (17, 23), (35, 23), (26, 22), (32, 22), (9, 21), (2, 20), (14, 20), (28, 20), (34, 20), (38, 20), (43, 20), (44, 20), (21, 19), (22, 18), (23, 18), (39, 18), (41, 18), (30, 17), (36, 17), (42, 17), (27, 16), (11, 15), (31, 14), (12, 13), (15, 13), (19, 13), (25, 13), (37, 13), (33, 12), (24, 11), (18, 10), (20, 10), (40, 10), (10, 9), (29, 4)]

In [5]: %timeit amber() 10000 loops, best of 3: 71.1 us per loop

In [6]: %timeit ashwc() 1000 loops, best of 3: 753 us per loop

Upvotes: 0

Views: 319

Answers (2)

Mats Kindahl
Mats Kindahl

Reputation: 2060

I find itertools.groupby very useful in these cases, so I thought that I should just mention an alternative. (Actually, I find itertools very useful in many cases, but I have a history of a lot of functional programming.) The advantage here is that you don't have to build a separate dictionary, which may or may not improve performance.

    from itertools import groupby

    PairDict= {(19, 6): 13, (2, 29): 10, (38, 8): 20, (38, 5): 5}
    key = lambda i: i[0][0]
    SortedPairDict = groupby(sorted(PairDict.iteritems(), key=key), key=key)

The SortOrder should then be possible to compute using:

    SortOrder = [ (k, sum(v[1] for v in g)) for k, g in SortedPairDict ]

I note that the the list in the longer example above is not in any particular order, so not sure if I have misunderstood anything.

Upvotes: 1

Amber
Amber

Reputation: 526573

sortkeys = dict((x[0], index) for index,x in enumerate(SortOrder))
SortedPairDict = sorted(PairDict.iteritems(),
                        key=lambda x: sortkeys[x[0][0]])

The idea here is that we don't really want to re-invent sorting, so we'd like to have things in a format that we can use Python's built-in sorted() function.

In order to do that, we need the ordering information in a format that sorted() will accept. The simplest way to do that is by defining a "key" that Python already knows how to sort, such as a set of ordered integers.

In this case, we do this by mapping the values from the SortOrder list to their position within that list. We then define our key function to simply look up the SortOrder position corresponding to the first element of your tuple key.


>>> sortkeys = dict((x[0], index) for index,x in enumerate(SortOrder))
>>> SortedPairDict = sorted(PairDict.iteritems(),
...                         key=lambda x: sortkeys[x[0][0]])
>>> SortedPairDict
[((38, 5), 5), ((38, 8), 20), ((19, 6), 13), ((2, 29), 10)]

Upvotes: 5

Related Questions