Las funciones map() y filter() forman parte de las herramientas integradas de Python para procesar iterables. map() transforma cada elemento mediante una función, mientras filter() conserva únicamente los elementos que cumplen una condición. Ambas devuelven iteradores perezosos, por lo que pueden trabajar con grandes cantidades de datos sin crear listas intermedias automáticamente.
Para utilizarlas correctamente conviene conocer las funciones en Python, las funciones lambda, los bucles for y las list comprehensions. Aunque map() y filter() pueden producir código elegante, no siempre son la opción más legible.
Cómo funciona map()
map(funcion, iterable) aplica la función a cada valor:
def duplicar(numero):
return numero * 2
numeros = [1, 2, 3, 4]
resultado = map(duplicar, numeros)
print(resultado)
print(list(resultado))El resultado inicial es un objeto map, no una lista. Los valores se calculan durante la iteración.
Usar map con lambda
precios = [10, 25, 40]
con_impuesto = map(lambda precio: precio * 1.21, precios)
print(list(con_impuesto))Una lambda resulta útil cuando la transformación es pequeña y evidente. Si incluye varias operaciones, validaciones o nombres importantes, utiliza una función definida con def.
Map con varias colecciones
map() puede recibir más de un iterable. La función debe aceptar el mismo número de argumentos:
cantidades = [2, 3, 1]
precios = [10.0, 5.5, 20.0]
totales = map(
lambda cantidad, precio: cantidad * precio,
cantidades,
precios
)
print(list(totales))La iteración termina cuando se agota el iterable más corto. Si necesitas detectar longitudes diferentes, valida antes o utiliza zip(..., strict=True) en versiones recientes de Python.
Cómo funciona filter()
filter(funcion, iterable) conserva los elementos para los que la función devuelve un valor verdadero:
def es_par(numero):
return numero % 2 == 0
numeros = [1, 2, 3, 4, 5, 6]
pares = filter(es_par, numeros)
print(list(pares))La función de prueba no necesita devolver literalmente True o False; Python utiliza la verdad lógica del resultado.
Filtrar con lambda
usuarios = [
{"nombre": "Ana", "activo": True},
{"nombre": "Luis", "activo": False},
{"nombre": "Marta", "activo": True},
]
activos = filter(lambda usuario: usuario["activo"], usuarios)
print(list(activos))Para reglas de negocio complejas, una función con nombre documenta mejor la intención.
Filter con None
valores = [0, 1, "", "Python", None, [], [1]]
limpios = filter(None, valores)
print(list(limpios))Cuando la función es None, se eliminan todos los valores falsy: cero, cadenas vacías, listas vacías y None. Esto puede borrar valores válidos como cero. Si solo quieres eliminar None, escribe una condición explícita:
limpios = filter(lambda valor: valor is not None, valores)Encadenar filter y map
numeros = range(1, 20)
pares = filter(lambda n: n % 2 == 0, numeros)
cuadrados = map(lambda n: n ** 2, pares)
print(list(cuadrados))El pipeline filtra primero y transforma después. Como ambos objetos son perezosos, no se crean listas intermedias.
El orden de las operaciones importa
Si la transformación es costosa, filtrar primero puede ahorrar trabajo:
validos = filter(validar, registros)
procesados = map(transformar, validos)Sin embargo, algunas condiciones solo pueden evaluarse después de transformar. Diseña el orden según el significado y el coste.
Comparación con list comprehensions
Este código:
resultado = list(
map(lambda n: n ** 2, filter(lambda n: n % 2 == 0, numeros))
)Puede escribirse de forma más directa:
resultado = [n ** 2 for n in numeros if n % 2 == 0]La comprensión suele ser más legible para transformaciones y filtros simples. map() es especialmente expresivo cuando ya existe una función con nombre:
normalizados = map(normalizar_email, correos)La decisión debe priorizar claridad, no cantidad de caracteres.
Comparación con expresiones generadoras
resultado = (
n ** 2
for n in numeros
if n % 2 == 0
)La expresión generadora también es perezosa. Para una transformación simple, puede ser más fácil de leer que una cadena de map() y filter().
Consumir el iterador una sola vez
resultado = map(str.upper, ["a", "b", "c"])
print(list(resultado))
print(list(resultado))La segunda lista queda vacía porque el iterador ya fue consumido. Si necesitas reutilizar los valores, conviértelos en lista una vez o crea nuevamente el pipeline.
Procesar archivos grandes
from pathlib import Path
with Path("eventos.log").open(encoding="utf-8") as archivo:
lineas = map(str.strip, archivo)
errores = filter(lambda linea: "ERROR" in linea, lineas)
for error in errores:
guardar_error(error)El archivo se procesa línea por línea. La memoria no depende del tamaño total del registro.
Normalizar datos
def normalizar_nombre(nombre):
return " ".join(nombre.strip().split()).title()
nombres = [" ana silva ", "LUIS perez", " marta"]
normalizados = list(map(normalizar_nombre, nombres))Una función con nombre facilita pruebas y reutilización.
Filtrar registros inválidos
def registro_valido(registro):
return (
registro.get("id") is not None
and registro.get("email")
and registro.get("activo") is True
)
validos = filter(registro_valido, registros)Evita acceder directamente a claves que pueden faltar si los datos proceden de una fuente externa.
Manejo de errores dentro de map
Si la función lanza una excepción, la iteración se detiene:
def convertir_entero(texto):
try:
return int(texto)
except (TypeError, ValueError):
return None
valores = map(convertir_entero, ["10", "x", "25"])
validos = filter(lambda valor: valor is not None, valores)
print(list(validos))En procesos donde necesitas conocer los errores, devuelve una estructura con estado en lugar de perder silenciosamente el valor original.
Usar funciones de la biblioteca estándar
import math
numeros = [1, 4, 9, 16]
raices = map(math.sqrt, numeros)
print(list(raices))map() funciona muy bien con funciones existentes como str.strip, str.lower, abs o math.sqrt.
Documentación oficial
La referencia oficial de map() explica la aplicación sobre uno o varios iterables. La referencia oficial de filter() describe el comportamiento con una función o con None.
Rendimiento y memoria
Como map() y filter() son perezosos, pueden ahorrar memoria. Esto no significa que siempre sean más rápidos. Las comprensiones están muy optimizadas y pueden ser igual o más rápidas para expresiones sencillas. Mide con timeit usando datos representativos.
El mayor beneficio suele ser evitar una lista intermedia cuando el consumidor acepta un iterable, por ejemplo sum(), any(), all(), un bucle o una escritura progresiva.
Cuándo evitar map y filter
Evítalos cuando la lambda sea difícil de entender, cuando haya varias condiciones anidadas, cuando necesites manejo de errores detallado o cuando el proceso tenga efectos secundarios. Un bucle explícito es mejor para registrar, actualizar varios objetos o controlar break y continue.
Errores frecuentes
Los problemas más comunes son olvidar convertir el iterador al mostrarlo, intentar recorrerlo varias veces, usar filter(None, ...) y perder ceros válidos, encadenar lambdas ilegibles y asumir que la pereza garantiza mayor velocidad. También debes recordar que una excepción puede aparecer durante el consumo, no al crear el objeto.
Conclusión
map() transforma elementos y filter() selecciona valores. Ambas funciones son perezosas y permiten crear pipelines sin listas intermedias. Son especialmente claras cuando reciben funciones con nombres descriptivos. Para expresiones cortas, las comprensiones y expresiones generadoras suelen ser más legibles. La mejor elección es la que comunica la intención, maneja los errores correctamente y se adapta al patrón de consumo de los datos.






