How to Sort the dict by value in Python?

Viewed 369

I just want to sort the dictionary by the values but little different way.

Here is my dictionary

issues = {
    1: {'title': 'Title 1','level': 'low'}, 
    2: {'title': 'Title 2','level': 'medium'}, 
    3: {'title': 'Title 3','level': 'high'}, 
    4: {'title': 'Title 4','level': 'low'}, 
    5: {'title': 'Title 5','level': 'medium'}, 
    6: {'title': 'Title 6','level': 'high'}
}

I want to sort the above issues by its level (low, medium, high).

sorted_issues = {
    1: {'title': 'Title 1','level': 'low'}, 
    4: {'title': 'Title 4','level': 'low'}, 
    2: {'title': 'Title 2','level': 'medium'}, 
    5: {'title': 'Title 5','level': 'medium'}, 
    3: {'title': 'Title 3','level': 'high'}, 
    6: {'title': 'Title 6','level': 'high'}
}

I got many examples but all those had only one value in the value section. But here values are not single value its dictionary

Thanks in advance.

8 Answers

You can do this

from collections import OrderedDict
issues = {
    1: {'title': 'Title 1','level': 'low'},
    2: {'title': 'Title 2','level': 'medium'},
    3: {'title': 'Title 3','level': 'high'},
    4: {'title': 'Title 4','level': 'low'},
    5: {'title': 'Title 5','level': 'medium'},
    6: {'title': 'Title 6','level': 'high'}
}
priorities = {
    'low': 1,
    'medium': 2,
    'high': 3
}
x = OrderedDict(sorted(issues.items(), key = lambda item: priorities[item[1]['level']]))
for key, value in x.items():
    print(f"{key}, {value}")
'''
1, {'title': 'Title 1', 'level': 'low'}
4, {'title': 'Title 4', 'level': 'low'}
2, {'title': 'Title 2', 'level': 'medium'}
5, {'title': 'Title 5', 'level': 'medium'}
3, {'title': 'Title 3', 'level': 'high'}
6, {'title': 'Title 6', 'level': 'high'}
'''
print(x)
'''
OrderedDict([(1, {'title': 'Title 1', 'level': 'low'}), (4, {'title': 'Title 4', 'level': 'low'}), (2, {'title': 'Title 2', 'level': 'medium'}), (5, {'title': 'Title 5', 'level': 'medium'}), (3, {'title': 'Title 3', 'level': 'high'}), (6, {'title': 'Title 6', 'level': 'high'})])
'''

Dictionaries mantain insertion order for pythons 3.7>, otherwise you have OrderedDict. To sort the dictionaries in such way, you'll need to define a lookup table to rank the level field, and then sort using tuples (level_lookup, title) (assuming you also need to sort by title, otherwise only the first term is necessary).

Also note that you'll need to cast the numerical part in title to integer, otherwise the sorting will be lexicographic, which implies that 9 > 10:

mapper = {'low':0, 'medium':1, 'high':2}
dict(sorted(issues.items(), key=lambda x: (mapper[x[1]['level']], 
                                           int(x[1]['title'].rsplit(maxsplit=1)[1]))))

{1: {'title': 'Title 1', 'level': 'low'},
 4: {'title': 'Title 4', 'level': 'low'},
 2: {'title': 'Title 2', 'level': 'medium'},
 5: {'title': 'Title 5', 'level': 'medium'},
 3: {'title': 'Title 3', 'level': 'high'},
 6: {'title': 'Title 6', 'level': 'high'}}

If further sorting by the title isn't necessary, you only need:

dict(sorted(issues.items(), key=lambda x: mapper[x[1]['level']]))

Even without maintaining the order of input we can write a custom function for sorting based on the keys or labels provided (level in your case),

import pprint
issues = {
    1: {'title': 'Title 1','level': 'low'}, 
    2: {'title': 'Title 2','level': 'medium'}, 
    3: {'title': 'Title 3','level': 'high'}, 
    4: {'title': 'Title 4','level': 'low'}, 
    5: {'title': 'Title 5','level': 'medium'}, 
    6: {'title': 'Title 6','level': 'high'}
}

def customSort(data, labels):
    temp = {}
    inx = 0
    for label in labels:
        for k,v in data.items():
            if v["level"] == label:
                inx += 1
                temp[inx] = v.copy()
    return temp

labels = ["low","medium","high"]
issues_sorted = customSort(issues,labels)
pprint.pprint(issues_sorted)

Output:

{1: {'level': 'low', 'title': 'Title 1'},
 2: {'level': 'low', 'title': 'Title 4'},
 3: {'level': 'medium', 'title': 'Title 2'},
 4: {'level': 'medium', 'title': 'Title 5'},
 5: {'level': 'high', 'title': 'Title 3'},
 6: {'level': 'high', 'title': 'Title 6'}}

