Cómo resolver closures y arrays con JavaScript

Clase 95 de 9930 días de JavaScript

Resumen

Aprende a construir un planificador de tareas y una calculadora usando JavaScript, arrays y programación funcional con closures. Verás cómo organizar un CRUD, evitar mutaciones innecesarias y aplicar métodos como filter, map, sort e includes para resolver desafíos reales con confianza.

¿Cómo se resuelve un planificador de tareas con closures en JavaScript?

La idea central es crear una función que devuelve métodos que comparten el mismo estado interno: un array de tareas. Este enfoque con closure permite encapsular datos y exponer solo la interfaz necesaria para operar el planner.

¿Por qué usar un closure y higher order functions?

  • Porque un closure guarda el estado (las tareas) sin variables globales.
  • Porque una función que retorna funciones organiza mejor el CRUD.
  • Porque facilita pruebas automáticas y control del alcance del scope.

¿Qué métodos forman el CRUD del planner?

  • addTask: agrega una tarea con completed en false por defecto.
  • removeTask: elimina por id o nombre con un único filtro.
  • getTasks: devuelve todas las tareas.
  • getPendingTasks: lista tareas con completed en false.
  • getCompletedTasks: lista tareas con completed en true.
  • markTaskAsCompleted: marca por id o por nombre.
  • getSortedTasksByPriority: ordena por prioridad 1, 2, 3 sin modificar el array original usando clon.
  • filterTasksByTag: filtra por una etiqueta usando includes.
  • updateTask: actualiza por id haciendo fusión de cambios estilo patch.

¿Cómo evitar mutaciones y ordenar por prioridad?

  • Clona el array antes de usar sort para no alterar el estado original.
  • Usa los “tres puntitos” para clonar objetos al agregar o actualizar.
  • Prefiere filter y map para crear nuevos estados.
// createTaskPlanner con closure y métodos del planner
function createTaskPlanner() {
  let tasks = [];

  const addTask = (task) => {
    const withCompleted = { ...task, completed: false };
    tasks.push(withCompleted);
  };

  const removeTask = (query) => {
    tasks = tasks.filter(t => t.id !== query && t.name !== query);
  };

  const getTasks = () => tasks;

  const getPendingTasks = () => tasks.filter(t => !t.completed);

  const getCompletedTasks = () => tasks.filter(t => t.completed);

  const markTaskAsCompleted = (query) => {
    const i = typeof query === 'number'
      ? tasks.findIndex(t => t.id === query)
      : -1;
    if (i >= 0) {
      tasks[i].completed = true;
    } else {
      tasks = tasks.map(t => t.name === query ? { ...t, completed: true } : t);
    }
  };

  const getSortedTasksByPriority = () => {
    const clone = [...tasks];
    return clone.sort((a, b) => a.priority - b.priority);
  };

  const filterTasksByTag = (tag) => tasks.filter(t => t.tags.includes(tag));

  const updateTask = (id, updates) => {
    const i = tasks.findIndex(t => t.id === id);
    if (i >= 0) tasks[i] = { ...tasks[i], ...updates };
  };

  return {
    addTask,
    removeTask,
    getTasks,
    getPendingTasks,
    getCompletedTasks,
    markTaskAsCompleted,
    getSortedTasksByPriority,
    filterTasksByTag,
    updateTask,
  };
}

¿Qué patrones y conceptos clave se practicaron?

Se reforzaron técnicas que verás a diario en frontend y backend, además de ideas útiles para entrevistas y retos con pruebas automáticas.

¿Qué habilidades se profundizaron con arrays y funciones?

  • Scope compartido con closures para administrar estado.
  • Inmutabilidad al clonar antes de sort y al usar filter/map.
  • Búsqueda y actualización con find index y fusión de cambios.
  • Filtro por tipo con typeof para distinguir id vs. nombre.
  • Etiquetas con includes para coincidencias en arrays.
  • Prioridad: 1 muy urgente, 2 urgente, 3 poco urgente.

¿Cómo se trabajó el flujo de desarrollo y pruebas?

  • Leer bien el problema: inputs, outputs y restricciones.
  • Estrategia: “Primero que funcione, luego que funcione bien, luego que funcione rápido.”
  • Feedback: pruebas automáticas en el Playground para validar la solución.

¿Qué recomendaciones ayudan a resolver más rápido?

  • Práctica constante manipulando arrays.
  • Pensamiento lógico como base del desempeño diario.
  • Experiencia transferible: en React evitar mutar el estado; los mismos patrones aplican.

¿Cómo se creó una calculadora con closures en minutos?

Se implementó una calculadora que mantiene un estado interno total y expone métodos para operar sobre él. El closure garantiza que el total solo cambie a través de sus métodos.

¿Qué interfaz expone la calculadora con closure?

  • add: suma al total.
  • subtract: resta del total.
  • multiply: multiplica el total.
  • clear: reinicia el total a 0.
  • getTotal: devuelve el total actual.
function createCalculator() {
  let total = 0;

  return {
    add: (value) => (total += value),
    subtract: (value) => (total -= value),
    multiply: (value) => (total *= value),
    clear: () => (total = 0),
    getTotal: () => total,
  };
}

¿Qué más se mencionó sobre ecosistema y herramientas?

  • Reto 30 días de JavaScript con sesiones en vivo y pruebas.
  • Soporte próximo en el Playground para SQL y luego TypeScript; después frameworks como React y Angular.
  • Comunidad en Discord para compartir dudas y comparar enfoques.

¿Te gustaría ver otro reto resuelto en vivo o proponer una variante del planner? Deja tu comentario con tu idea y la etiqueta que te interesa filtrar.