Python: Append names in alphabetic order to 26 sublists of major list

Viewed 46

I have a list containing loads of names (first & last name).

lst = ['Adam Sander', 'Brian Amnos', ....]

The first task was to sort all those names. Subsequently, I created a new list with 26 nested lists. My goal is to append the persons whose last name starts with A in the first nested list and so.. I am stuck on that part. Any hints would be much appreciated.

3 Answers

If you need this to be a list of lists, use ord() to turn the first letter of the last name into an index from 0 to 25:

>>> lst = ['Adam Sander', 'Brian Amnos']
>>> alpha_names = [[] for _ in range(26)]
>>> for name in lst:
...     alpha_names[ord(name.split()[-1][0].upper()) - ord('A')].append(name)
...
>>> alpha_names
[['Brian Amnos'], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], ['Adam Sander'], [], [], [], [], [], [], []]

Note that Amnos is in the first sublist (because A is the first letter of the alphabet) and Sander is in the nineteenth sublist.

If you just need to sort the names, the nested list is an unnecessary complication. Just do a regular sort with an appropriate key function:

>>> sorted(lst, key=lambda name: name.upper().split()[::-1])
['Brian Amnos', 'Adam Sander']

Note that the given key uppercases the name (to case-normalize it, because you probably don't want to sort a name differently based on capitalization differences/errors), splits it, and then reverses the result of the split (so the last name comes before the first name -- that way the sort is done with the last name first, but the first name is still considered when necessary to break ties).

If you do need to partition them based on last name, but it's okay to put them in a dict instead of a list, that's simpler because you don't need to turn the first letter of the last name into a numerical index:

>>> alpha_names = {}
>>> for name in lst:
...     alpha_names.setdefault(name.split()[-1][0].upper(), []).append(name)
...
>>> alpha_names
{'S': ['Adam Sander'], 'A': ['Brian Amnos']}

We can readily achieve this using a nested list comprehension.

[[name for name in lst if name[0].upper() == ch] for i in range(26) for ch in [chr(ord('A') + i)]]
# [['Adam Sander'], ['Brian Amnos'], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []]

If we break this down, we can see what ch is on each iteration.

[ch for i in range(26) for ch in [chr(ord('A') + i)]]
# ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']

We're then checking those values against the first character in each name.

Or just use the already existing string.uppercase_ascii.

from string import uppercase_ascii

[[name for name in lst if name[0].upper() == ch] for ch in ascii_uppercase]
# [['Adam Sander'], ['Brian Amnos'], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []]

Alternatively, you might use itertools.groupby.

from itertools import groupby

lst = ['Adam Sander', 'Brian Amnos']
{k: list(v) for k, v in groupby(sorted(lst), key=lambda x: x[0].upper())}
# {'A': ['Adam Sander'], 'B': ['Brian Amnos']}

This solutions follow these steps

  1. separate the first and last name
  2. sort by last name
  3. runs a loop and checks if the previous letter was the same as the current letter. If so, it Will add it to the same sub-list, else, it will create a new sub-list.
import pandas as pd 

# list of names
all_names = ['Adam Sander', 'Brian Amnos', 'Micheal Sivwire']

# creates a pandas DataFrame
df = pd.DataFrame({'full_name' : all_names})

# splits first name and last name
df[['first_name','last_name']] = df['full_name'].str.split(" ", 1, expand=True)

# sorts values by last name
df = df.sort_values(by=['last_name'])

# create an empty list
sorted_by_lastname = [[]]

# stores A as initial letter
previous_letter = 'A' 

# for loop to create sub-lists
for a in df.index:
    
    if df.last_name[a][0].upper() == previous_letter:
        # checks if the current letter is same as the previous letter 
        sorted_by_lastname[-1].append(df.full_name[a])
    else:
        # if not, then a new previous letter is created and new sub list will be created
        previous_letter = df.last_name[a][0].upper()
        sorted_by_lastname.append([df.full_name[a]])
            
print (sorted_by_lastname)

Output:

[['Brian Amnos'], ['Adam Sander', 'Micheal Sivwire']]
Related