2

I have a dict of lists like this:

a = [
   {'p': 3, 'id': 'ab1'},
   {'p': 10, 'id': 'ab2'},
   {'p': 5, 'id': 'ab3'},
   {'p': 8, 'id': 'ab4'},
   {'id': 'ab5'}
]

I want to sort a by the value of p. If dict doesn't has p, put it behind. I tried this way:

print sorted(a, key=lambda i: i.get('p'))

and got result:

[{'id': 'ab5'}, {'p': 3, 'id': 'ab1'}, {'p': 5, 'id': 'ab3'}, {'p': 8, 'id': 'ab4'}, {'p': 10, 'id': 'ab2'}]

but want to get is:

[{'p': 3, 'id': 'ab1'}, {'p': 5, 'id': 'ab3'}, {'p': 8, 'id': 'ab4'}, {'p': 10, 'id': 'ab2'},{'id': 'ab5'}]

What can I do to get the correct result with easier way?

Georgy
  • 12,464
  • 7
  • 65
  • 73
Yao Pan
  • 524
  • 9
  • 26
  • 1
    The above code will work for `python2` but not for `python3`, as it will fail with an error `TypeError: '<' not supported between instances of 'NoneType' and 'int'` for `'id': 'ab5'}` – Devesh Kumar Singh Apr 30 '19 at 10:24

3 Answers3

4

For python2 and python3 both, you can do as follows, where I assume if the value of p is missing, I am assuming sys.maxsize value, which will ensure that if we have missing values for p, they are always at the end of the list

import sys
a = [
   {'p': 3, 'id': 'ab1'},
   {'p': 10, 'id': 'ab2'},
   {'p': 5, 'id': 'ab3'},
   {'p': 8, 'id': 'ab4'},
   {'id': 'ab5'}
]

print(sorted(a, key=lambda i: i.get('p', sys.maxsize)))

Which will give you

[{'p': 3, 'id': 'ab1'}, {'p': 5, 'id': 'ab3'}, {'p': 8, 'id': 'ab4'}, {'p': 10, 'id': 'ab2'}, {'id': 'ab5'}]

Another solution if comparing to sys.maxsize is overkill, is to separate out the dictionaries without the key p, sort the remaining dictionary which contain the key p, and then extend it by attaching the non p key dictionaries like so. This also avoids us from providing a default value for p in item.get()

import sys
a = [
   {'p': 3, 'id': 'ab1'},
   {'p': 10, 'id': 'ab2'},
   {'p': 5, 'id': 'ab3'},
   {'p': 8, 'id': 'ab4'},
   {'id': 'ab5'}
]

#Dictionaries with key p
p_items = [item for item in a if 'p' in item.keys()]

#Dictionaries not with key p
non_p_items = [item for item in a if 'p' not in item.keys()]

#Sort the p-key dictionaries
result = sorted(p_items, key=lambda i: i.get('p'))

#Attach non p-key dictionay items at end
result.extend(non_p_items)

print(result)
Devesh Kumar Singh
  • 20,259
  • 5
  • 21
  • 40
2

Use

a = [
   {'p': 3, 'id': 'ab1'},
   {'p': 10, 'id': 'ab2'},
   {'p': 5, 'id': 'ab3'},
   {'p': 8, 'id': 'ab4'},
   {'id': 'ab5'}
]

print(sorted(a, key=lambda i: i.get('p', "NA"))) #Setting a string as a default value. 
#python 3  --> https://stackoverflow.com/questions/49829732/sorting-a-mixed-list-of-ints-and-strings
print(sorted(a, key=lambda v: (isinstance(v.get('p', "NA"), str), v.get('p', "NA"))))

Output:

[{'p': 3, 'id': 'ab1'}, {'p': 5, 'id': 'ab3'}, {'p': 8, 'id': 'ab4'}, {'p': 10, 'id': 'ab2'}, {'id': 'ab5'}]
Rakesh
  • 81,458
  • 17
  • 76
  • 113
2

Here's an option that I prefer that doesn't use lambda:

import sys
from operator import methodcaller

a = [
   {'p': 3, 'id': 'ab1'},
   {'p': 10, 'id': 'ab2'},
   {'p': 5, 'id': 'ab3'},
   {'p': 8, 'id': 'ab4'},
   {'id': 'ab5'}
]

print(sorted(a, key=methodcaller('get', 'p', sys.maxsize)))
Jab
  • 26,853
  • 21
  • 75
  • 114