Hello everyone,

I'm learning Python and I have a few questions about Dictionaries and sorting them by a value other than the key.

Lets say, for example, I have a Dictionary full of data that needs to be sorted on the 3rd value instead of the key. How would I go about doing this? I have tried just the .sort() and that just sorts by the key.

So if,

Data = [('1023', ['Name', '3.1', 'SomeData']), ('1000', )

how would I sort "Data" by the value where 3.1 is?

Please forgive my lack of correct terminology if I am missing some somewhere! Any help would be greatly appreciated.

Dani AI

Generated

Good pointers from and : the clean approach is to sort with a key function rather than writing a manual sort. Two practical follow-ups that the thread didn’t emphasise are important for real data: inner values that look like numbers must be converted to numbers (otherwise sorting is lexicographic), and mapping order behavior changed in modern Python (convert-back-to-dict will preserve insertion order on current Python). (docs.python.org)

A compact, robust pattern that handles numeric inner values and missing/bad data (avoid repeating any code already in the thread):

def safe_float(s):
    try:
        return float(s)
    except (TypeError, ValueError):
        return float('inf')   # treat bad/missing as "largest" so they sort last

# keys ordered by the 2nd element of the value-list, numerically
keys_sorted = sorted(data, key=lambda k: safe_float(data[k][1]))

# sorted list of the GPA strings (numeric order)
sorted_gpas = [v[1] for v in sorted(data.values(), key=lambda v: safe_float(v[1]))]

If the goal is an ordered mapping rather than just a sorted list, create an ordered mapping explicitly. For older Python versions use OrderedDict; on Python 3.7+ a normal dict preserves insertion order, so dict(sorted(...)) will keep the sort order. (docs.python.org)

For multi-key sorts (for example: primary by major, secondary by GPA descending) either sort with a tuple key or rely on stability to do multiple passes. Example key form:

sorted_items = sorted(data.items(),
                      key=lambda kv: (kv[1][2], -safe_float(kv[1][1])))

Sort stability (Python’s sort is stable) makes multi-pass sorts predictable. For small performance wins or clearer extraction of fields, consider operator.itemgetter for hot loops. (docs.python.org)

Troubleshooting notes: convert strings to numbers for numeric comparisons, handle missing/malformed with a sentinel in the key, and remember that converting back into a plain dict only preserves the order reliably on Python 3.7+.

Recommended Answers

All 14 Replies

It doesn't look like you're using a dictionary. It appears that you have a list of tuples, and each tuple consists of a string and a list.

I can't think of a built in way to do this. You might have to look at writing your own version of a bubble sort or something. To do that, you would go through the entire outer list and compare the 2nd element of the inner list. If they are not in the correct order, then switch them around. Repeat until you can go through the whole list without having to make any switches.

You can use the 'key' argument to 'sort' or 'sorted'. Here, Data is a list. You can write

def my_key(item):
    return item[1][1] # this returns 3.1 if item is ('1023', ['Name', '3.1', 'SomeData'])

Data.sort(key = my_key)

Data is easily turned into a dictionary:

Data = dict(Data)
L = sorted(Data.items(), key = my_key)

Similar to Gribouillis' code, but uses lambda as a helper function ...

# sort a more complex combination of lists/tuples:
mylist = [
(1, ['a', '3.1', 'ad']),
(2, ['b', '4.0', 'bd']),
(3, ['c', '2.5', 'cd']),
]
# sort by item at index [1][1] of each tuple
# using a helper function like lambda
newlist = sorted(mylist, key=lambda tup: tup[1][1])

print newlist

"""my result (made pretty) -->
[
(3, ['c', '2.5', 'cd']),
(1, ['a', '3.1', 'ad']), 
(2, ['b', '4.0', 'bd'])
]
"""

You can also use a somewhat older concept of sorting, the Schwartzian transform algorithm. This is made very easy with Python's list comprehension ...

# sort a more complex combination of lists/tuples:
    
mylist = [
(1, ['a', '3.1', 'ad']),
(2, ['b', '4.0', 'bd']),
(3, ['c', '2.5', 'cd']),
]

# use the Schwartzian transform algorithm
# temporarily put a copy of indexed item in front
templist = [(x[1][1], x) for x in mylist]
templist.sort()
# remove temporary front item after sorting
newlist = [val for (temp, val) in templist]

print newlist

"""my result (made pretty) -->
[
(3, ['c', '2.5', 'cd']),
(1, ['a', '3.1', 'ad']), 
(2, ['b', '4.0', 'bd'])
]
"""

I've tried all three of these methods, however, I haven't been able to get them to work correctly for my particular set of data. I believe I may have pasted my dictionary incorrectly.

Here is what I have in my dictionary:
data = {'1234': , '1000': , '1023': , '1001': , '9000': }

Would this have affected the methods of sorting that were mentioned?

So you have a dictionary after all!

Dictionaries are indexed by key and the keys are in a hash order for fast lookups. They will always display in this order! If you want to sort by a certain item, you have to convert them to lists. I thought that is what you did.

data_dict = {
'1234': ['Matt', '2.5', 'CS'], 
'1000': ['John', '4.0', 'Music'], 
'1023': ['Aaron', '3.1', 'PreMed'], 
'1001': ['Paul', '3.9', 'Music'], 
'9000': ['Kris', '3.5', 'Business']
}

# convert dictionary to a list of tuples
data_list = [(key, val) for key, val in data_dict.items()]

print(data_list)
"""the raw list -->
[
('1234', ['Matt', '2.5', 'CS']), 
('9000', ['Kris', '3.5', 'Business']), 
('1001', ['Paul', '3.9', 'Music']), 
('1023', ['Aaron', '3.1', 'PreMed']), 
('1000', ['John', '4.0', 'Music'])
]
"""

# now you can sort the list by item[1][1]
sorted_list = sorted(data_list, key=lambda tup: tup[1][1])

print(sorted_list)
"""result sorted by item[1][1] -->
[
('1234', ['Matt', '2.5', 'CS']), 
('1023', ['Aaron', '3.1', 'PreMed']), 
('9000', ['Kris', '3.5', 'Business']), 
('1001', ['Paul', '3.9', 'Music']), 
('1000', ['John', '4.0', 'Music'])]
"""

# however if you go back to a dictionary
new_dict = dict(sorted_list)

print(new_dict)
"""result is the dictionary order again -->
{
'1234': ['Matt', '2.5', 'CS'], 
'1000': ['John', '4.0', 'Music'], 
'1001': ['Paul', '3.9', 'Music'], 
'1023': ['Aaron', '3.1', 'PreMed'], 
'9000': ['Kris', '3.5', 'Business']}
"""

So, if you want to do any processing that needs a sorted order, to have to convert your dictionary to a temporary list.

Ah ha! That worked! Now, since you're putting it in a list to sort the values, is there also a way to separate those values you are sorting on into a list by themselves?

Ah ha! That worked! Now, since you're putting it in a list to sort the values, is there also a way to separate those values you are sorting on into a list by themselves?

Is this what you mean?

>>> my_dict = {'a':1, 'b':2, 'c':3}
>>> my_dict.keys()
['a', 'c', 'b']
>>> my_dict.values()
[1, 3, 2]
>>>

Is this what you mean?

>>> my_dict = {'a':1, 'b':2, 'c':3}
>>> my_dict.keys()
['a', 'c', 'b']
>>> my_dict.values()
[1, 3, 2]
>>>

Not exactly, I'd like to take the values that are being sorted on and put them into a new list/dict etc.

For example, extracting only these values from the dictionary above:
new_list = ('2.5','3.5','3.9','4.0','3.1')

Not exactly, I'd like to take the values that are being sorted on and put them into a new list/dict etc.

For example, extracting only these values from the dictionary above:
new_list = ('2.5','3.5','3.9','4.0','3.1')

Okay... how about this:

>>> data_dict = {
... '1234': ['Matt', '2.5', 'CS'], 
... '1000': ['John', '4.0', 'Music'], 
... '1023': ['Aaron', '3.1', 'PreMed'], 
... '1001': ['Paul', '3.9', 'Music'], 
... '9000': ['Kris', '3.5', 'Business']
... }
>>> [data[1] for data in data_dict.values()]
['2.5', '3.5', '3.9', '3.1', '4.0']
>>>

That's just a quick list comprehension that will extract the second element (at index 1) from each list (which is stored as the "value" part of each dictionary entry.

Hah, this thread nudged me in studying more on sorting (more complex data structures) with Python. So I took a swing at it. These are the ones I came up with:

data = {'1234': ['Matt', '2.5', 'CS'],
        '1000': ['John', '4.0', 'Music'],
        '1023': ['Aaron', '3.1', 'PreMed'],
        '1001': ['Paul', '3.9', 'Music'],
        '9000': ['Kris', '3.5', 'Business']}

# Sort by name, print keys only.
print sorted(data, key=lambda x: data[x][0])

# Sort by name, print keys and values.
a = sorted(data, key=lambda x: data[x][0])
for i in a:
    print i, '->', data[i]

# Sort by name, print values only.
print map(data.get, sorted(data, key=lambda x: data[x][0]))

# Sort by names, print keys and values.
print ["%s -> %s" % (i, data.get(i)) for i in sorted(data, key=lambda x: data[x][0])]

These all sort by name, but you can change the 0 to 1 to sort by the GPA (at least that is what I think it is), and change it to 2 to sort by category/group (or whatever it is). I actually worked on this for at least an hour (maybe more, kind of lost track of time), just kept finding additional information or more interesting python methods.

The nice thing about Xydric's approach is that it doesn't lose the important connectivity with the original dictionary via the keys ...

data = {
'1234': ['Matt', '2.5', 'CS'],
'1000': ['John', '4.0', 'Music'],
'1023': ['Aaron', '3.1', 'PreMed'],
'1001': ['Paul', '3.9', 'Music'],
'9000': ['Kris', '3.5', 'Business']
}

# sort by index in value, here GPA values at index 1
# gives a list of keys in that specified order
key_list = sorted(data, key=lambda x: data[x][1])

gpa_list = [data[key][1] for key in key_list]

print( gpa_list )  # ['2.5', '3.1', '3.5', '3.9', '4.0']

# now if you wanted to know who got the 2.5
# it should be the first key in the key_list too (at index=0)
print( data[key_list[0]] )     # ['Matt', '2.5', 'CS']
# or ...
print( data[key_list[0]][0] )  # Matt

The nice thing about Xydric's approach is that it doesn't lose the important connectivity with the original dictionary via the keys ...

data = {
'1234': ['Matt', '2.5', 'CS'],
'1000': ['John', '4.0', 'Music'],
'1023': ['Aaron', '3.1', 'PreMed'],
'1001': ['Paul', '3.9', 'Music'],
'9000': ['Kris', '3.5', 'Business']
}

# sort by index in value, here GPA values at index 1
# gives a list of keys in that specified order
key_list = sorted(data, key=lambda x: data[x][1])

gpa_list = [data[key][1] for key in key_list]

print( gpa_list )  # ['2.5', '3.1', '3.5', '3.9', '4.0']

# now if you wanted to know who got the 2.5
# it should be the first key in the key_list too (at index=0)
print( data[key_list[0]] )     # ['Matt', '2.5', 'CS']
# or ...
print( data[key_list[0]][0] )  # Matt

I couldn't get the syntax on the print (data[key_list[0]) to work, should there be another "]" there?

Also, could you use this method, for example, to search by "major"? So if you wanted a list of 'Music', how would you go about this?

I couldn't get the syntax on the print (data[key_list[0]) to work, should there be another "]" there?

Also, could you use this method, for example, to search by "major"? So if you wanted a list of 'Music', how would you go about this?

If you look at my code with a sharp eye you will find that all [] match.

On part 2 ...

# sorted() handles dictionaries directly
# for indexed sorting

data = {
'1234': ['Matt', '2.5', 'CS'],
'1000': ['John', '4.0', 'Music'],
'1023': ['Aaron', '3.1', 'PreMed'],
'1001': ['Paul', '3.9', 'Music'],
'9000': ['Kris', '3.5', 'Business']
}

# sort by index in value, here by 'Major' at index 2
# gives a list of keys in that specified order
key_list = sorted(data, key=lambda x: data[x][2])

major_list = [data[key][2] for key in key_list]

print( major_list )  # ['Business', 'CS', 'Music', 'Music', 'PreMed']

# optionally create a sub dictionary of all the music majors
music_major_dict = dict([(key, data[key]) \
    for key in key_list if data[key][2] == 'Music'])

print( music_major_dict )
"""
{'1001': ['Paul', '3.9', 'Music'], '1000': ['John', '4.0', 'Music']}
"""
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.