📊 Ordenación Avanzada

Los problemas de ordenación multi-criterio son los más comunes en nivel medio-avanzado. Dominando sorted() con key resuelves muchos problemas rápido.

🔑 El parámetro key

# Ordenar strings por longitud
palabras = ["hola", "a", "python", "es"]
sorted(palabras, key=len)
# ['a', 'es', 'hola', 'python']

# Ordenar por último carácter
sorted(palabras, key=lambda x: x[-1])

# Ordenar números por valor absoluto
nums = [3, -5, 1, -2]
sorted(nums, key=abs)
# [1, -2, 3, -5]

📋 Ordenación multi-criterio

💡
El truco: Devuelve una tupla. Python compara elemento por elemento.
estudiantes = [
    ("Ana", 85, 20),     # (nombre, nota, edad)
    ("Luis", 92, 19),
    ("Eva", 85, 21),
    ("Pedro", 92, 20)
]

# Por nota (descendente), luego por edad (ascendente)
resultado = sorted(estudiantes, key=lambda x: (-x[1], x[2]))
# [('Luis', 92, 19), ('Pedro', 92, 20), ('Ana', 85, 20), ('Eva', 85, 21)]
⚠️
El signo negativo: -x[1] invierte el orden (de mayor a menor). Solo funciona con números, no con strings.

🏅 Ejemplo clásico: Medallero olímpico

📝
Problema: Ordenar países por oro (desc), luego plata (desc), luego bronce (desc), luego nombre (asc).
n = int(input())
paises = []

for _ in range(n):
    partes = input().split()
    nombre = partes[0]
    oro = int(partes[1])
    plata = int(partes[2])
    bronce = int(partes[3])
    paises.append((nombre, oro, plata, bronce))

# Ordenar: más oro primero, empate → más plata, etc.
resultado = sorted(paises, key=lambda x: (-x[1], -x[2], -x[3], x[0]))

for p in resultado:
    print(p[0], p[1], p[2], p[3])

📝 Ordenar strings descendente

Como no puedes usar - con strings, hay dos opciones:

# Opción 1: reverse=True (si TODOS los criterios son descendentes)
sorted(nombres, reverse=True)

# Opción 2: Ordenar en dos pasos
# Primero por criterio secundario, luego por primario (stable sort)
datos = sorted(datos, key=lambda x: x.nombre)  # asc por nombre
datos = sorted(datos, key=lambda x: -x.puntos)  # desc por puntos

🎯 Patrón: Top N elementos

# Los 3 estudiantes con mejor nota
top_3 = sorted(estudiantes, key=lambda x: -x[1])[:3]

# Los 5 productos más baratos
baratos = sorted(productos, key=lambda x: x.precio)[:5]

📊 Ordenar diccionarios

puntos = {"Ana": 85, "Luis": 92, "Eva": 78}

# Por valor (puntos), descendente
ranking = sorted(puntos.items(), key=lambda x: -x[1])
# [('Luis', 92), ('Ana', 85), ('Eva', 78)]

# Por clave (nombre), ascendente
alfabetico = sorted(puntos.items(), key=lambda x: x[0])
# [('Ana', 85), ('Eva', 78), ('Luis', 92)]

✅ Ejercicio de práctica

📝
Problema: Lee N líneas con nombre y 3 notas. Ordena por media descendente, empates por nombre ascendente. Muestra nombre y media con 2 decimales.
👁️ Ver solución
n = int(input())
alumnos = []

for _ in range(n):
    partes = input().split()
    nombre = partes[0]
    notas = [float(partes[i]) for i in range(1, 4)]
    media = sum(notas) / 3
    alumnos.append((nombre, media))

# Ordenar por media desc, nombre asc
resultado = sorted(alumnos, key=lambda x: (-x[1], x[0]))

for nombre, media in resultado:
    print(f"{nombre} {media:.2f}")