Objetivo del mini-reto
Vas a construir una agenda de contactos por consola que permita hacer operaciones CRUD usando listas: Crear, Revisar (leer), Update (actualizar) y Delete (eliminar). La idea es practicar cómo modelar datos sencillos y cómo recorrer/editar una colección sin usar bases de datos.
Modelo de datos (simple y práctico)
Usaremos una lista llamada agenda. Cada contacto será otra lista con tres campos en orden fijo: [nombre, telefono, email]. Ejemplo: ["Ana", "555123", "ana@mail.com"]. Este formato es fácil de manipular con índices y suficiente para el reto.
Convención de índices
contacto[0]→ nombrecontacto[1]→ teléfonocontacto[2]→ email
Paso a paso: construye la agenda CRUD
Paso 1: estructura base y menú
Empieza con una lista vacía y un bucle principal que muestre opciones. Mantén el menú corto y claro.
agenda = [] # cada contacto: [nombre, telefono, email]
while True:
print("\n=== AGENDA SIMPLE ===")
print("1) Crear contacto")
print("2) Ver contactos")
print("3) Actualizar contacto")
print("4) Eliminar contacto")
print("5) Salir")
opcion = input("Elige una opción: ").strip()
if opcion == "5":
break
# aquí irán las opciones 1-4Paso 2: Crear (Create)
Crear significa pedir datos y agregarlos a la lista. Para evitar duplicados, puedes comprobar si ya existe un contacto con el mismo nombre (criterio simple).
def buscar_indice_por_nombre(agenda, nombre):
nombre = nombre.strip().lower()
for i, contacto in enumerate(agenda):
if contacto[0].strip().lower() == nombre:
return i
return -1
# Dentro del while, en la opción 1:
if opcion == "1":
nombre = input("Nombre: ").strip()
telefono = input("Teléfono: ").strip()
email = input("Email: ").strip()
if nombre == "":
print("Nombre obligatorio.")
else:
idx = buscar_indice_por_nombre(agenda, nombre)
if idx != -1:
print("Ya existe un contacto con ese nombre.")
else:
agenda.append([nombre, telefono, email])
print("Contacto creado.")Paso 3: Leer (Read) con formato
Leer es mostrar la lista. Un detalle útil es imprimir con numeración para que el usuario pueda elegir un contacto por índice en las operaciones de actualizar/eliminar.
Continúa en nuestra aplicación.
Podrás escuchar el audiolibro con la pantalla apagada, recibir un certificado gratuito para este curso y además tener acceso a otros 5.000 cursos online gratuitos.
O continúa leyendo más abajo...Descargar la aplicación
# Dentro del while, en la opción 2:
if opcion == "2":
if len(agenda) == 0:
print("Agenda vacía.")
else:
print("\n--- CONTACTOS ---")
for i, contacto in enumerate(agenda, start=1):
nombre, telefono, email = contacto
print(f"{i}. {nombre} | {telefono} | {email}")Paso 4: Actualizar (Update) por nombre o por número
Actualizar consiste en localizar un contacto y reemplazar uno o más campos. Para hacerlo cómodo, permite dejar un campo vacío para conservar el valor anterior.
def actualizar_contacto(agenda, idx):
nombre_actual, tel_actual, email_actual = agenda[idx]
print("Deja en blanco para mantener el valor actual.")
nuevo_nombre = input(f"Nombre ({nombre_actual}): ").strip()
nuevo_tel = input(f"Teléfono ({tel_actual}): ").strip()
nuevo_email = input(f"Email ({email_actual}): ").strip()
if nuevo_nombre != "":
agenda[idx][0] = nuevo_nombre
if nuevo_tel != "":
agenda[idx][1] = nuevo_tel
if nuevo_email != "":
agenda[idx][2] = nuevo_email
# Dentro del while, en la opción 3:
if opcion == "3":
if len(agenda) == 0:
print("No hay contactos para actualizar.")
else:
modo = input("Actualizar por (n)úmero o por (nombre): ").strip().lower()
if modo == "n":
n = input("Número de contacto: ").strip()
if not n.isdigit():
print("Debes escribir un número.")
else:
pos = int(n) - 1
if pos < 0 or pos >= len(agenda):
print("Número fuera de rango.")
else:
actualizar_contacto(agenda, pos)
print("Contacto actualizado.")
else:
nombre = input("Nombre a actualizar: ").strip()
idx = buscar_indice_por_nombre(agenda, nombre)
if idx == -1:
print("No encontrado.")
else:
actualizar_contacto(agenda, idx)
print("Contacto actualizado.")Paso 5: Eliminar (Delete) con confirmación
Eliminar es quitar un elemento de la lista. Añade confirmación para evitar borrados accidentales.
# Dentro del while, en la opción 4:
if opcion == "4":
if len(agenda) == 0:
print("No hay contactos para eliminar.")
else:
nombre = input("Nombre a eliminar: ").strip()
idx = buscar_indice_por_nombre(agenda, nombre)
if idx == -1:
print("No encontrado.")
else:
contacto = agenda[idx]
confirm = input(f"¿Eliminar a {contacto[0]}? (s/n): ").strip().lower()
if confirm == "s":
agenda.pop(idx)
print("Contacto eliminado.")
else:
print("Operación cancelada.")Checklist de mini-reto (para comprobar que lo lograste)
- La agenda inicia vacía y no se rompe si intentas ver/actualizar/eliminar sin contactos.
- Crear evita duplicados por nombre (o al menos avisa).
- Ver muestra contactos numerados y con campos separados.
- Actualizar permite mantener valores dejando el input vacío.
- Eliminar pide confirmación antes de borrar.
Extensiones opcionales (sube el nivel sin cambiar de herramienta)
1) Búsqueda parcial
Permite buscar por fragmento de nombre: si el usuario escribe an, mostrar Ana, Andrés, etc. Pista: compara con in usando minúsculas.
def buscar_por_fragmento(agenda, fragmento):
fragmento = fragmento.strip().lower()
resultados = []
for i, contacto in enumerate(agenda):
if fragmento in contacto[0].strip().lower():
resultados.append(i)
return resultados2) Ordenar por nombre
Antes de mostrar, ordena temporalmente por nombre. Pista: usa sorted con una clave que tome contacto[0].
agenda_ordenada = sorted(agenda, key=lambda c: c[0].strip().lower())
for i, contacto in enumerate(agenda_ordenada, start=1):
print(i, contacto[0], contacto[1], contacto[2])3) Validación mínima de email y teléfono
Sin complicarte: email debe contener @ y teléfono debe ser solo dígitos (o permitir + al inicio). Si no pasa, pide de nuevo.
def es_email_simple(email):
email = email.strip()
return ("@" in email) and ("." in email)
def es_tel_simple(tel):
tel = tel.strip()
if tel.startswith("+"):
return tel[1:].isdigit()
return tel.isdigit()Versión integrada (todo junto, lista para ejecutar)
Si quieres probar el mini-reto completo en una sola pieza, aquí tienes una versión compacta que une lo anterior.
def buscar_indice_por_nombre(agenda, nombre):
nombre = nombre.strip().lower()
for i, contacto in enumerate(agenda):
if contacto[0].strip().lower() == nombre:
return i
return -1
def actualizar_contacto(agenda, idx):
nombre_actual, tel_actual, email_actual = agenda[idx]
print("Deja en blanco para mantener el valor actual.")
nuevo_nombre = input(f"Nombre ({nombre_actual}): ").strip()
nuevo_tel = input(f"Teléfono ({tel_actual}): ").strip()
nuevo_email = input(f"Email ({email_actual}): ").strip()
if nuevo_nombre != "":
agenda[idx][0] = nuevo_nombre
if nuevo_tel != "":
agenda[idx][1] = nuevo_tel
if nuevo_email != "":
agenda[idx][2] = nuevo_email
agenda = []
while True:
print("\n=== AGENDA SIMPLE ===")
print("1) Crear contacto")
print("2) Ver contactos")
print("3) Actualizar contacto")
print("4) Eliminar contacto")
print("5) Salir")
opcion = input("Elige una opción: ").strip()
if opcion == "1":
nombre = input("Nombre: ").strip()
telefono = input("Teléfono: ").strip()
email = input("Email: ").strip()
if nombre == "":
print("Nombre obligatorio.")
else:
idx = buscar_indice_por_nombre(agenda, nombre)
if idx != -1:
print("Ya existe un contacto con ese nombre.")
else:
agenda.append([nombre, telefono, email])
print("Contacto creado.")
elif opcion == "2":
if len(agenda) == 0:
print("Agenda vacía.")
else:
print("\n--- CONTACTOS ---")
for i, contacto in enumerate(agenda, start=1):
nombre, telefono, email = contacto
print(f"{i}. {nombre} | {telefono} | {email}")
elif opcion == "3":
if len(agenda) == 0:
print("No hay contactos para actualizar.")
else:
modo = input("Actualizar por (n)úmero o por (nombre): ").strip().lower()
if modo == "n":
n = input("Número de contacto: ").strip()
if not n.isdigit():
print("Debes escribir un número.")
else:
pos = int(n) - 1
if pos < 0 or pos >= len(agenda):
print("Número fuera de rango.")
else:
actualizar_contacto(agenda, pos)
print("Contacto actualizado.")
else:
nombre = input("Nombre a actualizar: ").strip()
idx = buscar_indice_por_nombre(agenda, nombre)
if idx == -1:
print("No encontrado.")
else:
actualizar_contacto(agenda, idx)
print("Contacto actualizado.")
elif opcion == "4":
if len(agenda) == 0:
print("No hay contactos para eliminar.")
else:
nombre = input("Nombre a eliminar: ").strip()
idx = buscar_indice_por_nombre(agenda, nombre)
if idx == -1:
print("No encontrado.")
else:
contacto = agenda[idx]
confirm = input(f"¿Eliminar a {contacto[0]}? (s/n): ").strip().lower()
if confirm == "s":
agenda.pop(idx)
print("Contacto eliminado.")
else:
print("Operación cancelada.")
elif opcion == "5":
break
else:
print("Opción no válida.")