开发者

Sorting a list of dicts in python, using another list as a "filter"?

b = [{'id': 'a'}, {'id': 'c'}, {'id': 'b'}, {'id': 'e'}]

I need this to become:

b = [{'id': 'a'}, {'id': 'c'}, {'id': 'e'}, {'id': 'b'}]

This new order is defined by another list.

my_filter = ['a', 'c', 'e', 'b']

...as you can see, the list with dicts now have a sequence of ids that were presented on the my_filter variable.

I'm capable of reordering this but using a bunch of fors and it's not going to be efficient. Do you know a better way? I already know how to sort a list of dictionaries by values of the dictionary, but I need this order to be defined by another list.

EDIT: my_filter was named filter, I changed after Dave Kirby's recommendation since it's a builtin. Since some answers sti开发者_高级运维ll have filter in them, this edit section is to avoid confusion if you see filter in some of them.


>>> sorted(b, key=lambda x: filter.index(x['id']))
[{'id': 'a'}, {'id': 'c'}, {'id': 'e'}, {'id': 'b'}]


Keep in mind Ignacio's solution is equivalent to doing the nested for loops you wanted to avoid. i.e. it's n^2. A more efficient solution is as follows:

>>> filterdict = dict((k,i) for i,k in enumerate(filter))
>>> sorted(b, key=lambda x: filterdict[x['id']])
[{'id': 'a'}, {'id': 'c'}, {'id': 'e'}, {'id': 'b'}]

or:

>>> b.sort(key=lambda x: filterdict[x['id']])
>>> b
[{'id': 'a'}, {'id': 'c'}, {'id': 'e'}, {'id': 'b'}]

to sort in-place.

Edit: antonakos' solution is the best (2n) if your ids are unique (which is probably a safe assumption, but you didn't specify so I didn't want to assume). Here's a slightly shorter way of writing what he wrote, in case it helps make it clearer:

>>> d = dict((i['id'], i) for i in b)
>>> [d[key] for key in filter]
[{'id': 'a'}, {'id': 'c'}, {'id': 'e'}, {'id': 'b'}]


order = dict((v, i) for (i,v) in enumerate(filter))
sorted(b, key=lambda x:order[x['id']])

This should be somewhat more efficient that Ignacio's answer since a dictionary lookup is O(1) while index(x) is O(n).

BTW filter is the name of a built-in function, so you should not use that as a variable name.


Build a reverse dictionary and look up the values of the filter sequence:

def order_by_values_for_key(key, dictionaries, values):
    rev = {}
    for d in dictionaries:
        val = d[key]
        rev[val] = d

    return [ rev[val] for val in values ]

b = [{'id': 'a'}, {'id': 'c'}, {'id': 'b'}, {'id': 'e'}]
filter = ['a', 'c', 'e', 'b']
print order_by_values_for_key('id', b, filter)

Using the dictionary comprehensions of Python3 you can write:

def order_by_values_for_key(key, dictionaries, values):
    rev = { d[key] : d for d in dictionaries }
    return [ rev[val] for val in values ]


To add to antonakos and Keither's answers (I lack the rep to comment directly), dictionary comprehensions are also available in 2.7, and were about 1/3 faster for this example:

python -mtimeit "{k: i for i,k in enumerate(range(1000))}"
10000 loops, best of 3: 93.5 usec per loop

python -mtimeit "dict((k,i) for i,k in enumerate(range(1000)))"
10000 loops, best of 3: 158 usec per loop
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