Sorting list of lists

I’m working with lists. I need to sort by one of the values in the sub list. I need the whole sub list to move with the value that is being sorted.


Example data: files = [asb, 23, 89], [asc, 48, 89], [asa, 78, 89] # This is a list of lists

Really look like: files = [asb, 23, 89, asc, 48, 89, asa, 78, 89] I’m using an offset of 3 to go through the list and address the data of files[1] + offset(3) to be sorted. So for being clear I’m sorting 23, 48, 78.

Finished output of sort:


File = [asa, 78, 89, asc, 48, 89, asb, 23, 89]

This becomes a sorted data set by[1].

Is there way to do this properly?

Thanks

What are “asb”, etc? Are they really strings? In a Python script that would be "asb", etc.

Assuming the above:

files = ["asb", 23, 89, "asc", 48, 89, "asa", 78, 89]

# Turn it into a real list of lists
nested_list = []

for start in range(0, len(files), 3):
    nested_list.append(files[start : start + 3])

# Sort the list of lists
nested_list.sort(key=lambda sublist: sublist[0])

# Flatten the list again
files = []

for sublist in nested_list:
    files.extend(sublist)

Same at @MRAB, but with a couple “tricks”:

In [18]: File
Out[18]: ['asa', 78, 89, 'asc', 48, 89, 'asb', 23, 89]

In [19]: from operator import itemgetter

In [20]: sorted(zip(File[::3], File[1::3], File[2::3]), key=itemgetter(1))
Out[20]: [('asb', 23, 89), ('asc', 48, 89), ('asa', 78, 89)]

note that the three slices will make copies, which is not terribly efficient if the list is huge – in that case, you could use itertools.islice(), but that’s a lot more awkward to write.

oops – forgot to flatten it again:

In [30]: from operator import itemgetter

In [31]: from itertools import chain

In [32]: File
Out[32]: ['asa', 78, 89, 'asc', 48, 89, 'asb', 23, 89]

In [33]: list(chain(*sorted(zip(File[::3], File[1::3], File[2::3]), key=itemgetter(1))))
Out[33]: ['asb', 23, 89, 'asc', 48, 89, 'asa', 78, 89]

Chris Baker, I need some help with line 33 - In [33]: list(chain(*sorted(zip(File[::3], File[1::3], File[2::3]), key=itemgetter(1)))).

I believe that ‘File[::3], File[1::3], File[2::3]’ is for the three sub lists. If so what do you do if you don’t know how many sub files there maybe. Less than 20 in my case.

Could you break it down the rest of the line of code. I don’t like guessing and i’m new at Python.
Thanks so much.

I believe I’d go with:

#!/usr/local/cpython-3.11/bin/python3

"""
Just sort a list of lists, and then flatten it by one of the many definitions of 'flatten'.

https://discuss.python.org/t/sorting-list-of-lists/22238
"""

from operator import itemgetter


def flatten(lol):
    """Flatten a list of lists one level deep."""
    result = []
    for sublist in lol:
        result.extend(sublist)
    return result


def main():
    """Start the ball rolling."""
    lol = [['asb', 23, 89], ['asc', 48, 89], ['asa', 78, 89]]

    # Near as I can figure, you're sorting by the second element of each sublist, but in reverse order.
    lol.sort(key=itemgetter(1), reverse=True)

    flat = flatten(lol)

    print(flat)


if __name__ == '__main__':
    main()

Sorry about that – clever one-liners are hard to understand :frowning:

zip(File[::3], File[1::3], File[2::3])

is indeed creating three sub-lists, each offset by one, and then “zipping” them together into a list of tuples (actually, and iterator of tuples…)

If so what do you do if you don’t know how many sub files there maybe

if you don’t know at all, then you’ll need a totally different logic – how can you tell when a new “sub list” starts? are they all the same length, etc?

If you do know, but maybe not when writing the code, then you probably want to go with MRAB’s suggestion, and use a variable rather than 3.

for start in range(0, len(files), sub_list_len):
    nested_list.append(files[start : start + sub_list_len])

But I’d take a step back – where did this “flattened” list come from? maybe it could have been built as a list of lists in the first place?

You have two problems here:

  1. how to take a “flattened” list and make a nested list from it

  2. how to sort a list that has complex items in it in a non-default way.

(1) Is the tricks we are showing you

(2) is understanding how sorting, specifically the “key” function, work in Python.

Sorry I didn’t clearly break out the steps like MRAB did :wink:

I was surprised that there wasn’t a way to “unflatten” a list already in itertools.

But there is in more_itertools

https://more-itertools.readthedocs.io/en/stable/_modules/more_itertools/more.html#chunked

So we now have:

In [17]: from operator import itemgetter

In [18]: from itertools import chain

In [19]: from more_itertools import chunked

In [20]: File
Out[20]: ['asa', 78, 89, 'asc', 48, 89, 'asb', 23, 89]

In [21]: nested_list = chunked(File, 3)

In [22]: sorted_list = sorted(nested_list, key=itemgetter(1))

In [23]: flattened_list = list(chain(*sorted_list))

In [24]: flattened_list
Out[24]: ['asb', 23, 89, 'asc', 48, 89, 'asa', 78, 89]

Honestly, I’m a bit ambivalent about itertools and more_itertools: they are pretty darn nifty, but it can take as long to find the function you are looking for an figure out how to use it as it takes to just write the code yourself …

There is built-in sorted which accepts key function. Sorted creates new list, if modification of original list is required then list method .sort works in quite similar fashion inplace.

data = [["asb", 23, 89], ["asc", 48, 89], ["asa", 78, 89]]

new_data = sorted(data, key=lambda row: row[1], reverse=True)

# new_data -> [['asa', 78, 89], ['asc', 48, 89], ['asb', 23, 89]]

data.sort(key=lambda row: row[1], reverse=True)

# data ->  [['asa', 78, 89], ['asc', 48, 89], ['asb', 23, 89]]

There are several ways to flatten list of lists. If one is comfortable with comprehension then it can be written as:

flat = [item for record in data for item in record]

# flat -> ['asa', 78, 89, 'asc', 48, 89, 'asb', 23, 89]