views:

211

answers:

6

For example, this is my list of dictionaries:

[{'name': 'John', 'color': 'red'  },
 {'name': 'Bob',  'color': 'green'},
 {'name': 'Tom',  'color': 'blue' }]

Based on the list ['blue', 'red', 'green'] I want to return the following:

[{'name': 'Tom',  'color': 'blue' },
 {'name': 'John', 'color': 'red'  },
 {'name': 'Bob',  'color': 'green'}]
+2  A: 

Update:

>>> list_ = [{'c': 3}, {'c': 2}, {'c': 5}]
>>> mp = [3, 5, 2]
>>> sorted(list_, cmp=lambda x, y: cmp(mp.index(x.get('c')), mp.index(y.get('c'))))
[{'c': 3}, {'c': 5}, {'c': 2}]
iElectric
Will get them in the order blue, green, red, which wasn't what was asked for.
Lennart Regebro
I updated my answer.
iElectric
Downvote retracted.
Chris Lutz
A: 

Here is a simple loop function:

# Heres the people:
people = [{'name':'John', 'color':'red'}, 
          {'name':'Bob', 'color':'green'}, 
          {'name':'Tom', 'color':'blue'}] 

# Now we can make a method to get people out in order by color:
def orderpeople(order):
    for color in order:
        for person in people:
            if person['color'] == color:
                yield person

order = ['blue', 'red', 'green']
print(list(orderpeople(order)))

Now that will be VERY slow if you have many people. Then you can loop through them only once, but build an index by color:

# Here's the people:
people = [{'name':'John', 'color':'red'}, 
          {'name':'Bob', 'color':'green'}, 
          {'name':'Tom', 'color':'blue'}] 

# Now make an index:
colorindex = {}
for each in people:
    color = each['color']
    if color not in colorindex:
        # Note that we want a list here, if several people have the same color.
        colorindex[color] = [] 
    colorindex[color].append(each)

# Now we can make a method to get people out in order by color:
def orderpeople(order):
    for color in order:
        for each in colorindex[color]:
            yield each

order = ['blue', 'red', 'green']
print(list(orderpeople(order)))

This will be quite fast even for really big lists.

Lennart Regebro
It can be worth noting that this solution handles cases when the list of people is bigger than the list of colors, and also when several people have the same color. If this is a homework question (as I suspect) then this may not be necessary. But if it is not a homework question, and a question about a generic usecase, than this will handle those generic usecases.
Lennart Regebro
A: 

Given:

people = [{'name':'John', 'color':'red'}, {'name':'Bob', 'color':'green'}, {'name':'Tom', 'color':'blue'}]
colors = ['blue', 'red', 'green']

you can do something like this:

def people_by_color(people, colors):
  index = {}
  for person in people:
    if person.has_key('color'):
      index[person['color']] = person       
  return [index.get(color) for color in colors]

If you're going to do this many times with the same list of dictionaries but different lists of colors you'll want to split the index building out and keep the index around so you don't need to rebuild it every time.

Laurence Gonsalves
+4  A: 

This might be a little naieve, but it works:

data = [
    {'name':'John', 'color':'red'},
    {'name':'Bob', 'color':'green'},
    {'name':'Tom', 'color':'blue'}
]
colors = ['blue', 'red', 'green']
result = []

for c in colors:
    result.extend([d for d in data if d['color'] == c])

print result
harto
I'd say simple and straightforward rather than naive
foosion
Thanks! Worked like a charm.
teggy
+1  A: 

You can sort using any custom key function.

>>> people = [
    {'name': 'John', 'color': 'red'},
    {'name': 'Bob', 'color': 'green'},
    {'name': 'Tom', 'color': 'blue'},
]
>>> colors = ['blue', 'red', 'green']
>>> sorted(people, key=lambda person: colors.index(person['color']))
[{'color': 'blue', 'name': 'Tom'}, {'color': 'red', 'name': 'John'}, {'color': 'green', 'name': 'Bob'}]

list.index takes linear time though, so if the number of colors can grow, then convert to a faster key lookup.

>>> colorkeys = dict((color, index) for index, color in enumerate(colors))
>>> sorted(people, key=lambda person: colorkeys[person['color']])
[{'color': 'blue', 'name': 'Tom'}, {'color': 'red', 'name': 'John'}, {'color': 'green', 'name': 'Bob'}]
Coady
+1: the key= approach is intrinsically faster than the cmp= one (thankfully dropped in Py3!) and the colorkeys building is a worthy optimization -- so basically there's nothing left for me to add in a separate answer (well done!-).
Alex Martelli
+1  A: 

Riffing on Harto's solution:

>>> from pprint import pprint
>>> [{'color': 'red', 'name': 'John'},
...  {'color': 'green', 'name': 'Bob'},
...  {'color': 'blue', 'name': 'Tom'}]
[{'color': 'red', 'name': 'John'}, {'color': 'green', 'name': 'Bob'}, {'color':
'blue', 'name': 'Tom'}]
>>> data = [
...     {'name':'John', 'color':'red'},
...     {'name':'Bob', 'color':'green'},
...     {'name':'Tom', 'color':'blue'}
... ]
>>> colors = ['blue', 'red', 'green']
>>> result = [d for d in data for c in colors if d['color'] == c]
>>> pprint(result)
[{'color': 'red', 'name': 'John'},
 {'color': 'green', 'name': 'Bob'},
 {'color': 'blue', 'name': 'Tom'}]
>>>

The main difference is in using a list comprehension to build result.

Edit: What was I thinking? This clearly calls out for the use of the any() expression:

>>> from pprint import pprint
>>> data = [{'name':'John', 'color':'red'}, {'name':'Bob', 'color':'green'}, {'name':'Tom', 'color':'blue'}]
>>> colors = ['blue', 'red', 'green']
>>> result = [d for d in data if any(d['color'] == c for c in colors)]
>>> pprint(result)
[{'color': 'red', 'name': 'John'},
 {'color': 'green', 'name': 'Bob'},
 {'color': 'blue', 'name': 'Tom'}]
>>>
hughdbrown
Cool. Wasn't sure if that kind of nested list comprehension existed or not!
harto