简体   繁体   中英

Using Insertion sort to sort list of dictionary

This sort is For Educational purposes. no build in sort is allowed.

If you think my question and the answer helped, please vote me and the first answer people: @JF Sebastian

I have found this on SO -- " How to do an insertion sort on a list of dictionaries in python? "

but that answer does no seem right.

Using the answer code of the above question raises this error:

TypeError: list indices must be integers, not dict

Example :

lst = [{'a':20, 'b':30, 'c':25, 'd': 600},{'a':60, 'b':10, 'c':43, 'd': 20}]`

to sort using insertion sort, for example sort b , we should get

[{'a':60, 'b':10, 'c':43, 'd': 20},{'a':20, 'b':30, 'c':25, 'd': 600}]

But my code gets

[{'b': 10, 'c': 25, 'a': 20, 'd': 600}, {'b': 30, 'c': 43, 'a': 60, 'd': 20}]

he replace the key and value in the list of dictionary

Here is my Code:

def insertionSort(allData, key):

    for i in range(len(allData)):
        temp = allData[i][key]
        j = i
        while j > 0 and temp < allData[j - 1][key]:
               allData[j][key] = allData[j - 1][key]
               j = j - 1
        allData[j][key] = temp

My homework sort result:

{'snow': 19.2, 'minT': -10.8, 'month': 12, 'maxT': 9.0, 'rain': 45.0, 'year': 2003, 'meanT': -0.1, 'yearmonth': 193801}
{'snow': 35.6, 'minT': -20.0, 'month': 1, 'maxT': 8.9, 'rain': 34.3, 'year': 1974, 'meanT': -5.9, 'yearmonth': 193802}
{'snow': 0.0, 'minT': 9.7, 'month': 8, 'maxT': 34.8, 'rain': 20.8, 'year': 2007, 'meanT': 22.4, 'yearmonth': 193803}`

After sorting the yearmonth , they replace the yearmonth from small to big, but not changing the dictionary.

Why does this happen, and how shoudl I change it?

==================================

Answer:

After some basic copy of 'JF Sebastian' code I find that I can't directly using the code

sort(a,b)

output:

TypeError: 'str' object is not callable

I should use sort(a,b=lambda x: x['thekey']) then, JFS makes a new function to make it work.

I also find another way: just change JFS code line 5:

from

    if key(L[j]) <= key(d):

to

    if L[j][key] <= d[key]:

THEN EVERYTHING WORKS! Hope this can help other people too, and those using google and doing the same assignment as me.

Try to write an insertion sort procedure that works with integer lists. Then it is easy to modify it to accept a key parameter, to allow arbitrary comparisons:

def insertion_sort_impl(L, *, key):
    for i in range(1, len(L)): # loop-invariant: `L[:i]` is sorted
        d = L[i]
        for j in range(i - 1, -1, -1): 
            if key(L[j]) <= key(d): 
               break
            L[j + 1] = L[j]
        else: # `key(L[j]) > key(d)` for all `j`
            j -= 1
        L[j + 1] = d

Example:

lst = [{'a':20, 'b':30, 'c':25, 'd': 600},{'a':60, 'b':10, 'c':43, 'd': 20}]
insertion_sort_impl(lst, key=lambda x: x['d']) # sort by `d` key
print(lst)

Output

[{'a': 60, 'c': 43, 'b': 10, 'd': 20}, {'a': 20, 'c': 25, 'b': 30, 'd': 600}]

Note: ignore the order of keys inside dictionary. It may change from run to run, but the dictionary with the key d that has lesser value will always be on the left.

The name key for insertion_sort_impl() function is unrelated to dictionary key. It is a naming convention that is used by many builtin functions; key is used to get a value to be used for comparison:

>>> max([1, 2], key=lambda x: -x)
1
>>> min([1, -2], key=lambda x: x*x)
1
>>> sorted([-1, 0, 1], key=lambda x: x*x-x)
[0, 1, -1]

To understand, why the loop is started at 1 , you could read the general description of Insertion sort .

If the interface of insertion_sort function is fixed then you could define it in terms of insertion_sort_impl :

from operator import itemgetter

def insertion_sort(L, key):
    return insertion_sort_impl(L, key=itemgetter(key))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM