├── .gitignore
├── README.md
├── package.json
└── retos
├── reto-1
├── README.md
├── alternativas
│ └── 1.ts
├── main.ts
├── mejor-solucion.ts
└── reto1.test.js
├── reto-10
├── README.md
└── main.ts
├── reto-11
├── README.md
└── main.ts
├── reto-12
└── main.ts
├── reto-13
└── main.ts
├── reto-14
└── main.ts
├── reto-2
├── README.md
├── main.ts
└── reto2.test.js
├── reto-3
├── README.md
├── main.ts
└── reto3.test.js
├── reto-4
├── README.md
├── main.ts
└── reto4.test.js
├── reto-5
├── README.md
├── main.ts
└── reto5.test.js
├── reto-6
├── README.md
├── main.ts
└── reto6.test.js
├── reto-7
├── README.md
└── main.ts
├── reto-8
├── README.md
└── main.ts
└── reto-9
├── README.md
└── main.ts
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.lock*
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🌟 | Advent JS 2023
2 |
3 | Por favor dale a la estrellita y sígueme en YouTube y mis redes, este repositorio me ha costado demasiadas horas de trabajo:
4 |
5 | [](https://wakatime.com/badge/user/dfad5e3e-d673-48d4-a2d9-29c1c546ed80/project/018c25e2-b7d8-41bd-b08d-234b9e801de0)
6 |
7 | Repositorio donde estoy subiendo y explicando los retos del Advent JS 2023.
8 |
9 | # 🎮 | Retos
10 |
11 | | IMG | ## | Nombre del reto | Dificultad[^1] | Descripción | Solución | Video |
12 | | ------------------------------------------------------------------------------------------------- | :-: | -------------------------------------------------------------------------------- | -------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ----- |
13 | |
| 01 | [🎁 ¡Primer regalo repetido!](https://adventjs.dev/es/challenges/2023/1) | 🟢 | [Ver](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-1/README.md) | [SPOILER](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-1/main.ts) | ~~~~~ |
14 | |
| 02 | [🏭 Ponemos en marcha la fábrica](https://adventjs.dev/es/challenges/2023/2) | 🟢 | [Ver](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-2/README.md) | [SPOILER](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-2/main.ts) | ~~~~~ |
15 | |
| 03 | [😏 El elfo travieso](https://adventjs.dev/es/challenges/2023/3) | 🟢 | [Ver](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-3/README.md) | [SPOILER](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-3/main.ts) | ~~~~~ |
16 | |
| 04 | [😵💫 Dale la vuelta a los paréntesis](https://adventjs.dev/es/challenges/2023/4) | 🟠 | [Ver](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-4/README.md) | [SPOILER](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-4/main.ts) | ~~~~~ |
17 | |
| 05 | [🛷 El CyberTruck de Santa](https://adventjs.dev/es/challenges/2023/5) | 🟠 | [Ver](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-5/README.md) | [SPOILER](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-5/main.ts) | ~~~~~ |
18 | |
| 06 | [🦌 Los renos a prueba](https://adventjs.dev/es/challenges/2023/6) | 🟢 | [Ver](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-6/README.md) | [SPOILER](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-6/main.ts) | ~~~~~ |
19 | |
| 07 | [📦 Las cajas en 3D](https://adventjs.dev/es/challenges/2023/7) | 🟢 | [Ver](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-7/README.md) | [SPOILER](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-7/main.ts) | ~~~~~ |
20 | |
| 08 | [🏬 Ordenando el almacén](https://adventjs.dev/es/challenges/2023/8) | 🟠 | [Ver](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-8/README.md) | [SPOILER](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-8/main.ts) | ~~~~~ |
21 | |
| 09 | [🚦 Alterna las luces](https://adventjs.dev/es/challenges/2023/9) | 🟢 | [Ver](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-9/README.md) | [SPOILER](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-9/main.ts) | ~~~~~ |
22 | |
| 10 | [🎄 Crea tu propio árbol de navidad](https://adventjs.dev/es/challenges/2023/10) | 🟢 | [Ver](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-10/README.md) | [SPOILER](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-10/main.ts) | ~~~~~ |
23 | |
| 11 | [📖 Los elfos estudiosos](https://adventjs.dev/es/challenges/2023/11) | 🟠 | [Ver](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-11/README.md) | [SPOILER](https://github.com/Achalogy/advent-js-2023/blob/main/retos/reto-11/main.ts) | ~~~~~ |
24 | | | 12 | ~~~~~ | | ~~~~~ | ~~~~~ | ~~~~~ |
25 | | | 13 | ~~~~~ | | ~~~~~ | ~~~~~ | ~~~~~ |
26 | | | 14 | ~~~~~ | | ~~~~~ | ~~~~~ | ~~~~~ |
27 | | | 15 | ~~~~~ | | ~~~~~ | ~~~~~ | ~~~~~ |
28 | | | 16 | ~~~~~ | | ~~~~~ | ~~~~~ | ~~~~~ |
29 | | | 17 | ~~~~~ | | ~~~~~ | ~~~~~ | ~~~~~ |
30 | | | 18 | ~~~~~ | | ~~~~~ | ~~~~~ | ~~~~~ |
31 | | | 19 | ~~~~~ | | ~~~~~ | ~~~~~ | ~~~~~ |
32 | | | 20 | ~~~~~ | | ~~~~~ | ~~~~~ | ~~~~~ |
33 | | | 21 | ~~~~~ | | ~~~~~ | ~~~~~ | ~~~~~ |
34 | | | 22 | ~~~~~ | | ~~~~~ | ~~~~~ | ~~~~~ |
35 | | | 23 | ~~~~~ | | ~~~~~ | ~~~~~ | ~~~~~ |
36 | | | 24 | ~~~~~ | | ~~~~~ | ~~~~~ | ~~~~~ |
37 | | | 25 | ~~~~~ | | ~~~~~ | ~~~~~ | ~~~~~ |
38 |
39 | [^1]: **Dificultad**: 🟢 Facil 🟠 Media 🔴 Dificil 🟣 Muy Dificil
40 | [^2]: Dificultad un poco elevada
41 |
42 | ## ⌨️ | Instalación
43 |
44 | Instala Jest para las pruebas con:
45 |
46 | `npm install`
47 |
48 | ## 🧪 | Tests
49 |
50 | Puedes comprobar los retos usando en tu terminal el comando `npm run jest`.
51 | Si quieres probar tus propios scripts, tienes dos opciones:
52 |
53 | 1. Cambiar de nombre el archivo `index.js` y crear tu propio `index.js`
54 | 2. Comentar la función en el archivo `index.js` y crear tu propia función
55 |
56 | ```bash
57 | npm run test # Para correr todos los test de todos los retos
58 |
59 | npm run test:n # n siendo el numero del reto, por ejemplo
60 | npm run test:1 # Correra el test del reto 1
61 | ```
62 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "@types/node": "^20.10.1",
4 | "fs": "^0.0.1-security",
5 | "jest": "^29.7.0"
6 | },
7 | "scripts": {
8 | "test": "jest",
9 | "test:1": "jest -i retos/reto-1/reto1.test.js",
10 | "test:2": "jest -i retos/reto-2/reto2.test.js",
11 | "test:3": "jest -i retos/reto-3/reto3.test.js",
12 | "test:4": "jest -i retos/reto-4/reto4.test.js",
13 | "test:5": "jest -i retos/reto-5/reto5.test.js",
14 | "test:6": "jest -i retos/reto-6/reto6.test.js",
15 | "test:7": "jest -i retos/reto-7/reto7.test.js",
16 | "test:8": "jest -i retos/reto-8/reto8.test.js",
17 | "test:9": "jest -i retos/reto-9/reto9.test.js",
18 | "test:10": "jest -i retos/reto-10/reto10.test.js",
19 | "test:11": "jest -i retos/reto-11/reto11.test.js",
20 | "test:12": "jest -i retos/reto-12/reto12.test.js",
21 | "test:13": "jest -i retos/reto-13/reto13.test.js",
22 | "test:14": "jest -i retos/reto-14/reto14.test.js",
23 | "test:15": "jest -i retos/reto-15/reto15.test.js",
24 | "test:16": "jest -i retos/reto-16/reto16.test.js",
25 | "test:17": "jest -i retos/reto-17/reto17.test.js",
26 | "test:18": "jest -i retos/reto-18/reto18.test.js",
27 | "test:19": "jest -i retos/reto-19/reto19.test.js",
28 | "test:20": "jest -i retos/reto-20/reto20.test.js",
29 | "test:21": "jest -i retos/reto-21/reto21.test.js",
30 | "test:22": "jest -i retos/reto-22/reto22.test.js",
31 | "test:23": "jest -i retos/reto-23/reto23.test.js",
32 | "test:24": "jest -i retos/reto-24/reto24.test.js"
33 | }
34 | }
--------------------------------------------------------------------------------
/retos/reto-1/README.md:
--------------------------------------------------------------------------------
1 | # Reto 1
2 |
3 | En la fábrica de juguetes del Polo Norte, cada juguete tiene un número de identificación único.
4 |
5 | Sin embargo, debido a un error en la máquina de juguetes, algunos números se han asignado a más de un juguete.
6 |
7 | ¡Encuentra el primer número de identificación que se ha repetido, donde la segunda ocurrencia tenga el índice más pequeño!
8 |
9 | En otras palabras, si hay más de un número repetido, debes devolver el número cuya segunda ocurrencia aparezca primero en la lista. Si no hay números repetidos, devuelve -1.
10 |
11 | ```ts
12 | const giftIds = [2, 1, 3, 5, 3, 2];
13 | const firstRepeatedId = findFirstRepeated(giftIds);
14 | console.log(firstRepeatedId); // 3
15 | // Aunque el 2 y el 3 se repiten
16 | // el 3 aparece primero por segunda vez
17 |
18 | const giftIds2 = [1, 2, 3, 4];
19 | const firstRepeatedId2 = findFirstRepeated(giftIds2);
20 | console.log(firstRepeatedId2); // -1
21 | // Es -1 ya que no se repite ningún número
22 |
23 | const giftIds3 = [5, 1, 5, 1];
24 | const firstRepeatedId3 = findFirstRepeated(giftIds3);
25 | console.log(firstRepeatedId3); // 5
26 | ```
27 |
28 | ¡Ojo! Los elfos dicen que esto es una prueba técnica de Google.
29 |
30 | # Solución
31 |
32 | Tenemos que encontrar la primer repetición de un arreglo, para esto podemos usar una estructura auxiliar, en mi caso usare un objeto. La idea principal es que en nuestra estructura auxiliar almacenemos si ese número ya esta "visitado" así luego podemos retornar el primero que ya este "visitado" o de lo contrario `-1`.
33 |
34 | Ya que solo debemos encontrar el **primero**, usaré find, ya que este solo se ejecuta hasta encontrar un elemento que cumpla la condición, condición que en nuestro caso es que ya exista un valor en la posición de ese número, es decir que ya este visitado.
35 |
36 | ```ts
37 | let obj = {};
38 | let res = -1;
39 |
40 | gifts.find((g) => {
41 | if (obj[g]) {
42 | res = g;
43 | return true;
44 | }
45 | obj[g] = true;
46 | });
47 | ```
48 |
49 | Hay muchas formas de lograr el -1, pero en mi caso, fue mejor el puntaje realizandolo de esta forma, si nunca encontramos un numero que cumpla la condición, la variable `res` mantiene su valor en -1.
--------------------------------------------------------------------------------
/retos/reto-1/alternativas/1.ts:
--------------------------------------------------------------------------------
1 | function findFirstRepeated(gifts) {
2 | let obj = {}
3 |
4 | for (const i in gifts) {
5 | const g = gifts[i]
6 | if (obj[g]) return g
7 | obj[g] = true
8 | }
9 |
10 | return -1
11 | }
--------------------------------------------------------------------------------
/retos/reto-1/main.ts:
--------------------------------------------------------------------------------
1 | // @ts-expect-error
2 | function findFirstRepeated(gifts) {
3 | let obj = {}
4 | let res = -1;
5 |
6 | gifts.find(g => {
7 | if (obj[g]) {
8 | res = g;
9 | return true;
10 | }
11 | obj[g] = true
12 | })
13 |
14 | return res
15 | }
16 |
17 | module.exports = findFirstRepeated
18 |
--------------------------------------------------------------------------------
/retos/reto-1/mejor-solucion.ts:
--------------------------------------------------------------------------------
1 | // Créditos https://github.com/SantiMenendez19/adventjs/blob/main/2023/challenge01/solution.js
2 |
3 | function findFirstRepeated(gifts) {
4 | const repeated = gifts.filter((gift, i) => gifts.indexOf(gift) !== i)
5 | return repeated.length > 0 ? repeated[0] : -1
6 | }
--------------------------------------------------------------------------------
/retos/reto-1/reto1.test.js:
--------------------------------------------------------------------------------
1 | const findFirstRepeated = require('./main.ts')
2 |
3 | test('Test #01 - Returns a Number', () => {
4 | expect(
5 | typeof findFirstRepeated([2, 1, 3, 5, 3, 2])
6 | ).toBe("number")
7 | })
8 |
9 | test('Test #02 - findFirstRepeated([2, 1, 3, 5, 3, 2])', () => {
10 | expect(findFirstRepeated([2, 1, 3, 5, 3, 2])).toStrictEqual(3)
11 | })
12 |
13 | test('Test #03 - findFirstRepeated([2, 2])', () => {
14 | expect(findFirstRepeated([2, 2])).toStrictEqual(2)
15 | })
16 |
17 | test('Test #04 - findFirstRepeated([2, 4, 3, 5, 1])', () => {
18 | expect(findFirstRepeated([2, 4, 3, 5, 1])).toStrictEqual(-1)
19 | })
20 |
21 | test('Test #05 - findFirstRepeated([1])', () => {
22 | expect(findFirstRepeated([1])).toStrictEqual(-1)
23 | })
24 |
25 | test('Test #06 - findFirstRepeated([])', () => {
26 | expect(findFirstRepeated([])).toStrictEqual(-1)
27 | })
28 |
29 | test('Test #07 - findFirstRepeated([10, 20, 30])', () => {
30 | expect(findFirstRepeated([10, 20, 30])).toStrictEqual(-1)
31 | })
32 |
33 | test('Test #08 - findFirstRepeated([5, 1, 2, 3, 2, 5, 1])', () => {
34 | expect(findFirstRepeated([5, 1, 2, 3, 2, 5, 1])).toStrictEqual(2)
35 | })
36 |
37 | test('Test #09 - findFirstRepeated([12, 20, 30, 11, 20, 12])', () => {
38 | expect(findFirstRepeated([12, 20, 30, 11, 20, 12])).toStrictEqual(20)
39 | })
40 |
41 | test('Test #10 - findFirstRepeated([0, 1, 3, 5, 0, 2])', () => {
42 | expect(findFirstRepeated([0, 1, 3, 5, 0, 2])).toStrictEqual(0)
43 | })
--------------------------------------------------------------------------------
/retos/reto-10/README.md:
--------------------------------------------------------------------------------
1 | # Reto 10
2 |
3 | ¡Vaya idea ha tenido Sam Elfman! Quiere ofrecer un servicio que te crea un **árbol de Navidad 🎄 personalizado** en cuestión de segundos.
4 |
5 | Para crearlo nos pasan una **cadena de caracteres para formar el árbol** y un **número que indica la altura del mismo**.
6 |
7 | Cada carácter de la cadena representa un adorno del árbol, y vamos utilizándolos **de forma cíclica** hasta llegar a la altura indicada. Como **mínimo siempre nos pasarán uno**.
8 |
9 | Debemos devolver un **string** multilínea con el árbol de Navidad formado con los adornos, la altura indicada **más una última línea con el tronco formado por el carácter |** en el centro y, finalmente, un salto de línea \n.
10 |
11 | Por ejemplo si recibimos la cadena "123" y el número 4 como altura, tendríamos que construir este árbol:
12 |
13 | ```
14 | 1
15 | 2 3
16 | 1 2 3
17 | 1 2 3 1
18 | |
19 | ```
20 |
21 | Si recibimos la cadena \*@o y el número 3, el árbol que debemos devolver es:
22 |
23 | ```
24 | *
25 | @ o
26 | * @ o
27 | |
28 | ```
29 |
30 | Nota:
31 |
32 | - El árbol siempre debe estar centrado, para ello añade espacios en blanco a la izquierda de cada línea.
33 | - Crea espacios sólo a la izquierda de cada línea del árbol. No dejes espacios en blanco a la derecha.
34 | - Los adornos tienen un espacio en blanco entre ellos de separación.
35 |
36 | # Solución
37 |
38 | Mi solución se basa en ir cortando por partes un arreglo donde se repiten muchas veces la decoración.
39 |
40 | Definiremos el ancho del árbol como `(height * 2) - 1`, ya que si nos fijamos en el siguiente ejemplo, el árbol tiene un tamaño de $4$ y su ancho es igual a $4$ más los espacios que deja entre cada número, que en este caso serán $4-1$:
41 |
42 | ```js
43 | 1
44 | 2 3
45 | 1 2 3
46 | 1 2 3 1
47 | |
48 | ```
49 |
50 | Ahora repetiremos muchas veces las decoraciones, en mi caso el problema funciona repitiéndolo `60` veces, ya que usaremos algunos métodos exclusivos de array, lo mejor es convertirlo de una vez
51 |
52 | ```js
53 | ornaments = [...ornaments.repeat(60)];
54 | ```
55 |
56 | Ahora iteraremos desde `i=0` mientras `i < height`, así tendremos todos los números desde `0` hasta `height-1`, uno en cada iteración. Con esto podremos saber cuantos ornamentos deben ir en cada nivel del árbol, ya que siempre será `i+1`. Además, el tamaño de esa capa con los espacios que deben ir entre cada decoración, que nuevamente es `(i * 2) - 1`.
57 |
58 | ```js
59 | for (let i = 0; i < height; i++) {
60 | const layerWidth = i * 2 + 1;
61 | }
62 | ```
63 |
64 | Así que la cantidad de espacios que debemos poner es la diferencia entre el ancho total del árbol y el ancho de la capa actual, pero el reto solo nos pide poner los espacios de la izquierda, así que serán la mitad.
65 |
66 | ```js
67 | const spaceCount = (width - layerWidth) / 2;
68 |
69 | response += " ".repeat((width - layerWidth) / 2);
70 | ```
71 |
72 | Ahora cortaremos (literalmente) la capa de nuestro arreglo de decoración, el método `.splice()` funciona similar a un `.slice()`, el primer parámetro será el lugar donde iniciaremos a cortar y el segundo es la cantidad de elementos a cortar, además tiene la particularidad de que al cortar, también reemplaza el arreglo con uno nuevo ya cortado, es decir:
73 |
74 | ```js
75 | console.log(ornaments); // ["@", "$", "%", "#", "@", "*"]
76 |
77 | ornaments.splice(0, 2);
78 |
79 | console.log(ornaments); // ["%", "#", "@", "*"]
80 | ```
81 |
82 | Así que cortaremos `h+1` (cantidad de decoraciones en la capa) elementos del arreglo y los uniremos con un espacio intermedio, luego de esto solo queda agregar el salto de línea.
83 |
84 | ```js
85 | response +=
86 | " ".repeat((width - layerWidth) / 2) + // Espacios
87 | ornaments.splice(0, h + 1).join(" ") + // Decoración
88 | "\n"; // Salto de línea
89 | ```
90 |
91 | Y solo nos queda agregar el tronco del árbol, que tendrá que ir a la mitad del árbol, así que la cantidad de espacios es `width/2`
92 |
93 | ```js
94 | response += " ".repeat(width / 2) + "|" + "\n";
95 | ```
96 |
--------------------------------------------------------------------------------
/retos/reto-10/main.ts:
--------------------------------------------------------------------------------
1 | function createChristmasTree(ornaments, height) {
2 | let response = "";
3 | const width = (height * 2) - 1
4 |
5 | ornaments = [...ornaments.repeat(60)]
6 |
7 | for (const h of Array.from({ length: height }).keys()) {
8 | const layerWidth = (h * 2) + 1
9 |
10 | response += " ".repeat(
11 | (width - layerWidth) / 2
12 | )
13 | + ornaments.splice(0, h + 1).join(" ")
14 | + "\n"
15 | }
16 | response += " ".repeat(width / 2) + "|" + "\n"
17 |
18 | return response
19 | }
--------------------------------------------------------------------------------
/retos/reto-11/README.md:
--------------------------------------------------------------------------------
1 | # Reto 11
2 |
3 | En el taller de Santa, los elfos aman los acertijos 🧠. Este año, han creado uno especial: un desafío para formar un palíndromo navideño.
4 |
5 | **Un palíndromo es una palabra que se lee igual hacia adelante y hacia atrás.** Los elfos quieren saber si es posible formar un palíndromo haciendo, como mucho, un intercambio de letras.
6 |
7 | Crea una función getIndexsForPalindrome que reciba una cadena de caracteres y devolverá:
8 |
9 | - Si ya es un palíndromo, un array vacío.
10 | - Si no es posible, null.
11 | - Si se puede formar un palíndromo con un cambio, un array con las dos posiciones (índices) que se deben intercambiar para poder crearlo.
12 |
13 | Por ejemplo:
14 |
15 | ```js
16 | getIndexsForPalindrome('anna') // []
17 | getIndexsForPalindrome('abab') // [0, 1]
18 | getIndexsForPalindrome('abac') // null
19 | getIndexsForPalindrome('aaaaaaaa') // []
20 | getIndexsForPalindrome('aaababa') // [1, 3]
21 | getIndexsForPalindrome('caababa') // null
22 | ```
23 |
24 | Si se puede formar el palíndromo con diferentes intercambios, **siempre se debe devolver el primero que se encuentre.**
25 |
26 | # Solución
27 |
28 | Recorreremos todos los `swaps` posibles, es decir, cada una de las posibilidades que tenemos para intercambiar dos posiciones, así que debemos generar primero los índices que vamos a intercambiar:
29 |
30 | ```js
31 | for (let a = 0; a < word.length; a++) {
32 | for (let b = 0; b < word.length; b++) {
33 | console.log([a, b]);
34 | }
35 | }
36 | ```
37 |
38 | Este código nos dará por cada letra de `word`, todas las otras letras de `word` con las que puede intercambiarse, si `word` es `"Hola"`, la `"H"` tendrá otro ciclo (el ciclo de b), donde estará en la primera iteración `"H"`, luego `"o"`, luego `"l"` y luego `"a"`.
39 |
40 | Ahora realizaremos el intercambio, por comodidad trabajaremos `word` como un arreglo, cuidado con la variable `aux`, ya que sin esta no es posible el intercambio.
41 |
42 | ```js
43 | let swapped = [...word];
44 | let aux = word[a];
45 | swapped[a] = word[b];
46 | swapped[b] = aux;
47 | ```
48 |
49 | Ahora solo debemos comparar cada mitad de la palabra, esto es posible porque en los casos de tamaño impar, la letra del centro da igual. Así que cortaremos desde 0 hasta el `Math.floor(word.length/2)`, que redondeará hacia abajo, y luego desde `Math.ceil(word.length/2)` que redondeará hacia arriba.
50 |
51 | ```js
52 | let left = swapped.slice(0, Math.floor(word.length / 2)).join("");
53 | let right = swapped
54 | .slice(Math.ceil(word.length / 2))
55 | .reverse()
56 | .join("");
57 | ```
58 |
59 | Para revisar si es palíndromo uno de los strings debe de estar reversado. Si los dos índices son iguales, eso significa que la palabra es palíndroma y no necesita ningún cambio, entonces tendremos que usar otro condicional:
60 |
61 | ```js
62 | if (left == right) {
63 | if(a == b) {
64 | return []
65 | }
66 | return [a, b];
67 | }
68 | ```
69 |
70 | # Optimización
71 |
72 | Muy similar a retos anteriores, se usa `for .. of` y arreglos con dos posiciones en lugar de condicionales, revisar retos anteriores donde está mejor explicada este tipo de optimización.
--------------------------------------------------------------------------------
/retos/reto-11/main.ts:
--------------------------------------------------------------------------------
1 | function getIndexsForPalindrome(word) {
2 | let res: any = null
3 | for (const a of Array.from({ length: word.length }).keys()) {
4 | for (const b of Array.from({ length: word.length }).keys()) {
5 | let swapped = [...word]
6 | let aux = word[a]
7 | swapped[a] = word[b]
8 | swapped[b] = aux
9 |
10 | let left = swapped.slice(0, Math.floor(word.length / 2)).join("")
11 | let right = swapped.slice(Math.ceil(word.length / 2)).reverse().join("")
12 |
13 | res = [
14 | [
15 | null
16 | , [
17 | []
18 | , [a, b]
19 | ][+((a + b) > 0)]
20 | ][+(left == right)]
21 | , res
22 | ][+!!res]
23 | }
24 | }
25 | return res
26 | }
--------------------------------------------------------------------------------
/retos/reto-12/main.ts:
--------------------------------------------------------------------------------
1 | function checkIsValidCopy(original, copy) {
2 | let i = -1
3 | let res = true
4 |
5 | for (let o of [...original]) {
6 | i++;
7 |
8 | if (o.toLowerCase() == copy[i]) continue;
9 |
10 | let simbolos = [o, o.toLowerCase(), "#", "+", ":", ".", " ", ""]
11 |
12 | res = [false, [false, true][
13 | +(
14 | simbolos
15 | .slice(
16 | (/([A-Z])|([a-z])|(\#)|(\+)|(\:)|(\.)|(\s)/g
17 | .exec(o)?.lastIndexOf(o) ?? 1) - 1
18 | )
19 | .includes(copy[i])
20 | )
21 | ]][+res]
22 |
23 | }
24 | return res
25 | }
--------------------------------------------------------------------------------
/retos/reto-13/main.ts:
--------------------------------------------------------------------------------
1 | function calculateTime(deliveries) {
2 | const max = 3600 * 7
3 | let time = 0
4 |
5 | for (let d of deliveries) {
6 | const numbers = d.split(":")
7 | time += (+numbers[0]) * 3600
8 | + (+numbers[1]) * 60
9 | + (+numbers[2])
10 | }
11 |
12 | const signo = (max - time) > 0
13 |
14 | time = Math.abs(max - time)
15 |
16 | let response = ["", "-"][+signo]
17 |
18 | response += (~~(time / 3600) + "").padStart(2, "0") + ":"
19 | time %= 3600
20 | response += (~~(time / 60) + "").padStart(2, "0") + ":"
21 | time %= 60
22 | response += (time + "").padStart(2, "0")
23 |
24 | return response
25 | }
--------------------------------------------------------------------------------
/retos/reto-14/main.ts:
--------------------------------------------------------------------------------
1 | function maxGifts(houses) {
2 | let incluir = 0;
3 | let excluir = 0;
4 | let noIncluirAnterior;
5 |
6 | for (let h of houses) {
7 | noIncluirAnterior = [excluir, incluir][+(incluir > excluir)];
8 |
9 | incluir = excluir + h;
10 | excluir = noIncluirAnterior;
11 | }
12 |
13 | return [excluir, incluir][+(incluir > excluir)];
14 | }
--------------------------------------------------------------------------------
/retos/reto-2/README.md:
--------------------------------------------------------------------------------
1 | # Reto 2
2 |
3 | En el taller de Santa, los elfos tienen una **lista de regalos** que desean fabricar y un conjunto limitado de materiales.
4 |
5 | Los regalos son cadenas de texto y los materiales son caracteres. Tu tarea es escribir una función que, dada una lista de regalos y los materiales disponibles, devuelva una **lista de los regalos que se pueden fabricar**.
6 |
7 | Un regalo se puede fabricar si contamos con todos los materiales necesarios para fabricarlo.
8 |
9 | ```js
10 | const gifts = ["tren", "oso", "pelota"];
11 | const materials = "tronesa";
12 |
13 | manufacture(gifts, materials); // ["tren", "oso"]
14 | // 'tren' SÍ porque sus letras están en 'tronesa'
15 | // 'oso' SÍ porque sus letras están en 'tronesa'
16 | // 'pelota' NO porque sus letras NO están en 'tronesa'
17 |
18 | const gifts = ["juego", "puzzle"];
19 | const materials = "jlepuz";
20 |
21 | manufacture(gifts, materials); // ["puzzle"]
22 |
23 | const gifts = ["libro", "ps5"];
24 | const materials = "psli";
25 |
26 | manufacture(gifts, materials); // []
27 | ```
28 |
29 | # Solución
30 |
31 | Vamos a basar la solución en un `Set` que al cambiar de tamaño nos dirá si ese regalo contiene más letras de las que contenemos en materiales.
32 |
33 | La solución se basa en filtrar los regalos que cumplan con que el `Set` de la unión de `g` (regalo) y `materials` (`g + materials`), tengan la misma longitud que `materials`, esto se logra porque `Set` no va a permitir elementos repetidos.
34 |
35 | Usamos *spread operator* para separar en letras `g + materials`, ya que `Set` pide cada elemento como argumento, más no un solo arreglo, así que lo convertimos a partes separadas. Luego se vuelve a usar para convertir `Set` en un arreglo. *Spread operator* separa en pedacitos cada uno, así que al ponerlo dentro de un arreglo vacío, cada parte va a pasar a formar parte del arreglo.
36 |
37 | ## Ejemplo
38 |
39 | ```ts
40 | const g = "tren" // g en la primer iteración
41 |
42 | const x = [...new Set(...[g + materials])]
43 |
44 | console.log(x) // [ "t", "r", "e", "n", "o", "s", "a" ]
45 |
46 | ```
47 |
48 | En este caso `materials` es exactamente igual a `x`, por lo tanto tienen la misma longitud, es decir que **sí** podemos construir este regalo.
49 |
--------------------------------------------------------------------------------
/retos/reto-2/main.ts:
--------------------------------------------------------------------------------
1 | function manufacture(gifts, materials) {
2 | return gifts.filter(g => {
3 | const x = [...new Set(...[g + materials])]
4 |
5 | return x.length == materials.length
6 | })
7 | }
8 |
9 | module.exports = manufacture
--------------------------------------------------------------------------------
/retos/reto-2/reto2.test.js:
--------------------------------------------------------------------------------
1 | const manufacture = require('./main.ts')
2 |
3 | test("Test #01 - Returns an Array", () => {
4 | expect(
5 | Array.isArray(
6 | manufacture(['tren', 'oso', 'pelota'], 'tronesa')
7 | )
8 | ).toBe(true)
9 | })
10 |
11 | test("Test #02 - manufacture(['tren', 'oso', 'pelota'], 'tronesa')", () => {
12 | expect(manufacture(['tren', 'oso', 'pelota'], 'tronesa')).toStrictEqual([
13 | "tren",
14 | "oso"
15 | ])
16 | })
17 |
18 |
19 | test("Test #03 - manufacture(['coche', 'muñeca', 'balon'], 'ocmuñalb')", () => {
20 | expect(manufacture(['coche', 'muñeca', 'balon'], 'ocmuñalb')).toStrictEqual([])
21 | })
22 |
23 | test("Test #04 - manufacture(['patineta', 'robot', 'libro'], 'nopor')", () => {
24 | expect(manufacture(['patineta', 'robot', 'libro'], 'nopor')).toStrictEqual([])
25 | })
26 |
27 | test("Test #05 - manufacture([], 'letras')", () => {
28 | expect(manufacture([], 'letras')).toStrictEqual([])
29 | })
--------------------------------------------------------------------------------
/retos/reto-3/README.md:
--------------------------------------------------------------------------------
1 | # Reto 3
2 |
3 | En el taller de Santa, **un elfo travieso** ha estado jugando en la cadena de fabricación de regalos, añadiendo o eliminando un paso no planificado.
4 |
5 | Tienes la secuencia original de pasos en la fabricación original y la secuencia modificada modified que puede incluir un paso extra o faltar un paso.
6 |
7 | Tu tarea es **escribir una función que identifique y devuelva el primer paso extra que se ha añadido o eliminado en la cadena de fabricación**. Si no hay ninguna diferencia entre las secuencias, devuelve una cadena vacía.
8 |
9 | ```js
10 | const original = "abcd";
11 | const modified = "abcde";
12 | findNaughtyStep(original, modified); // 'e'
13 |
14 | const original = "stepfor";
15 | const modified = "stepor";
16 | findNaughtyStep(original, modified); // 'f'
17 |
18 | const original = "abcde";
19 | const modified = "abcde";
20 | findNaughtyStep(original, modified); // ''
21 | ```
22 |
23 | A tener en cuenta:
24 |
25 | - Siempre habrá un paso de diferencia o ninguno.
26 | - La modificación puede ocurrir en cualquier lugar de la cadena.
27 | - La secuencia original puede estar vacía
28 |
29 | # Solución
30 |
31 | Debemos revisar la `cadena más larga`, posición por posición, hasta encontrar la posición que no sea igual en la `cadena más corta`. Se hace esto porque la cadena más larga siempre será la que tiene el paso extra o el paso faltante.
32 |
33 | Hay muchas formas de hacerlo, una de estas es:
34 |
35 | ```js
36 | const [lessWords, mostWords] = [original, modified].sort(
37 | (a, b) => a.length - b.length
38 | );
39 | ```
40 |
41 | Y ahora solo tendremos que encontrar el que no se repita en la `cadena más corta`:
42 |
43 | ```js
44 | return [...mostWords].find((x, i) => lessWords[i] != x) ?? "";
45 | ```
46 |
47 | Hay que tener en cuenta que al ser `strings` debemos usar el spread operator para poder usar los métodos de un array, como `.find()`.
--------------------------------------------------------------------------------
/retos/reto-3/main.ts:
--------------------------------------------------------------------------------
1 | function findNaughtyStep(original, modified) {
2 |
3 | const [lessWords, mostWords] =
4 | [original, modified].sort((a, b) => a.length - b.length)
5 |
6 | return [...mostWords].find((x, i) => lessWords[i] != x) ?? "";
7 | }
8 |
9 | module.exports = findNaughtyStep
--------------------------------------------------------------------------------
/retos/reto-3/reto3.test.js:
--------------------------------------------------------------------------------
1 | const findNaughtyStep = require('./main.ts')
2 |
3 | test("Test #01 - Returns a String", () => {
4 | expect(
5 | typeof findNaughtyStep('abcd', 'abcde')
6 | ).toBe("string")
7 | })
8 |
9 | test("Test #02 - findNaughtyStep('abcd', 'abcde')", () => {
10 | expect(findNaughtyStep('abcd', 'abcde')).toStrictEqual("e")
11 | })
12 |
13 | test("Test #03 - findNaughtyStep('xxxx', 'xxoxx')", () => {
14 | expect(findNaughtyStep('xxxx', 'xxoxx')).toStrictEqual("o")
15 | })
16 |
17 | test("Test #04 - findNaughtyStep('stepfor', 'stepor')", () => {
18 | expect(findNaughtyStep('stepfor', 'stepor')).toStrictEqual("f")
19 | })
20 |
21 | test("Test #05 - findNaughtyStep('iiiii', 'iiiii')", () => {
22 | expect(findNaughtyStep('iiiii', 'iiiii')).toStrictEqual("")
23 | })
--------------------------------------------------------------------------------
/retos/reto-4/README.md:
--------------------------------------------------------------------------------
1 | # Reto 4
2 |
3 | En el taller de Santa 🎅, algunos mensajes navideños han sido escritos de manera peculiar: **las letras dentro de los paréntesis deben ser leídas al revés**.
4 |
5 | **Santa necesita que estos mensajes estén correctamente formateados**. Tu tarea es escribir una función que tome una cadena de texto y revierta los caracteres dentro de cada par de paréntesis, eliminando los paréntesis en el mensaje final.
6 |
7 | Eso sí, ten en cuenta que **pueden existir paréntesis anidados**, por lo que debes invertir los caracteres en el orden correcto.
8 |
9 | ```js
10 | const a = decode("hola (odnum)");
11 | console.log(a); // hola mundo
12 |
13 | const b = decode("(olleh) (dlrow)!");
14 | console.log(b); // hello world!
15 |
16 | const c = decode("sa(u(cla)atn)s");
17 | console.log(c); // santaclaus
18 |
19 | // Paso a paso:
20 | // 1. Invertimos el anidado -> sa(ualcatn)s
21 | // 2. Invertimos el que queda -> santaclaus
22 | ```
23 |
24 | Notas:
25 |
26 | - Las cadenas de entrada siempre estarán bien formadas con paréntesis que coinciden correctamente, no necesitas validarlos.
27 | - En el mensaje final no deben quedar paréntesis.
28 | - El nivel máximo de anidamiento es 2.
29 |
30 | # Solución
31 |
32 | Se usará regex para encontrar primero los paréntesis que, en el caso de ser anidados, sean los que están más en el fondo, esto se logra buscando por cualquier string entre paréntesis que no tenga paréntesis dentro, **o como se entiende en regex, todos los caracteres que no sea paréntesis y estén dentro de otros paréntesis**: `/\(([^()]+)\)/g`
33 |
34 | Ahora con el grupo que definimos en el regex:
35 |
36 | ```js
37 | /\( ( [^()]+ ) \)/g
38 | ^ ^
39 | ```
40 |
41 | Podemos eliminar los paréntesis exteriores y solo conservar el texto, al usar `.replace()` con una expresión regex, el primer argumento es el `match`, pero este incluye los paréntesis, para eso definimos el grupo, ya que el segundo argumento es el primer grupo.
42 |
43 | Así que podemos definir una función que reciba dos argumentos y se encargue de reversar el string, hay que tener en cuenta que un string no se puede reversar directamente, así que primero tendremos que convertirlo a un arreglo y luego de reversarlo volver a convertirlo a string:
44 |
45 | ```js
46 | const replacer = (_, group) => [...group].reverse().join("");
47 | ```
48 |
49 | El reto no consiste de más de 1 paréntesis anidado, así que simplemente podemos ejecutar dos veces el `.replace()` y enviar la solución:
50 |
51 | ```js
52 | return message
53 | .replace(regex, replacer)
54 | .replace(regex, replacer);
55 | ```
56 |
57 | Pero si fuese necesario realizar más de dos veces por la cantidad de paréntesis anidados, podemos hacer un ciclo que se repita las veces que existan '(' en el texto original:
58 |
59 | ```js
60 | let count = message.match(/\(/g).length;
61 |
62 | while (count--) {
63 | // Acá va el código :D
64 | }
65 | ```
66 |
--------------------------------------------------------------------------------
/retos/reto-4/main.ts:
--------------------------------------------------------------------------------
1 | function decode(message) {
2 | const regex = /\(([^()]+)\)/g
3 | const replacer = (_, group) => [...group].reverse().join("")
4 |
5 | return message
6 | .replace(regex, replacer)
7 | .replace(regex, replacer)
8 | }
9 |
10 | module.exports = decode
11 |
--------------------------------------------------------------------------------
/retos/reto-4/reto4.test.js:
--------------------------------------------------------------------------------
1 | const decode = require('./main.ts')
2 |
3 | test("Test #01 - Returns a String", () => {
4 | expect(
5 | typeof decode('hola (odnum)')
6 | ).toBe("string")
7 | })
8 |
9 | test("Test #02 - decode('hola (odnum)')", () => {
10 | expect(decode('hola (odnum)')).toStrictEqual("hola mundo")
11 | })
12 |
13 | test("Test #03 - decode('(olleh) (dlrow)!')", () => {
14 | expect(decode('(olleh) (dlrow)!')).toStrictEqual("hello world!")
15 | })
16 |
17 | test("Test #04 - decode('sa(u(cla)atn)s')", () => {
18 | expect(decode('sa(u(cla)atn)s')).toStrictEqual("santaclaus")
19 | })
20 |
21 | test("Test #05 - decode('((nta)(sa))')", () => {
22 | expect(decode('((nta)(sa))')).toStrictEqual("santa")
23 | })
--------------------------------------------------------------------------------
/retos/reto-5/README.md:
--------------------------------------------------------------------------------
1 | # Reto 5
2 |
3 | Santa 🎅 está probando su nuevo trineo eléctrico, el CyberReindeer, en una carretera del Polo Norte. La carretera se representa con una cadena de caracteres, donde:
4 |
5 | - . = Carretera
6 | - S = Trineo de Santa
7 | - \* = Barrera abierta
8 | - | = Barrera cerrada
9 |
10 | Ejemplo de carretera: `S...|....|.....`
11 |
12 | Cada unidad de tiempo, **el trineo avanza una posición a la derecha**. Si encuentra una barrera cerrada, se detiene hasta que la barrera se abra. Si está abierta, la atraviesa directamente.
13 |
14 | **Todas las barreras empiezan cerradas**, pero después de 5 unidades de tiempo, se abren todas **para siempre.**
15 |
16 | **Crea una función que simule el movimiento del trineo** durante un tiempo dado y **devuelva un array** de cadenas representando el estado de la carretera en cada unidad de tiempo:
17 |
18 | ```js
19 | const road = "S..|...|..";
20 | const time = 10; // unidades de tiempo
21 | const result = cyberReindeer(road, time);
22 |
23 | /* -> result:
24 | [
25 | 'S..|...|..', // estado inicial
26 | '.S.|...|..', // avanza el trineo la carretera
27 | '..S|...|..', // avanza el trineo la carretera
28 | '..S|...|..', // el trineo para en la barrera
29 | '..S|...|..', // el trineo para en la barrera
30 | '...S..._..', // se abre la barrera, el trineo avanza
31 | '..._S.._..', // avanza el trineo la carretera
32 | '..._.S._..', // avanza el trineo la carretera
33 | '..._..S_..', // avanza el trineo la carretera
34 | '..._...S..', // avanza por la barrera abierta
35 | ]
36 | */
37 | ```
38 |
39 | El resultado es un **array donde cada elemento muestra la carretera en cada unidad de tiempo**.
40 |
41 | Ten en cuenta que **si el trineo está en la misma posición que una barrera**, entonces toma su lugar en el array.
42 |
43 | Los elfos se **inspiraron en este** [**reto de Code Wars**](https://www.codewars.com/kata/5d0ae91acac0a50232e8a547/javascript).
44 |
45 | # Solución
46 |
47 | Vamos a basar la solución en un regex que reemplazará `S.` y `S*`, ya que son los únicos movimientos válidos, pero tenemos un problema, y es que si pasamos por un `*`, debemos volver a ponerlo cuando hayamos avanzado nuevamente y esa posición quede atrás.
48 |
49 | Además, no olvidar que en el momento que pasen 5 segundos o iteraciones, debemos convertir todas las `|` a `*`.
50 |
51 | Lo que haremos para conservar los `*` es revisar si el reno se movió, en ese caso guardaremos la posición a la que se movió y en el siguiente movimiento la reemplazaremos, cosa que solo pasará hasta que el reno se pueda mover, ya que nuestro regex solo se ejecuta en movimientos válidos.
52 |
53 | Para guardar la posición y siguiente posición del reno, debemos usar una variable auxiliar, porque la del tiempo no mantiene los mismos valores de la posición del reno, puesto que a veces este no se mueve, esta variable, llamada `a` solo aumentará valor si el reno se movió:
54 |
55 | ```js
56 | const newRoad = road.replace(/S[\.\*]/, `${b}S`);
57 | if (newRoad != road) {
58 | a++;
59 | b = road[a];
60 | }
61 |
62 | // Se agrega el movimiento a la lista
63 |
64 | road = newRoad;
65 | moves.push(road);
66 | ```
67 |
68 | Algunas consideraciones:
69 |
70 | - `b` se debe iniciar en `let b = "."`
71 | - `moves` por defecto ya trae el camino original `let moves = [road]`
72 | - Ya que tenemos el camino original en `moves`, solo ejecutaremos el ciclo `time-1` veces `for (let i = 1; i < time; i++)`
73 |
--------------------------------------------------------------------------------
/retos/reto-5/main.ts:
--------------------------------------------------------------------------------
1 | function cyberReindeer(road, time) {
2 | let moves = [road]
3 | let a = 0
4 | let b = "."
5 |
6 | for (let i = 1; i < time; i++) {
7 | if (i == 5) road = road.replace(/\|/g, "*")
8 | const newRoad = road.replace(/S[\.\*]/, `${b}S`)
9 | if (newRoad != road) {
10 | a++
11 | b = road[a]
12 | }
13 | road = newRoad
14 | moves.push(road)
15 | }
16 |
17 | return moves;
18 | }
19 |
20 | module.exports = cyberReindeer
--------------------------------------------------------------------------------
/retos/reto-5/reto5.test.js:
--------------------------------------------------------------------------------
1 | const cyberReindeer = require('./main.ts')
2 |
3 | test("Test #01 - Returns an Array", () => {
4 | expect(
5 | Array.isArray(
6 | cyberReindeer('S..|...|..', 10)
7 | )
8 | ).toBe(true)
9 | })
10 |
11 | test("Test #02 - cyberReindeer('S..|...|..', 10)", () => {
12 | expect(cyberReindeer('S..|...|..', 10)).toStrictEqual([
13 | "S..|...|..",
14 | ".S.|...|..",
15 | "..S|...|..",
16 | "..S|...|..",
17 | "..S|...|..",
18 | "...S...*..",
19 | "...*S..*..",
20 | "...*.S.*..",
21 | "...*..S*..",
22 | "...*...S.."
23 | ])
24 | })
25 |
26 |
27 | test("Test #03 - cyberReindeer('S.|.', 4)", () => {
28 | expect(cyberReindeer('S.|.', 4)).toStrictEqual([
29 | "S.|.",
30 | ".S|.",
31 | ".S|.",
32 | ".S|."
33 | ])
34 | })
35 |
36 | test("Test #04 - cyberReindeer('S.|.|.', 7)", () => {
37 | expect(cyberReindeer('S.|.|.', 7)).toStrictEqual([
38 | "S.|.|.",
39 | ".S|.|.",
40 | ".S|.|.",
41 | ".S|.|.",
42 | ".S|.|.",
43 | "..S.*.",
44 | "..*S*."
45 | ])
46 | })
47 |
48 | test("Test #05 - cyberReindeer('S.|..', 6)", () => {
49 | expect(cyberReindeer('S.|..', 6)).toStrictEqual([
50 | "S.|..",
51 | ".S|..",
52 | ".S|..",
53 | ".S|..",
54 | ".S|..",
55 | "..S.."
56 | ])
57 | })
58 |
59 | test("Test #06 - cyberReindeer('S.|.|.|......|.||.........', 8)", () => {
60 | expect(cyberReindeer('S.|.|.|......|.||.........', 8)).toStrictEqual([
61 | "S.|.|.|......|.||.........",
62 | ".S|.|.|......|.||.........",
63 | ".S|.|.|......|.||.........",
64 | ".S|.|.|......|.||.........",
65 | ".S|.|.|......|.||.........",
66 | "..S.*.*......*.**.........",
67 | "..*S*.*......*.**.........",
68 | "..*.S.*......*.**........."
69 | ])
70 | })
--------------------------------------------------------------------------------
/retos/reto-6/README.md:
--------------------------------------------------------------------------------
1 | # Reto 6
2 |
3 | Los elfos están catalogando los renos de Santa 🦌 según la distancia que pueden recorrer.
4 |
5 | Para ello tienen una cadena de texto movements donde cada caracter representa la dirección del movimiento del reno:
6 |
7 | - \> = Avanza a la derecha
8 | - \< = Avanza a la izquierda
9 | - \* = Puede avanzar o retroceder
10 |
11 | Por ejemplo, si el movimiento es >>\*<, va hacia la derecha dos veces, luego puede ir a derecha o izquierda (lo que maximice la distancia recorrida final) y luego ir a la izquierda.
12 |
13 | Los elfos quieren saber cuál es la máxima distancia que recorre el reno al **finalizar todos los movimientos**.
14 |
15 | **En el ejemplo anterior, la máxima distancia que recorre el reno es 2**. Va a la derecha dos veces +2, luego con el \* puede ir a la derecha otra vez para maximizar la distancia +1 y luego va a la izquierda -1.
16 |
17 | Crea una función maxDistance que reciba la cadena de texto movements y devuelva **la máxima distancia** que puede recorrer el reno en **cualquier dirección**:
18 |
19 | ```js
20 | const movements = ">>*<";
21 | const result = maxDistance(movements);
22 | console.log(result); // -> 2
23 |
24 | const movements2 = "<<<>";
25 | const result2 = maxDistance(movements2);
26 | console.log(result2); // -> 2
27 |
28 | const movements3 = ">***>";
29 | const result3 = maxDistance(movements3);
30 | console.log(result3); // -> 5
31 | ```
32 |
33 | Ten en cuenta que no importa si es a la izquierda o la derecha, la distancia es **el valor absoluto de la distancia recorrida máxima al finalizar los movimientos**.
34 |
35 | # Solución
36 |
37 | Lo primero que debemos hacer es encontrar la cantidad de movimientos a cada dirección, `>` para derecha y `<` para izquierda, la distancia que en realidad se alejo desde donde inicio, es la resta entre derecha e izquierda, el problema que surge es cuando se movio más veces a la izquierda, ya que nos dará un número negativo, ya que de todas formas el número esta bien, solo debemos quitar el negativo, esto se conoce como _valor absoluto_ y lo logramos con `Math.abs()`
38 |
39 | ```js
40 | Math.abs(-5); // 5
41 | ```
42 |
43 | Encontrar la cantidad de movimientos a cada dirección se puede lograr de muchas formas, una de ellas usando regex para contar la cantidad de `<` `>`:
44 |
45 | ```js
46 | let distance = 0;
47 |
48 | let right = movements.match(/>/g)?.length ?? 0;
49 | let left = movements.match(//g)?.length ?? 0
5 | let left = movements.match(/ {
4 | expect(
5 | typeof maxDistance('>>*<')
6 | ).toBe('number')
7 | })
8 |
9 | test('Test #02 - maxDistance(">>*<")', () => {
10 | expect(maxDistance(">>*<")).toStrictEqual(2)
11 | })
12 |
13 |
14 | test('Test #03 - maxDistance("<<<<<")', () => {
15 | expect(maxDistance("<<<<<")).toStrictEqual(5)
16 | })
17 |
18 | test('Test #04 - maxDistance(">***>")', () => {
19 | expect(maxDistance(">***>")).toStrictEqual(5)
20 | })
21 |
22 | test('Test #05 - maxDistance("**********")', () => {
23 | expect(maxDistance("**********")).toStrictEqual(10)
24 | })
25 |
26 | test('Test #06 - maxDistance("<<**>>")', () => {
27 | expect(maxDistance("<<**>>")).toStrictEqual(2)
28 | })
--------------------------------------------------------------------------------
/retos/reto-7/README.md:
--------------------------------------------------------------------------------
1 | # Reto 7
2 |
3 | Santa está experimentando con nuevos diseños de regalos y **necesita tu ayuda para visualizarlos en 3D**.
4 |
5 | Tu tarea es escribir una función que, dado un tamaño n (entero), **genere un dibujo de un regalo en 3D** utilizando caracteres ASCII.
6 |
7 | Las líneas de los regalos se dibujan con `#` y las caras con el símbolo que nos pasan como parámetro:
8 |
9 | ```js
10 | drawGift(4, "+");
11 |
12 | /*
13 | ####
14 | #++##
15 | #++#+#
16 | ####++#
17 | #++#+#
18 | #++##
19 | ####
20 | */
21 |
22 | drawGift(5, "*");
23 | /*
24 | #####
25 | #***##
26 | #***#*#
27 | #***#**#
28 | #####***#
29 | #***#**#
30 | #***#*#
31 | #***##
32 | #####
33 | */
34 |
35 | drawGift(1, "^");
36 | /*
37 | #
38 | */
39 | ```
40 |
41 | Importante: Nos han dicho que **siempre hay que dejar un salto de línea al final del dibujo**.
42 |
43 | **Nota**: Ten en cuenta que, en los tests, la primera línea se ve empujada por el caracter ".
44 |
45 | # Solución
46 |
47 | Primero debemos definir los números que vamos a usar, es decir, qué tamaño tendrá el regalo y que tamaño tendrá el fondo o la cara de cada regalo, por ejemplo, en un regalo de tamaño 4, el fondo (`+`) es de tamaño 2 y los bordes son del mismo tamaño que el regalo:
48 |
49 | ```
50 | #### // Tamaño 4 = Tamaño del regalo
51 | #++##
52 | #++#+#
53 | ####++#
54 | #++#+#
55 | #++##
56 | #### // Tamaño 4 = Tamaño del regalo
57 | ```
58 |
59 | Debemos tener cuidado con los negativos, ya que si tenemos un cuadrado de tamaño 1, el tamaño del fondo será de -1, para esto podemos usar un condicional cuando lo necesitemos, pero una versión que no aumente la complejidad es agregarle el símbolo contrario de `bgSize + 1`, ya que:
60 |
61 | ```js
62 | size = 3;
63 | bgSize = size - 2; // 1
64 |
65 | bgSize += +!(bgSize + 1);
66 | // +!(2)
67 | // +false
68 | // 0
69 | ```
70 |
71 | ```js
72 | size = 2;
73 | bgSize = size - 2; // 0
74 |
75 | bgSize += +!(bgSize + 1);
76 | // +!(1)
77 | // +false
78 | // 0
79 | ```
80 |
81 | ```js
82 | size = 1;
83 | bgSize = size - 2; // -1
84 |
85 | bgSize += +!(bgSize + 1);
86 | // +!(0)
87 | // +true
88 | // 1
89 | ```
90 |
91 | Esta solución tiene en cuenta que nunca se pedirá un regalo de tamaño 0.
92 |
93 | Ya con estos valores podemos crear la parte de arriba, la parte de abajo y el centro.
94 |
95 | ```js
96 | const top = " ".repeat(size - 1) + "#".repeat(size) + "\n";
97 | const center = "#".repeat(size) + symbol.repeat(bgSize) + "#" + "\n";
98 | const bottom = "#".repeat(size) + "\n";
99 | ```
100 |
101 | Ahora solo debemos calcular lo que va entre estas 3 lineas:
102 |
103 | ```
104 | --------- TOP
105 | #++##
106 | #++#+#
107 | --------- CENTER
108 | #++#+#
109 | #++##
110 | --------- BOTTOM
111 | ```
112 |
113 | Si nos fijamos, podemos ver que ambas partes son iguales, solo que están del revés y una no tiene espacios al inicio, así que primero calcularemos la parte de abajo, con un ciclo que vaya desde `0` hasta `bgSize-1` la definiremos como:
114 |
115 | ```js
116 | const c = "#" + symbol.repeat(bgSize) + "#" + symbol.repeat(a) + "#";
117 | ```
118 |
119 | Que nos dará algo como esto, que deberemos reversar para la parte centro-abajo:
120 |
121 | ```
122 | IT1 - #++##
123 | IT2 - #++#+#
124 | ```
125 |
126 | La siguiente linea reversa y guarda el centro-abajo en una variable:
127 |
128 | ```js
129 | bottomCenter = c + "\n" + bottomCenter;
130 | ```
131 |
132 | Y para la parte de arriba, calcularemos los espacios que van antes con:
133 |
134 | ```js
135 | topCenter += " ".repeat(bgSize - a) + c + "\n";
136 | ```
137 |
138 | Ya con esto, podemos definir nuestra respuesta como la suma de cada una de las partes, menos en el caso en que `size = 1`, acá es cuando tendremos que validar si `size > 0`.
139 |
140 | ## Optimización
141 |
142 | Si revisaste mi solución, verás algunas cosas raras, estas son importantes para lograr el puntaje más alto, ya que se encargan de reducir la complejidad.
143 |
144 | ### Ciclo con Complejidad 0
145 |
146 | Al parecer, hacer uso de `for .. of` no aumenta la complejidad, este itera un arreglo, así que primero tendremos que hacer un arreglo lleno con los números desde `0` hasta `bgSize-1`, la forma de hacerlo sin aumentar la complejidad es:
147 |
148 | ```js
149 | [...Array.from({ length: bgSize }).keys()]
150 | ```
151 |
152 | ### Usar `.repeat()` para evitar el último condicional
153 |
154 | Podemos usar repeat con el valor numerico del signo de `size-1`, ya que el caso en el que necesitamos que no se agreguen todas las partes a la respuesta es cuando `size = 1`, así que si repetimos `+!!(size -1)` veces el texto, en el caso de `size = 1` se repetirá 0 veces.
155 |
156 | ```js
157 | const size = 3
158 | // +!!(size - 1)
159 | // +!!(2)
160 | // +true
161 | // 1
162 | ```
163 |
164 | ```js
165 | const size = 2
166 | // +!!(size - 1)
167 | // +!!(1)
168 | // +true
169 | // 1
170 | ```
171 |
172 | ```js
173 | const size = 1
174 | // +!!(size - 1)
175 | // +!!(0)
176 | // +false
177 | // 0
178 | ```
--------------------------------------------------------------------------------
/retos/reto-7/main.ts:
--------------------------------------------------------------------------------
1 | function drawGift(size, symbol) {
2 | let bgSize = size - 2
3 |
4 | bgSize += +!(bgSize + 1)
5 |
6 | let response = ""
7 |
8 | let topCenter = ""
9 | let bottomCenter = ""
10 |
11 | for (const a of [...Array.from({ length: bgSize }).keys()]) {
12 | const c = "#"
13 | + symbol.repeat(bgSize)
14 | + "#" + symbol.repeat(a) + "#"
15 | bottomCenter = c + "\n" + bottomCenter
16 | topCenter += " ".repeat(bgSize - a) + c + "\n"
17 | }
18 |
19 | response = " ".repeat(size - 1) + "#".repeat(size) + "\n"
20 | + (topCenter
21 | + "#".repeat(size) + symbol.repeat(bgSize) + "#" + "\n"
22 | + bottomCenter
23 | + "#".repeat(size) + "\n").repeat(+!!(size - 1))
24 |
25 | return response
26 | }
27 |
28 | module.exports = drawGift
--------------------------------------------------------------------------------
/retos/reto-8/README.md:
--------------------------------------------------------------------------------
1 | # Reto 8
2 |
3 | Los elfos están muy ocupados en el taller de Santa Claus organizando regalos 🎁 para la víspera de Navidad 🎄.
4 |
5 | El formato de entrada es especial, ya que indica el número de regalos y el tipo de regalo con letras de la a a la z. Por ejemplo, `'66a11b'` significa 66 regalos a y 11 regalos b.
6 |
7 | Los elfos tienen un **sistema especial** para organizar los regalos:
8 |
9 | - Cada 10 regalos del mismo tipo se empaquetan en una caja, representada por {x}. Por ejemplo, 20 regalos tipo a se empaquetan en 2 cajas así: {a}{a}.
10 | - Cada 5 cajas se apilan en un palé, representado por [x]. Por ejemplo, 10 cajas de a se apilan en 2 palés de esta manera: [a][a]
11 | - Cualquier regalo adicional se coloca en una bolsa, representada por () y se colocan todas dentro. Por ejemplo 4 regalos de b se colocan en una bolsa así (bbbb)
12 |
13 | **Los regalos luego se colocan en el siguiente orden**: palés, cajas y bolsas. Y los regalos aparecen en el mismo orden que la cadena de entrada.
14 |
15 | Tu tarea es escribir una función organizeGifts que tome una cadena de regalos como argumento y devuelva una cadena representando el almacén.
16 |
17 | ```js
18 | const result1 = organizeGifts(`76a11b`);
19 | console.log(result1);
20 | // '[a]{a}{a}(aaaaaa){b}(b)'
21 |
22 | /* Explicación:
23 |
24 | 76a: 76 regalos tipo 'a' se empaquetarían en 7 cajas y sobrarían 6 regalos, resultando en 1 palé [a] (por las primeras 5 cajas), 2 cajas sueltas {a}{a} y una bolsa con 6 regalos (aaaaaa)
25 |
26 | 11b: 11 regalos tipo 'b' se empaquetarían en 1 caja y sobraría 1 regalo, resultando en 1 caja suelta {b} y una bolsa con 1 regalo (b) */
27 | ```
28 |
29 | # Solución
30 |
31 | Por cada regalo debemos ir agrupandolos en cantidades de 50, luego de 10 y luego los sobrantes, es decir que por cada 50, vamos a tener un `[a]`, luego por cada 10 un `{a}` y luego por cada sobrante, los agregaremos dentro de parentesis, es decir que si nos sobran 4: `(aaaa)`
32 |
33 | Para separar cada regalo y cada nombre del regalo podemos usar regex:
34 |
35 | ```js
36 | const countGifts = gifts.match(/\d+/g);
37 | const nameGifts = gifts.match(/[^0-9]/g);
38 | ```
39 |
40 | Ya solo quedaria ir iterando uno por uno agregando las cajas necesarias, siendo `c` la cantidad del regalo, `g` el nombre del regalo y `a` un string donde almacenamos las cajas:
41 |
42 | ```js
43 | c = +c;
44 |
45 | a += `[${g}]`.repeat(c / 50);
46 | c %= 50;
47 |
48 | a += `{${g}}`.repeat(c / 10);
49 | c %= 10;
50 |
51 | a += `(${g.repeat(c)})`.repeat(+!!c);
52 | ```
53 |
54 | Solo queda unir estos strings y esa sería nuestra respuesta.
55 |
56 | ## Optimización
57 |
58 | Si revisaste mi solución, verás algunas cosas raras, estas son importantes para lograr el puntaje más alto, ya que se encargan de reducir la complejidad.
59 |
60 | ### Ciclo con Complejidad 0
61 |
62 | Al parecer, hacer uso de `for .. of` no aumenta la complejidad, este itera un arreglo, `countGifts`.
63 |
64 | ### Usar `.repeat()` para evitar el último condicional
65 |
66 | Podemos usar repeat con el valor numerico del signo de `c`, ya que el caso en el que necesitamos que no se agreguen los regalos extra a la respuesta es cuando `c == 0`, así que si repetimos `+!!c` veces el texto, en el caso de `c == 0` se repetirá 0 veces y en cualquier otro caso solo 1.
67 |
--------------------------------------------------------------------------------
/retos/reto-8/main.ts:
--------------------------------------------------------------------------------
1 | function organizeGifts(gifts) {
2 | const countGifts = gifts.match(/\d+/g)
3 | const nameGifts = gifts.match(/[^0-9]/g)
4 |
5 | let response = ""
6 | let i = 0
7 |
8 | for (let c of countGifts) {
9 | const g = nameGifts[i]
10 | let a = ""
11 |
12 | c = +c
13 |
14 | a += `[${g}]`.repeat(c / 50)
15 | c %= 50
16 |
17 | a += `{${g}}`.repeat(c / 10)
18 | c %= 10
19 |
20 | a += `(${g.repeat(c)})`.repeat(+!!c)
21 |
22 | response += a
23 | i++
24 | }
25 |
26 | return response
27 | }
28 |
29 | module.exports = organizeGifts
--------------------------------------------------------------------------------
/retos/reto-9/README.md:
--------------------------------------------------------------------------------
1 | # Reto 9
2 |
3 | Están encendiendo las **luces de Navidad** 🎄 en la ciudad y, como cada año, ¡hay que arreglarlas!
4 |
5 | Las luces son de dos colores: 🔴 y 🟢 . Para que el efecto sea el adecuado, **siempre deben estar alternadas**. Es decir, si la primera luz es roja, la segunda debe ser verde, la tercera roja, la cuarta verde, etc.
6 |
7 | Nos han pedido que escribamos una función adjustLights que, dado un array de strings con el color de cada luz, devuelva el **número mínimo** de luces que hay que cambiar para que estén los colores alternos.
8 |
9 | ```js
10 | adjustLights(['🟢', '🔴', '🟢', '🟢', '🟢'])
11 | // -> 1 (cambias la cuarta luz a 🔴)
12 |
13 | adjustLights(['🔴', '🔴', '🟢', '🟢', '🔴'])
14 | // -> 2 (cambias la segunda luz a 🟢 y la tercera a 🔴)
15 |
16 | adjustLights(['🟢', '🔴', '🟢', '🔴', '🟢'])
17 | // -> 0 (ya están alternadas)
18 |
19 | adjustLights(['🔴', '🔴', '🔴'])
20 | // -> 1 (cambias la segunda luz a 🟢)
21 | ```
22 |
23 | # Solución
24 |
25 | Iremos revisando luz por luz y sumaremos `+1` a un contador en el caso de que la luz anterior sea igual, además en el caso de que sea igual, la cambiaremos.
26 |
27 | ```js
28 | ["🔴", "🔴", "🟢", "🟢", "🔴"];
29 | ```
30 |
31 | Primero revisaremos la primer 🔴, que como es diferente a la anterior (valor por defecto) `""`, así que solo cambiaremos nuestra variable donde guardamos el valor anterior y lo actualizaremos con 🔴.
32 |
33 | Luego revisaremos la segunda 🔴, esta si es igual a la anterior, así que sumaremos `+1`a la respuesta y cambiaremos la variable donde guardamos el valor anterior por cualquier cosa diferente a 🔴.
34 |
35 | ```js
36 | if (l == start) {
37 | // l siendo cada elemento de lights
38 | start = "";
39 | res++;
40 | } else {
41 | start = l;
42 | }
43 | ```
44 |
45 | # Optimización
46 |
47 | Similar al reto 7 y 8.
48 |
49 | ## Ignorar Condicionales
50 |
51 | Para evitar tener que validar si `l == start`, sumaremos `+(l == start)`, ya que solo será `+1`en el caso de que `l` sea igual a `start`.
52 |
53 | Y para la asignación del nuevo valor de `start`, podemos usar nuevamente el `0` y el `1` que nos da `+(l == start)`, esta vez viéndolos como el índice a acceder de un arreglo. En `0` estaría el caso en que es diferente, es decir que se tiene que reemplazar por el actual, y en `1` el caso donde es igual y se tiene que reemplazar por cualquier otro valor.
54 |
55 | ```js
56 | start = [l, " "][+(l == start)];
57 | ```
--------------------------------------------------------------------------------
/retos/reto-9/main.ts:
--------------------------------------------------------------------------------
1 | function adjustLights(lights) {
2 | let start = ""
3 | let res = 0;
4 |
5 | for (let l of lights) {
6 | res += +(l == start);
7 | start = [l, " "][+(l == start)]
8 | }
9 |
10 | return res
11 | }
--------------------------------------------------------------------------------