Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 35 You must be signed in to star a gist
  • Fork 18 You must be signed in to fork a gist
  • Save malero/418204 to your computer and use it in GitHub Desktop.
Save malero/418204 to your computer and use it in GitHub Desktop.
# Sort a list of dictionary objects by a key - case sensitive
from operator import itemgetter
mylist = sorted(mylist, key=itemgetter('name'))
# Sort a list of dictionary objects by a key - case insensitive
mylist = sorted(mylist, key=lambda k: k['name'].lower())
# Sort by multiple keys - case sensitive
from operator import itemgetter
mylist = sorted(mylist, key=itemgetter('name', 'age'))
# Sort by multiple keys - case insensitive
mylist = sorted(mylist, key=lambda k: (k['name'].lower(), k['age']))
# Sort name alphabetically and age in descending order
mylist = sorted(mylist, key=lambda k: (k['name'].lower(), -k['age']))
from operator import itemgetter
def multikeysort(items, columns, functions={}, getter=itemgetter):
"""Sort a list of dictionary objects or objects by multiple keys bidirectionally.
Keyword Arguments:
items -- A list of dictionary objects or objects
columns -- A list of column names to sort by. Use -column to sort in descending order
functions -- A Dictionary of Column Name -> Functions to normalize or process each column value
getter -- Default "getter" if column function does not exist
operator.itemgetter for Dictionaries
operator.attrgetter for Objects
"""
comparers = []
for col in columns:
column = col[1:] if col.startswith('-') else col
if not column in functions:
functions[column] = getter(column)
comparers.append((functions[column], 1 if column == col else -1))
def comparer(left, right):
for func, polarity in comparers:
result = cmp(func(left), func(right))
if result:
return polarity * result
else:
return 0
return sorted(items, cmp=comparer)
def compose(inner_func, *outer_funcs):
"""Compose multiple unary functions together into a single unary function"""
if not outer_funcs:
return inner_func
outer_func = compose(*outer_funcs)
return lambda *args, **kwargs: outer_func(inner_func(*args, **kwargs))
# We will use a list of dictionary objects representing students, their classes and their current grade
students = [
{'name':'Paul Allen', 'class':'Science','grade':'A'},
{'name':'paul allen', 'class':'Math','grade':'C'},
{'name':'Bob Lewis', 'class':'Science','grade':'D'},
{'name':'Bob Lewis', 'class':'math','grade':'b'},
{'name':'bob Lewis', 'class':'History','grade':'f'},
]
# Sort by class and grade descending, case sensitive
ex1 = multikeysort(students, ['class', '-grade'])
# Sort by class and grade descending, case insensitive
from operator import itemgetter, methodcaller
# Create a unary function that will get the column value and then call the .lower() method
get_class = compose(itemgetter('class'), methodcaller('lower'))
get_grade = compose(itemgetter('grade'), methodcaller('lower'))
ex1 = multikeysort(students, ['class', '-grade'],{'class':get_class,'grade':get_grade})
@antoine-tran
Copy link

The 4th method is really elegant and neat. Thanks a lot !!

@shrhawk-entertainer
Copy link

python-3 version

from functools import cmp_to_key
from operator import itemgetter, methodcaller

def cmp(a, b):
    try:
        return (a > b) - (a < b)
    except TypeError:
        return -1

def multikeysort(items, columns, functions={}, getter=itemgetter):
    """Sort a list of dictionary objects or objects by multiple keys bidirectionally.

    Keyword Arguments:
    items -- A list of dictionary objects or objects
    columns -- A list of column names to sort by. Use -column to sort in descending order
    functions -- A Dictionary of Column Name -> Functions to normalize or process each column value
    getter -- Default "getter" if column function does not exist
              operator.itemgetter for Dictionaries
              operator.attrgetter for Objects
    """
    comparers = []
    for col in columns:
        column = col[1:] if col.startswith('-') else col
        if not column in functions:
            functions[column] = getter(column)
        comparers.append((functions[column], 1 if column == col else -1))

    def comparer(left, right):
        for func, polarity in comparers:
            result = cmp(func(left), func(right))
            if result:
                return polarity * result
        else:
            return 0
    return sorted(items, key=cmp_to_key(comparer))

def compose(inner_func, *outer_funcs):
     """Compose multiple unary functions together into a single unary function"""
     if not outer_funcs:
         return inner_func
     outer_func = compose(*outer_funcs)
     return lambda *args, **kwargs: outer_func(inner_func(*args, **kwargs))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment