Reputation: 2897
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
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
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
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