ExplodingGayFish
ExplodingGayFish

Reputation: 2897

Sort a list of dictionary with order of specific key

I have a list of dict like this:

data = [{"uid": "a1", "name": "b1"}, 
        {"uid": "a3", "name": "b3"}, 
        {"uid": "a4", "name": "b4"},
        {"uid": "a5", "name": "b5"}]

Now I want to sort that list with the order of the uid field like this:

uid_order = ['a1', 'a5', 'a3', 'a4']

Which means the output should be:

[{"uid": "a1", "name": "b1"}, 
 {"uid": "a5", "name": "b5"},
 {"uid": "a3", "name": "b3"},
 {"uid": "a4", "name": "b4"}]

Of course, I can create a new empty list then use 2 for loops to check and add every element, but is there any elegant way (or better complexity) to do this?

Upvotes: 2

Views: 207

Answers (3)

Kelly Bundy
Kelly Bundy

Reputation: 27589

O(n) solution:

>>> [*map({d['uid']: d for d in data}.get, uid_order)]
[{'uid': 'a1', 'name': 'b1'},
 {'uid': 'a5', 'name': 'b5'},
 {'uid': 'a3', 'name': 'b3'},
 {'uid': 'a4', 'name': 'b4'}]

Upvotes: 4

Mad Physicist
Mad Physicist

Reputation: 114310

You can sort a list in place with it's sort method if you want to avoid making a new list. This will use Timsort, which has O(n log n) complexity, much better than your suggested O(n^2) implementation:

uid_order = {k: i for i, k in enumerate(uid_order)}
data.sort(key=uid_order.get)

To speed up lookup into uid_order, I invert it into a dictionary (invert because a list maps index to item, while you want item to index). Instead of an O(n) lookup at each evaluation of the key, you have an O(1) lookup now.

An alternative way to make the dictionary using only function calls:

uid_order = dict(map(reversed, enumerate(uid_order)))

See also: Python Sorting HOW TO

Upvotes: 3

Sebastian Baltser
Sebastian Baltser

Reputation: 758

With the sorted-function you can specify a key that the function should sort after. You want to access the value of the "uid" key for each dictionary and that value's index in uid_order determines that dictionary's index in the sorted list:

sorted(data, key = lambda x: uid_order.index(x["uid"]))

What this code means is, each element of the list gets passed to the lambda-function. The resulting value is used as key to sort after.

Upvotes: 2

Related Questions