Since Python version 3.7 dicts keep the insertion order of your elements. In previous versions of python this was not the case.

Therefore you can order the items, sort them depending on your criteria and insert them afterwards in a (new) dict.

Or use OrderedDict:

import collections

def custom_sort(item):
    prio = ['low', 'medium', 'high']
    return prio.index(item[-1]["level"])

issues = {
    1: {'title': 'Title 1','level': 'low'},
    2: {'title': 'Title 2','level': 'medium'},
    3: {'title': 'Title 3','level': 'high'},
    4: {'title': 'Title 4','level': 'low'},
    5: {'title': 'Title 5','level': 'medium'},
    6: {'title': 'Title 6','level': 'high'}
}

res = collections.OrderedDict(sorted(issues.items(), key=custom_sort))
print(res)

Please try the code below:

issues = {
        1: {'title': 'Title 1','level': 'low'}, 
        2: {'title': 'Title 2','level': 'medium'}, 
        3: {'title': 'Title 3','level': 'high'}, 
        4: {'title': 'Title 4','level': 'low'}, 
        5: {'title': 'Title 5','level': 'medium'}, 
        6: {'title': 'Title 6','level': 'high'}
    }
sortlist=['low','medium','high']
    
sorted_issues={k: v for k, v in sorted(issues.items(), key=lambda item: sortlist.index(item[1]['level']))}
print(sorted_issues)

Output:

{1: {'title': 'Title 1', 'level': 'low'}, 
4: {'title': 'Title 4', 'level': 'low'}, 
2: {'title': 'Title 2', 'level': 'medium'}, 
5: {'title': 'Title 5', 'level': 'medium'}, 
3: {'title': 'Title 3', 'level': 'high'}, 
6: {'title': 'Title 6', 'level': 'high'}}

This is a bit of a hack since the last letters of 'low', 'medium', and 'high' have a rather unintentional alphabetized order. But a one liner non the less:

sorted_issues = {k: v for k, v in  sorted(issues.items(), key=lambda kv: kv[1]['level'][-1], reverse=True)}

You can try

issues = {
    1: {'title': 'Title 1','level': 'low'},
    2: {'title': 'Title 2','level': 'medium'},
    3: {'title': 'Title 3','level': 'high'},
    4: {'title': 'Title 4','level': 'low'},
    5: {'title': 'Title 5','level': 'medium'},
    6: {'title': 'Title 6','level': 'high'}
}

levels_priorty = ['low', 'medium', 'high']
res = dict(sorted(issues.items(), key=lambda x: [levels_priorty.index(x[1]['level']), int(x[1]['title'].replace("Title ", ""))]))
print(res)

Output

{1: {'title': 'Title 1', 'level': 'low'}, 
4: {'title': 'Title 4', 'level': 'low'}, 
2: {'title': 'Title 2', 'level': 'medium'}, 
5: {'title': 'Title 5', 'level': 'medium'}, 
3: {'title': 'Title 3', 'level': 'high'}, 
6: {'title': 'Title 6', 'level': 'high'}}

This can easily be achieved without importing or or installing additional modules in just a few short lines of code.

Because below python 3.1 dictionaries can't remember their order. https://docs.python.org/3.1/whatsnew/3.1.html

You can simply order this dict like this:

issues = {
            1: {'title': 'Title 1','level': 'low'}, 
            2: {'title': 'Title 2','level': 'medium'}, 
            3: {'title': 'Title 3','level': 'high'}, 
            4: {'title': 'Title 4','level': 'low'}, 
            5: {'title': 'Title 5','level': 'medium'}, 
            6: {'title': 'Title 6','level': 'high'}
        }


# CONVERT THE issues DICTIONARY INTO A LIST
issues_list = [{k: v} for k, v in issues.items()]

# A FUNCTION THAT SELECTS THE SORTING ELEMENT 
level = lambda issue: issue.items()[0][1]['level']

# WE USE THIS DICTIONARY TO CONVERT THE SORTING ELEMENT INTO A NUMBER
levels = {'low':1, 'medium':2, 'high':3}

# A FUNCTION THAT RETURNS A NUMBER TO BE SORTED BY THE SORT FUNCTION
dict_element = lambda d: levels[level(d)]

# NOW SORT THE LIST BASED ON LEVEL
issues_list.sort(key=dict_element)

# PRINT SORTED LIST
for i in issues_list:
    print i


# RESULT
# {1: {'level': 'low', 'title': 'Title 1'}}
# {4: {'level': 'low', 'title': 'Title 4'}}
# {2: {'level': 'medium', 'title': 'Title 2'}}
# {5: {'level': 'medium', 'title': 'Title 5'}}
# {3: {'level': 'high', 'title': 'Title 3'}}
# {6: {'level': 'high', 'title': 'Title 6'}} 
Related