SolveConPython

Python Reto #14 — Eliminar duplicados preservando el orden

Nivel: Intermedio
Tema: Listas, sets, orden estable, validación de entradas, complejidad, tests con pytest
Objetivo: Eliminar elementos duplicados de una lista manteniendo el orden original de aparición, un patrón común en limpieza de datos.

Reto #14 Eliminar duplicados preservando el orden
Reto #14 Eliminar duplicados preservando el orden

Enunciado

Crea una función llamada eliminar_duplicados(lista) que:

  1. Reciba un valor lista.
  2. Si lista es None, devuelva una lista vacía [].
  3. Si lista no es una lista (list), lance TypeError.
  4. Devuelva una nueva lista sin duplicados.
  5. Mantenga el orden original.
  6. Solo debe funcionar con elementos “hashables” (por ejemplo: int, str, tuple).
    • Si aparece un elemento no hashable (por ejemplo, un dict o list), la función debe lanzar TypeError.

Ejemplos

  • eliminar_duplicados([1, 2, 2, 3, 1])[1, 2, 3]
  • eliminar_duplicados(["a", "a", "b", "a"])["a", "b"]
  • eliminar_duplicados([])[]
  • eliminar_duplicados(None)[]
  • eliminar_duplicados([1, {"a": 1}])TypeError

Pistas

  1. Usa un set para recordar qué elementos ya viste.
  2. Recorre la lista y añade al resultado solo si no lo has visto.
  3. Para detectar si algo es hashable, puedes intentar hacer hash(valor) dentro de un try/except.

Solución explicada (paso a paso)

  1. Si lista es None, devolvemos [].
  2. Validamos que lista sea una lista.
  3. Creamos:
    • vistos (un set) para registrar elementos únicos,
    • resultado (una lista) para la salida.
  4. Recorremos cada elemento:
    • verificamos que sea hashable (si no, TypeError),
    • si no está en vistos, lo añadimos a resultado y lo registramos.
  5. Devolvemos resultado.

Python
def eliminar_duplicados(lista: list | None) -> list:
"""
Elimina duplicados preservando el orden de aparición.
Reglas:
- None -> []
- No list -> TypeError
- Mantiene orden original
- Si encuentra un elemento no hashable -> TypeError
"""
if lista is None:
return []
if not isinstance(lista, list):
raise TypeError("El parámetro 'lista' debe ser una lista o None.")
vistos = set()
resultado = []
for valor in lista:
try:
hash(valor)
except TypeError as exc:
raise TypeError("La lista contiene un elemento no hashable (p. ej. dict o list).") from exc
if valor not in vistos:
vistos.add(valor)
resultado.append(valor)
return resultado

Python
import pytest
from reto_14_eliminar_duplicados import eliminar_duplicados
def test_numeros_con_duplicados():
assert eliminar_duplicados([1, 2, 2, 3, 1]) == [1, 2, 3]
def test_strings_con_duplicados():
assert eliminar_duplicados(["a", "a", "b", "a"]) == ["a", "b"]
def test_lista_vacia():
assert eliminar_duplicados([]) == []
def test_none_devuelve_lista_vacia():
assert eliminar_duplicados(None) == []
def test_tipo_incorrecto_lanza_typeerror():
with pytest.raises(TypeError):
eliminar_duplicados("no_es_lista")
def test_elemento_no_hashable_lanza_typeerror():
with pytest.raises(TypeError):
eliminar_duplicados([1, {"a": 1}])

Ejecuta:

  • pytest -q

Variantes para subir de nivel (opcional)

  1. Permitir elementos no hashables convirtiéndolos a una representación (por ejemplo, json.dumps) (avanzado)
  2. Eliminar duplicados por clave en una lista de diccionarios (patrón real)
  3. Devolver también los duplicados encontrados
  4. Comparar rendimiento vs. dict.fromkeys(lista) (con restricciones)

Lo que aprendiste

  • Cómo eliminar duplicados sin perder orden
  • Uso de set para búsquedas O(1)
  • Qué significa que un objeto sea hashable
  • Validación defensiva en funciones intermedias

Accede al código completo y a los tests en GitHub para ejecutar y modificar la solución localmente.

Continúa con Reto #15 — Leer un CSV y calcular métricas (sin pandas) para practicar procesamiento de archivos y datos tabulares, muy útil en tareas reales.