tags:

views:

103

answers:

5

I have a list of 4 dicts (always 4) that look something like this:

[{'id':'1','name':'alfa'},{'id':'2','name':'bravo'},{'id':'3','name':'charlie'},{'id':'4','name':'delta'}]

I know exactly the order I want them in, which is:

2, 3, 1, 4

what's the simplest way of reordering them?

+2  A: 
the_list.sort(key=lambda x: (3, 1, 2, 4)[int(x["id"])-1])

Update0

A new much simpler answer

the_list = [the_list[i - 1] for i in (2, 3, 1, 4)]

This way the OP can see his desired ordering, and there's no silliness with sorting, which is not required here. It's probably fast too.

Matt Joiner
I have no idea why you'd want to do this...
Matt Joiner
But it does not show where (3, 1, 2, 4) is coming from...
tokland
I might add that although this is complex for some people, looking at the other answers, this one is probably both faster and more straightforward.
Matt Joiner
The array (3, 1, 2, 4) is indexed into using the `id - 1` of the dicts, to get the order you requested.
Matt Joiner
+2  A: 

If it's always four, and you always know the order, just simply like this:

lst = [{...},{...},{...},{...}]
ordered = [lst[1],lst[2],lst[0],lst[3]]

If you meant to sort them by 'id', in that order:

ordered = sorted(lst, key=lambda d: [2,3,1,4].index(int(d['id'])))

Note that index() is O(n) but doesn't require you to build a dictionary. So for small inputs, this may actually be faster. In your case, there are four elements, ten comparisons are guaranteed. Using timeit, this snippet runs 10% faster than the dictionary based solution by tokland... but it doesn't really matter since neither will likely be significant.

Stephen
thanks - forgot about indexes, I was calling the first item 1 etc hence it through a index out of range error.
chrism
Isn't this code calling list.index (O(n)) for each element in the lst to be ordered? it won't perform well with large lists.
tokland
@tokland : Yes, it's `O(n)` on 4 elements. Max of 16. I suppose that's worthy of a downvote... but his question was solved by the first solution.
Stephen
@Stephen, I thought that was the way to go, downvote but reasoning why. But I am sorry it upset you, but I cannot remove the vote unless the question is edited. Which one was the first solution? Alex Martelli's?
tokland
@tokland : I appreciate that you reasoned why. I'm not upset that you downvoted me... I just wanted to point out that you're arguing an asymptotic complexity with a constant 4 elements. Asymptotic complexity is for larger (or variable sized) inputs, he guaranteed 4 elements. For something that small, it's usually faster to avoid building a O(1) data structure. I added a note that this was linear , but I benchmarked it as faster than your solution.
Stephen
What I meant by "the first solution" was what I wrote to reorder the tuple elements (before I suggested using index). As far as overall answer ordering, Matt Joiner was first, I was second to answer. You can see this by sorting the answers by "oldest".
Stephen
+2  A: 

Here's a pretty general function to impose a wanted order (any key value not in the wanted order is placed at the end of the resulting list, in arbitrary sub-order):

def ordered(somelist, wantedorder, keyfunction):
    orderdict = dict((y, x) for x, y in enumerate(wantedorder))
    later = len(orderdict)
    def key(item):
        return orderdict.get(keyfunction(item), later)
    return sorted(somelist, key=key)

You'd be using it as

import operator
sortedlist = ordered(dictlist, ('2', '3', '1', '4'),
                     operator.itemgetter('id'))
Alex Martelli
A: 

If you want to re-order without regard for content of the dicts:

>>> order = 2, 3, 1, 4
>>> d = [{'id':'1','name':'alfa'},{'id':'2','name':'bravo'},{'id':'3','name':'charlie'},{'id':'4','name':'delta'}]
>>> index = dict(enumerate(dd))
>>> [index[i-1] for i in order]
[{'id': '2', 'name': 'bravo'}, {'id': '3', 'name': 'charlie'}, {'id': '1', 'name': 'alfa'}, {'id': '4', 'name': 'delta'}]

If you want to base your sorting on the 'id' of the dicts:

>>> sorted(d, key=lambda x: order.index(int(x['id'])))
[{'id': '2', 'name': 'bravo'}, {'id': '3', 'name': 'charlie'}, {'id': '1', 'name': 'alfa'}, {'id': '4', 'name': 'delta'}]
SilentGhost
+2  A: 

A non-generalized solution:

lst = [{'id':'1','name':'alfa'},{'id':'2','name':'bravo'},{'id':'3','name':'charlie'},{'id':'4','name':'delta'}]
order = ["2", "3", "1", "4"]
indexes = dict((idfield, index) for (index, idfield) in enumerate(order))
print sorted(lst, key=lambda d: indexes[d["id"]])
# [{'id': '2', 'name': 'bravo'}, {'id': '3', 'name': 'charlie'}, {'id': '1', 'name': 'alfa'}, {'id': '4', 'name': 'delta'}]

And here generalized:

def my_ordered(it, wanted_order, key):
    indexes = dict((value, index) for (index, value) in enumerate(wanted_order))
    return sorted(it, key=lambda x: indexes[key(x)])

import operator  
print my_ordered(lst, order, operator.itemgetter("id"))
tokland
Well, this appears just to be Alex Martelli's answer without taking in account elements in input but not in wanted_order.
tokland