py_cryptic_sorter

Código comentado línea a línea y paso a paso:

Entregas: py_cryptic_sorter.py con cryptic_sorter(strings). Rank 03 · nivel 1.

Qué pide (en palabras normales)

Subject (enunciado original)
Write a function that sorts a list of strings according to multiple criteria:
1. Primary sort: By string length (shortest first)
2. Secondary sort: ASCII order, except letters are compared case-insensitively
   (for strings of same length)
3. Tertiary sort: By number of vowels (ascending, for same length and lexically equal)
4. Equal strings will appear in the same order as in the input list.

**Forbidden functions: sorted(), list.sort()**

Expected file: py_cryptic_sorter.py
Function signature:
def cryptic_sorter(strings: list[str]) -> list[str]:
Forbidden: sorted(), list.sort()

Examples:
cryptic_sorter(["apple","cat","banana","dog","elephant"])
=> ["cat","dog","apple","banana","elephant"]
cryptic_sorter(["aaa","bbb","AAA","BBB"])
=> ["aaa", "AAA", "bbb", "BBB"]
cryptic_sorter(["hello","world","hi","test"])
=> ["hi","test","hello","world"]
cryptic_sorter([])
=> []
cryptic_sorter([""])
=> [""]

Los criterios en una tupla

def key(s):
    vowels = sum(1 for ch in s.lower() if ch in "aeiou")
    return (len(s), s.lower(), vowels)

Python compara tuplas elemento a elemento y solo mira el siguiente si hay empate. Eso es exactamente "criterio primario, secundario, terciario". Toda la lógica de comparación cabe en una línea.

Merge sort estable

while i < len(left) and j < len(right):
    if right[j][0] < left[i][0]:
        merged.append(right[j])
        j += 1
    else:
        merged.append(left[i])
        i += 1

Parto la lista en dos, ordeno cada mitad y mezclo. La estabilidad (criterio 4) está en el < estricto: si empatan, tomo primero el de la izquierda, que venía antes en la entrada. Ordeno pares (clave, string) para no recalcular la clave en cada comparación.

Trampas