简体   繁体   中英

Sort list of dictionaries by appearance of key in a second ordered list

I have two lists. My first list first_list_ordered contains strings.

first_list_ordered = ["id1", "id2", "id3", "id4", "id5", "id6", "id7"]

My second list second_list_unsorted contains dictionaries with at least one key called id , where the value might appear in first_list_ordered .

second_list_unordered = [{"id": "id6", "content": "sth"},
                         {"id": "id4", "content": "sth"},
                         {"id": "id1", "content": "sth"},
                         {"id": "id3", "content": "sth"}]

Now I would like to sort the second list by the appearance order of id 's value in the first list. The result should look like this:

result = [{"id": "id1", "content": "sth"},
          {"id": "id3", "content": "sth"},
          {"id": "id4", "content": "sth"},
          {"id": "id6", "content": "sth"}]

So if you create a list of all values id of every dict in second_list_unordered you get a unordered subset of first_list_ordered .

My approach would look like:

>>> first_list_ordered = ["id1", "id2", "id3", "id4", "id5", "id6", "id7"]
>>> second_list_unordered = [{"id": "id6", "content": "sth"}, {"id": "id4", "content": "sth"}, {"id": "id1", "content": "sth"}, {"id": "id3", "content": "sth"}]
>>> indices = {c: i for i, c in enumerate(first_list_ordered)}
>>> result = sorted(second_list_unordered, key=indices.get)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'

Obviously it doesn't work that way... now I'm stuck.

Thanks for any hints!

You need to pass the id key in to indices.get , not the whole dictionary:

result = sorted(second_list_unordered, key=lambda d: indices.get(d['id']))

Demo:

>>> from pprint import pprint
>>> first_list_ordered = ["id1", "id2", "id3", "id4", "id5", "id6", "id7"]
>>> second_list_unordered = [{"id": "id6", "content": "sth"},
...                          {"id": "id4", "content": "sth"},
...                          {"id": "id1", "content": "sth"},
...                          {"id": "id3", "content": "sth"}]
>>> indices = {c: i for i, c in enumerate(first_list_ordered)}
>>> sorted(second_list_unordered, key=lambda d: indices.get(d['id']))
[{'content': 'sth', 'id': 'id1'}, {'content': 'sth', 'id': 'id3'}, {'content': 'sth', 'id': 'id4'}, {'content': 'sth', 'id': 'id6'}]
>>> pprint(_)
[{'content': 'sth', 'id': 'id1'},
 {'content': 'sth', 'id': 'id3'},
 {'content': 'sth', 'id': 'id4'},
 {'content': 'sth', 'id': 'id6'}]

To make it a little more interesting, shuffling the first_list_ordered , as the sorted order of the id values obscures the purpose somewhat:

>>> import random
>>> random.shuffle(first_list_ordered)
>>> first_list_ordered
['id2', 'id7', 'id1', 'id4', 'id6', 'id5', 'id3']
>>> indices = {c: i for i, c in enumerate(first_list_ordered)}
>>> sorted(second_list_unordered, key=lambda d: indices.get(d['id']))
[{'content': 'sth', 'id': 'id1'}, {'content': 'sth', 'id': 'id4'}, {'content': 'sth', 'id': 'id6'}, {'content': 'sth', 'id': 'id3'}]
>>> pprint(_)
[{'content': 'sth', 'id': 'id1'},
 {'content': 'sth', 'id': 'id4'},
 {'content': 'sth', 'id': 'id6'},
 {'content': 'sth', 'id': 'id3'}]

If speed is not an issue, then why not do it manually?

Here cheap and dirty oneliner.

In [55]: second_list_unordered = [{"id": "id6", "content": "sth"}, {"id": "id4", "content": "sth"}, {"id": "id1", "content": "sth"}, {"id": "id3", "content": "sth"}]

In [56]: first_list_ordered = ["id1", "id2", "id3", "id4", "id5", "id6", "id7"]
In [57]: f = first_list_ordered

In [58]: s = second_list_unordered

In [59]: [oval[0] for oval in [[val for val in s if (val["id"] == key)] for key in f] if oval]
Out[59]: 
[{'content': 'sth', 'id': 'id1'},
 {'content': 'sth', 'id': 'id3'},
 {'content': 'sth', 'id': 'id4'},
 {'content': 'sth', 'id': 'id6'}]

In [60]: fff = ["id3", "id4", "id5", "id2", "id1", "id6", "id7"]

In [61]: [oval[0] for oval in [[val for val in s if (val["id"] == key)] for key in fff] if oval]
Out[61]: 
[{'content': 'sth', 'id': 'id3'},
 {'content': 'sth', 'id': 'id4'},
 {'content': 'sth', 'id': 'id1'},
 {'content': 'sth', 'id': 'id6'}]

Same, but splitted in two operations for understanding:

In [62]: temp = [[val for val in s if (val["id"] == key)] for key in f]

In [63]: [val[0] for val in temp if val]
Out[63]: 
[{'content': 'sth', 'id': 'id1'},
 {'content': 'sth', 'id': 'id3'},
 {'content': 'sth', 'id': 'id4'},
 {'content': 'sth', 'id': 'id6'}]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM