Advent JS 2021 - Dias 6 a 10

Día 6: “Rematando los exámenes finales”

Dificultad: Normal

Primer reto en el que deberemos aplicar un algoritmo para resolverlo, en este caso sliding window. Un algoritmo bastante habitual y que simplemente nos permite recorrer una estructura de datos de forma gradual a través de un ventana, en nuestro caso una ventana de dos elementos los cuales deberán sumar un valor concreto.

function sumPairs(numbers, result) {
  for (let i = 0; i < numbers.length; i++) {
    for (let j = 1; j < numbers.length; j++) {
      if (i != j && numbers[i] + numbers[j] === result)
        return [numbers[i], numbers[j]];
    }
  }
  return null;
}

Día 7: “Buscando en el almacén…"

Dificultad: Normal

Primer reto donde vamos a tener que enfrentarnos a la recursividad. Tengo que admitir que con el paso del tiempo cada vez me gustan este tipo de problemás aunque inicialmente me parecieran los más difíciles de resolver, una vez entiendes cómo aplicar la recursividad este tipo de problemás se vuelven muy sencillos.

En este caso en concreto recibiremos un object y un value para que validemos si en alguno de los niveles del objeto existe ese valor. Para ellos bastará con analizar el tipo del valor para cada key y si este es un objeto a sí mismo deberemos aplicar recursividad, en caso contrario simplemente deberemos comprobar su valor.

function contains(store, product) {
  for (const property in store) {
    if (typeof store[property] === 'string' && store[property] === product) {
      return true;
    }
    if (typeof store[property] === 'object') {
      if (contains(store[property], product)) return true;
    }
  }
  return false;
}

Día 8: “La locura de las criptomonedas”

Dificultad: Normal

Para el día 8 debemos aplicar nuevamente el algoritmo de sliding window cómo en el día 6, en este caso a la versión ordenada de menor a mayor de un array de números que recibimos por parámetro y cogiendo los valores en los extremos (i = 0 & j = sorted.length).

function maxProfit(prices) {
  const sorted = [...prices].sort();

  for (let i = 0; i < sorted.length; i++) {
    for (let j = sorted.length; j >= 0; j--) {
      if (prices.indexOf(sorted[i]) < prices.indexOf(sorted[j])) {
        if (sorted[i] < sorted[j]) {
          return sorted[j] - sorted[i];
        }
      }
    }
  }

  return -1;
}

Día 9: “Agrupando cosas automáticamente”

Dificultad: Difícil

En este día nos enfrentamos a unos de los retos más complejos del Advent, la parte más complicada es entender cómo debemos hacer la agrupación en función del tipo por el que debemos agrupar:

  • Si es una function debemos llamar a la misma y almacenar el resultado de cada llamada.
  • Si es una key que a su vez forma parte del objeto que recibimos cómo parámetro de entrada, debemos agruparlo por esa key.
  • Por último, si el parámetro para agrupar es un método de la instancia agrupar, cómo length para string o size para Set, debemos llamar al método y almacenar el resultado.
function groupBy(collection, it) {
  const result = {};
  if (typeof it === 'function') {
    collection.reduce((acc, current) => {
      const x = it(current);
      if (acc[x] === undefined) acc[x] = [];
      acc[x].push(current);
      return acc;
    }, result);
  } else if (collection.some((c) => Object.keys(c).includes(it))) {
    collection.reduce((acc, value) => {
      const x = value[`${it}`];
      if (acc[x] === undefined) acc[x] = [];
      const t = Object.entries(value).reduce((acc, [x, y]) => {
        acc[x] = y;
        return acc;
      }, {});
      acc[x].push(t);
      return acc;
    }, result);
  } else if (Object.getOwnPropertyNames(Object.getPrototypeOf(typeof it))) {
    collection.reduce((acc, current) => {
      const x = current[`${it}`];
      if (acc[x] === undefined) acc[x] = [];
      acc[x].push(current);
      return acc;
    }, result);
  }

  return result;
}

Día 10: “La máquina de cambio”

Dificultad: Difícil

Una vez solucionado y mirando las respuestas de otras personas vi que no todo el mundo pensó en resolverlo con recursividad pero a mi me parece la manera más limpia de hacerlo.

Simplemente debemos buscar la primera moneda que es menor que el cambio que recibimos y con el resto llamamos de forma recursiva al método getCoins.

La recursión se termina si el cambio es <= 0, caso en el que retornaremos el array que nos pedián con las monedas de cada tipo.

function getCoins(change, out = [0, 0, 0, 0, 0, 0]) {
  if (change <= 0) return out;

  const coins = [50, 20, 10, 5, 2, 1];

  for (let i = 0; i < coins.length; i++) {
    const element = coins[i];
    if (change - element >= 0) {
      out[coins.length - 1 - i]++;
      return getCoins(change - element, out);
    }
  }

  return out;
}

…Continua aquí