Welcome, guest | Sign In | My Account | Store | Cart

When you write a directory for a group of people, you want it grouped by last name initial and then sorted alphabetically. This recipe does just that; it creates a dictionary keyed by last name initial with a value of a tuple of names sorted in alphabetical order.

The input to 'groupnames' should be an iterable that returns strings that contain names written in first-middle-last fashion with each part separated by whitespace. The resulting names in the grouped dict will have normalized whitespace.

Python, 38 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
"""Provides functionality for taking in an iterable that returns names and then
separates them by last name initial and sorts them."""

import itertools

def groupnames(name_iterable):
    """Return a dict keyed by last name initial with a value of a tuple of the
    names sorted by last first name.

    The items returned by name_iterable are expected to be strings containing
    the names formatted as ``first middle last`` with middle being optional.

    >>> groupnames(('Anna Martelli', 'Alex Martelli', 'Todd Cannon', 'Gwen C',
    ... 'John Doe'))
    {'C': ('Gwen C', 'Todd Cannon'), 'M': ('Alex Martelli', 'Anna Martelli'), 'D': ('John Doe',)}

    """
    sorted_names = sorted(name_iterable, key=sortkeyfunc)
    name_dict = {}
    for key, group in itertools.groupby(sorted_names, groupkeyfunc):
        name_dict[key] = tuple(group)
    return name_dict

def sortkeyfunc(name):
    """Return name in last-first-middle order"""
    name_parts = name.split()
    new_name = ' '.join((name_parts[-1], name_parts[0]))
    if len(name_parts) == 3:
        new_name = ' '.join((new_name, name_parts[1]))
    return new_name

def groupkeyfunc(name):
    """Return the last name initial"""
    return name.split()[-1][0]

if __name__ == '__main__':
    import doctest
    doctest.testmod()

This recipe uses several new features in Python 2.4 . One is the built-in 'sorted' which takes in an iterable and returns a new list of the items returned from the iterable sorted. It also uses a key function for the sort which is new functionality also for list.sort that for using the DSU (Decorate-Sort-Undecorate) idiom without having to do the decoration ahead of time. Lastly, the itertools module's new 'groupby' function is used to separate out the names based on last name initial. It also uses doctest for testing which for Python 2.4 has been thoroughly refactored and rewritten.

Possible improvements is forcing all parts of the name to be capitalized properly (easy using str.capitalize) and not assuming everyone has a last name.

1 comment

Amos Newcombe 19 years, 6 months ago  # | flag
 >   new_name = ' '.join((name_parts[-1], name_parts[0]))
 >   if len(name_parts) == 3:
 >       new_name = ' '.join((new_name, name_parts[1]))

I would do it like this:

new_name = ' '.join([name_parts[-1]] + name_parts[0:-1])

Shorter, and it eliminates one point of failure for singleton names.