Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 310
user3421420 Avatar asked Sep 12 '25 01:09

user3421420


2 Answers

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)))))
like image 51
Patrick Haugh Avatar answered Sep 14 '25 14:09

Patrick Haugh


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))]
like image 24
jpp Avatar answered Sep 14 '25 15:09

jpp