user3421420
user3421420

Reputation: 103

Python - Sorting a list item by alphabet in a list of lists, and have other lists follow the swapping order

I am trying to sort a list of lists in Python by the first row (specifically not using Numpy, I know there are many solutions using Numpy but this is a question that specifically asks for a way without using Numpy)

Here is my list of lists:

listOfLists = [ ['m', 'e', 'l', 't', 's'],
                ['g', 'p', 's', 'k', 't'],
                ['y', 'q', 'd', 'h', 's'] ]

I am looking to sort this list 1) alphabetically BUT 2) only by the first list item, the vertical slices should just follow the order of the first list item. For example:

newListofLists = [ ['e', 'l', 'm', 's', 't'],
                   ['p', 's', 'g', 't', 'k'],
                   ['q', 'd', 'y', 's', 'h'] ]

The first item in listOfLists is 'melts', which is then sorted alphabetically to become 'elmst'. The rest of the items in the list of list aren't sorted alphabetically, rather they are 'following' the switch and sort pattern of the first item in the list.

I may be being ridiculous but I've spent hours on this problem (which forms part of a larger program). I have tried slicing the first item from the list of lists and sorting it alphabetically on its own, then comparing this to a slice of the first list in the list of lists that HASN'T been sorted, and comparing positions. But I just can't seem to get anything working.

Upvotes: 3

Views: 1787

Answers (2)

Patrick Haugh
Patrick Haugh

Reputation: 60994

You can transpose the list using zip, sort the transpose, and then transpose that list back into one of the correct dimensions.

listOfLists = [ ['m', 'e', 'l', 't', 's'],
                ['g', 'p', 's', 'k', 't'],
                ['y', 'q', 'd', 'h', 's'] ]

print(list(zip(*sorted(zip(*listOfLists)))))
# [('e', 'l', 'm', 's', 't'), ('p', 's', 'g', 't', 'k'), ('q', 'd', 'y', 's', 'h')]

Edit:

As @StevenRumbalski points out in the comments, the above will completely sort the vertical slices (by first letter, then second letter, etc), instead of sorting them stably by first letter (sorting by first letter, then by relative order in the input). I'll reproduce his solution here for visibility:

from operator import itemgetter
list(map(list, zip(*sorted(zip(*listOfLists), key=itemgetter(0)))))

Upvotes: 11

jpp
jpp

Reputation: 164673

numpy is the way to go for both performance and readability:

import numpy as np

listOfLists = [ ['m', 'e', 'l', 't', 's'],
                ['g', 'p', 's', 'k', 't'],
                ['y', 'q', 'd', 'h', 's'] ]

lol = np.array(listOfLists)

lol[:, np.argsort(listOfLists[0])]

# array([['e', 'l', 'm', 's', 't'],
#        ['p', 's', 'g', 't', 'k'],
#        ['q', 'd', 'y', 's', 'h']], 
#       dtype='<U1')

A non-numpy solution:

idx = sorted(range(len(lol[0])), key=lol[0].__getitem__)

[list(map(lol[j].__getitem__, idx)) for j in range(len(lol))]

Upvotes: 1

Related Questions