Sorting a Python list by two fields

前端 未结 7 1030
不思量自难忘°
不思量自难忘° 2020-11-22 07:51

I have the following list created from a sorted csv

list1 = sorted(csv1, key=operator.itemgetter(1))

I would actually like to sort the list

相关标签:
7条回答
  • 2020-11-22 08:51

    Python has a stable sort, so provided that performance isn't an issue the simplest way is to sort it by field 2 and then sort it again by field 1.

    That will give you the result you want, the only catch is that if it is a big list (or you want to sort it often) calling sort twice might be an unacceptable overhead.

    list1 = sorted(csv1, key=operator.itemgetter(2))
    list1 = sorted(list1, key=operator.itemgetter(1))
    

    Doing it this way also makes it easy to handle the situation where you want some of the columns reverse sorted, just include the 'reverse=True' parameter when necessary.

    Otherwise you can pass multiple parameters to itemgetter or manually build a tuple. That is probably going to be faster, but has the problem that it doesn't generalise well if some of the columns want to be reverse sorted (numeric columns can still be reversed by negating them but that stops the sort being stable).

    So if you don't need any columns reverse sorted, go for multiple arguments to itemgetter, if you might, and the columns aren't numeric or you want to keep the sort stable go for multiple consecutive sorts.

    Edit: For the commenters who have problems understanding how this answers the original question, here is an example that shows exactly how the stable nature of the sorting ensures we can do separate sorts on each key and end up with data sorted on multiple criteria:

    DATA = [
        ('Jones', 'Jane', 58),
        ('Smith', 'Anne', 30),
        ('Jones', 'Fred', 30),
        ('Smith', 'John', 60),
        ('Smith', 'Fred', 30),
        ('Jones', 'Anne', 30),
        ('Smith', 'Jane', 58),
        ('Smith', 'Twin2', 3),
        ('Jones', 'John', 60),
        ('Smith', 'Twin1', 3),
        ('Jones', 'Twin1', 3),
        ('Jones', 'Twin2', 3)
    ]
    
    # Sort by Surname, Age DESCENDING, Firstname
    print("Initial data in random order")
    for d in DATA:
        print("{:10s} {:10s} {}".format(*d))
    
    print('''
    First we sort by first name, after this pass all
    Twin1 come before Twin2 and Anne comes before Fred''')
    DATA.sort(key=lambda row: row[1])
    
    for d in DATA:
        print("{:10s} {:10s} {}".format(*d))
    
    print('''
    Second pass: sort by age in descending order.
    Note that after this pass rows are sorted by age but
    Twin1/Twin2 and Anne/Fred pairs are still in correct
    firstname order.''')
    DATA.sort(key=lambda row: row[2], reverse=True)
    for d in DATA:
        print("{:10s} {:10s} {}".format(*d))
    
    print('''
    Final pass sorts the Jones from the Smiths.
    Within each family members are sorted by age but equal
    age members are sorted by first name.
    ''')
    DATA.sort(key=lambda row: row[0])
    for d in DATA:
        print("{:10s} {:10s} {}".format(*d))
    

    This is a runnable example, but to save people running it the output is:

    Initial data in random order
    Jones      Jane       58
    Smith      Anne       30
    Jones      Fred       30
    Smith      John       60
    Smith      Fred       30
    Jones      Anne       30
    Smith      Jane       58
    Smith      Twin2      3
    Jones      John       60
    Smith      Twin1      3
    Jones      Twin1      3
    Jones      Twin2      3
    
    First we sort by first name, after this pass all
    Twin1 come before Twin2 and Anne comes before Fred
    Smith      Anne       30
    Jones      Anne       30
    Jones      Fred       30
    Smith      Fred       30
    Jones      Jane       58
    Smith      Jane       58
    Smith      John       60
    Jones      John       60
    Smith      Twin1      3
    Jones      Twin1      3
    Smith      Twin2      3
    Jones      Twin2      3
    
    Second pass: sort by age in descending order.
    Note that after this pass rows are sorted by age but
    Twin1/Twin2 and Anne/Fred pairs are still in correct
    firstname order.
    Smith      John       60
    Jones      John       60
    Jones      Jane       58
    Smith      Jane       58
    Smith      Anne       30
    Jones      Anne       30
    Jones      Fred       30
    Smith      Fred       30
    Smith      Twin1      3
    Jones      Twin1      3
    Smith      Twin2      3
    Jones      Twin2      3
    
    Final pass sorts the Jones from the Smiths.
    Within each family members are sorted by age but equal
    age members are sorted by first name.
    
    Jones      John       60
    Jones      Jane       58
    Jones      Anne       30
    Jones      Fred       30
    Jones      Twin1      3
    Jones      Twin2      3
    Smith      John       60
    Smith      Jane       58
    Smith      Anne       30
    Smith      Fred       30
    Smith      Twin1      3
    Smith      Twin2      3
    

    Note in particular how in the second step the reverse=True parameter keeps the firstnames in order whereas simply sorting then reversing the list would lose the desired order for the third sort key.

    0 讨论(0)
提交回复
热议问题