0

I’m trying to figure out how to sort a list but not all at once like in:

list.sort()

But instead I want to sort the first 3, then the next 3 and so on.

Let’s say my list is the following

list = [ F, V, T, O, D, Q, I, P, M]

Id want the out put to look like this:

[F, T, V, D, O, Q, I, M, P]

Does anyone know how I could go about doing this?

  • Have you considered a loop that iterates `for i in range(len(array)//3)` where i is then used to find the slices (indexes) to sort over? – treedust Mar 13 '22 at 01:58

3 Answers3

0

You can sort a slice of a list, and use slice assignment to replace it in the original list:

>>> my_list = ['F', 'V', 'T', 'O', 'D', 'Q', 'I', 'P', 'M']
>>> for i in range(len(my_list) // 3):
...     my_list[i*3:i*3+3] = sorted(my_list[i*3:i*3+3])
...
>>> my_list
['F', 'T', 'V', 'D', 'O', 'Q', 'I', 'M', 'P']

Another option would be to build a whole new list with sum and just assign it to my_list at the end (whether or not this is equivalent depends on whether there are other references to my_list anywhere):

>>> my_list = ['F', 'V', 'T', 'O', 'D', 'Q', 'I', 'P', 'M']
>>> my_list = sum((sorted(my_list[i*3:i*3+3]) for i in range(len(my_list) // 3)), [])
>>> my_list
['F', 'T', 'V', 'D', 'O', 'Q', 'I', 'M', 'P']
Samwise
  • 68,105
  • 3
  • 30
  • 44
0
my_list = ["F", "V", "T", "O", "D", "Q", "I", "P", "M"]

slice = 3

sorted_list = sum([sorted(my_list[i:i+slice]) for i in range(0, len(my_list), slice)], [])

# ['F', 'T', 'V', 'D', 'O', 'Q', 'I', 'M', 'P']
keyiflerolsun
  • 191
  • 1
  • 7
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Mar 13 '22 at 06:45
0

Just for fun a silly hack you shouldn't rely on:

i = iter(range(len(lst)))
lst.sort(key=lambda x: (next(i) // 3, x))

Benchmark results for list random.choices(ascii_uppercase, k=30_000):

 6.10 ms  Samwise
 5.66 ms  Samwise_juanpa
11.63 ms  Kelly
10.94 ms  Kelly_juanpa
 1.81 ms  Kelly6

Benchmark code (Try it online!):

def Samwise(my_list):
    for i in range(len(my_list) // 3):
        my_list[i*3:i*3+3] = sorted(my_list[i*3:i*3+3])
    return my_list

def Samwise_juanpa(my_list):
    for i in range(len(my_list) // 3):
        sl = slice(i*3, i*3+3)
        my_list[sl] = sorted(my_list[sl])
    return my_list

def Kelly(lst):
    i = iter(range(len(lst)))
    lst.sort(key=lambda x: (next(i) // 3, x))
    return lst

def Kelly_juanpa(lst):
    pairs = sorted([(i//3, x) for i, x in enumerate(lst)])
    return [x for _, x in pairs]

def Kelly6(lst):
    result = []
    for a, b, c in zip(*[iter(lst)]*3):
        if b < a:
            if c < b:
               result += c, b, a
            elif c < a:
               result += b, c, a
            else:
               result += b, a, c
        else:
            if c < a:
               result += c, a, b
            elif c < b:
               result += a, c, b
            else:
               result += a, b, c
    result += sorted(lst[len(lst) // 3 * 3:])
    return result

funcs = [
    Samwise,
    Samwise_juanpa,
    Kelly,
    Kelly_juanpa,
    Kelly6,
]

from timeit import default_timer as timer
from string import ascii_uppercase
import random

for _ in range(3):
    lst = random.choices(ascii_uppercase, k=30_000)
    previous = None
    for func in funcs:
        times = []
        for _ in range(20):
            copy = lst[:]
            t0 = timer()
            result = func(copy)
            t1 = timer()
            times.append(t1 - t0)
        print('%5.2f ms ' % (min(times) * 1e3), func.__name__)  #, len(result), previous is None or result == previous)
        assert previous is None or result == previous
        previous = result
    print()
Kelly Bundy
  • 23,480
  • 7
  • 29
  • 65
  • To me, this is more clear if you do the "Schwartzian transform" explicitly, so `pairs = sorted([(i//3, x) for i, x in enumerate(data)])` then `result = [x for _, x in pairs]` and that would actually be a solution I would end up using I think – juanpa.arrivillaga Mar 13 '22 at 02:18
  • @juanpa.arrivillaga Yes, that would be a proper version. I really did this just for fun, but now that you're turning it seriously, I wonder whether it might actually be faster than Samwise's with all its slicing and many `sorted` calls... – Kelly Bundy Mar 13 '22 at 02:26
  • I think it would for practical purposes, but speed wouldn't be my main concern here – juanpa.arrivillaga Mar 13 '22 at 02:32
  • @juanpa.arrivillaga Benchmarked with `random.choices(ascii_uppercase, k=300_000)`, Samwise's is about twice as fast. I have one that's three times as fast as Samwise's, but it's like 30 lines of code with six indentation levels :-D – Kelly Bundy Mar 13 '22 at 02:47
  • 1
    @juanpa.arrivillaga Made that fast one a bit nicer and added a benchmark to the answer now, including your versions. – Kelly Bundy Mar 13 '22 at 03:09