├── 2021 ├── 01-contando-ovejas-para-dormir │ ├── README.md │ ├── index.js │ └── index.test.js ├── 02-ayuda-al-elfo-a-listar-los-regalos │ ├── README.md │ ├── index.js │ └── index.test.js ├── 05-contando-los-dias-para-los-regalos │ ├── README.md │ ├── index.js │ └── index.test.js ├── 13-envuelve-regalos-con-asteriscos │ ├── README.md │ ├── index.js │ └── index.test.js ├── 16-descifrando-los-numeros │ ├── README.md │ ├── index.js │ └── index.test.js └── 20-una-carta-de-pangramas-que │ ├── README.md │ ├── index.js │ └── index.test.js ├── 2022 ├── 01-regalos-de-navidad │ ├── README.md │ ├── index.js │ └── index.test.js ├── 02-horas-extra │ ├── README.md │ ├── index.js │ └── index.test.js ├── 03-cajas-de-regalo │ ├── README.md │ ├── index.js │ └── index.test.js ├── 04-caja-dentro-de-otra-caja │ ├── README.md │ ├── index.js │ └── index.test.js ├── 05-optimizando-viajes │ ├── README.md │ ├── index.js │ └── index.test.js ├── 06-adornos-navideños │ ├── README.md │ ├── index.js │ └── index.test.js ├── 07-inventarios-de-regalos │ ├── README.md │ ├── index.js │ └── index.test.js ├── 08-mecanico │ ├── README.md │ ├── index.js │ └── index.test.js ├── 09-locas-luces │ ├── README.md │ ├── index.js │ └── index.test.js ├── 10-salto-del-trineo │ ├── README.md │ ├── index.js │ └── index.test.js ├── 11-es-scrum-master │ ├── README.md │ ├── index.js │ └── index.test.js ├── 12-trineos-electricos │ ├── README.md │ ├── index.js │ └── index.test.js ├── 13-backup-de-archivos │ ├── README.md │ ├── index.js │ └── index.test.js ├── 14-mejor-camino │ ├── README.md │ ├── index.js │ └── index.test.js ├── 15-decorando-el-arbol │ ├── README.md │ ├── index.js │ └── index.test.js ├── 16-arreglando-las-cartas │ ├── README.md │ ├── index.js │ └── index.test.js ├── 17-regalos-en-sacos │ ├── README.md │ ├── index.js │ └── index.test.js ├── 18-sin-tinta │ ├── README.md │ ├── index.js │ └── index.test.js ├── 19-ordenando-los-regalos │ ├── README.md │ ├── index.js │ └── index.test.js ├── 20-mas-viajes-retadores │ ├── README.md │ ├── index.js │ └── index.test.js ├── 21-tabla-de-regalos │ ├── README.md │ ├── index.js │ └── index.test.js ├── 22-iluminacion-en-sintonia │ ├── README.md │ ├── index.js │ └── index.test.js ├── 23-compilador-de-papa-noel │ ├── README.md │ ├── index.js │ └── index.test.js └── 24-laberinto │ ├── README.md │ ├── index.js │ └── index.test.js ├── 2023 ├── 01-primer-regalo-repetido │ ├── README.md │ ├── index.js │ └── index.test.js ├── 02-ponemos-en-marcha-la-fabrica │ ├── README.md │ ├── index.js │ └── index.test.js ├── 03-el-elfo-travieso │ ├── README.md │ ├── index.js │ └── index.test.js ├── 04-dale-la-vuelta-a-los-parentesis │ ├── README.md │ ├── index.js │ └── index.test.js ├── 05-el-cybertruck-de-santa │ ├── README.md │ ├── index.js │ └── index.test.js ├── 06-los-renos-a-prueba │ ├── README.md │ ├── index.js │ └── index.test.js ├── 07-las-cajas-en-3d │ ├── README.md │ ├── index.js │ └── index.test.js ├── 08-ordenando-el-almacen │ ├── README.md │ ├── index.js │ └── index.test.js ├── 09-alterna-las-luces │ ├── README.md │ ├── index.js │ └── index.test.js ├── 10-crea-tu-propio-arbol-de-navidad │ ├── README.md │ ├── index.js │ └── index.test.js ├── 11-los-elfos-estudiosos │ ├── README.md │ ├── index.js │ └── index.test.js ├── 12-es-una-copia-valida │ ├── README.md │ ├── index.js │ └── index.test.js ├── 13-calculando-el-tiempo │ ├── README.md │ ├── index.js │ └── index.test.js ├── 14-evita-la-alarma │ ├── README.md │ ├── index.js │ └── index.test.js ├── 15-robot-autonomo │ ├── README.md │ ├── index.js │ └── index.test.js ├── 16-despliegue-en-viernes │ ├── README.md │ ├── index.js │ └── index.test.js ├── 17-optimizando-el-alquiler │ ├── README.md │ ├── index.js │ └── index.test.js ├── 18-el-reloj-digital │ ├── README.md │ ├── index.js │ └── index.test.js ├── 19-enfrenta-el-sabotaje │ ├── README.md │ ├── index.js │ └── index.test.js ├── 20-distribuye-el-peso │ ├── README.md │ ├── index.js │ └── index.test.js ├── 21-mensaje-binario │ ├── README.md │ ├── index.js │ └── index.test.js ├── 22-lenguaje-de-programacion │ ├── README.md │ ├── index.js │ └── index.test.js ├── 23-la-comida-de-navidad │ ├── README.md │ ├── index.js │ └── index.test.js ├── 24-brincos-en-la-escalera │ ├── README.md │ ├── index.js │ └── index.test.js └── 25-calculando-distancias │ ├── README.md │ ├── index.js │ └── index.test.js ├── 2024 ├── 01-primer-regalo-repetido │ ├── README.md │ ├── index.js │ └── index.test.js ├── 02-enmarcando-nombres │ ├── README.md │ ├── index.js │ └── index.test.js ├── 03-organizando-el-inventario │ ├── README.md │ ├── index.js │ └── index.test.js ├── 04-decorando-el-arbol-de-navidad │ ├── README.md │ ├── index.js │ └── index.test.js ├── 05-emparejando-botas │ ├── README.md │ ├── index.js │ └── index.test.js ├── 06-regalo-dentro-de-la-caja │ ├── README.md │ ├── index.js │ └── index.test.js ├── 07-el-ataque-del-grinch │ ├── README.md │ ├── index.js │ └── index.test.js ├── 08-la-carrera-de-renos │ ├── README.md │ ├── index.js │ └── index.test.js ├── 09-el-tren-magico │ ├── README.md │ ├── index.js │ └── index.test.js ├── 10-el-ensamblador-elfico │ ├── README.md │ ├── index.js │ └── index.test.js ├── 11-nombres-de-archivos-codificados │ ├── README.md │ ├── index.js │ └── index.test.js ├── 12-cuanto-cuesta-el-arbol │ ├── README.md │ ├── index.js │ └── index.test.js ├── 13-el-robot-esta-de-vuelta │ ├── README.md │ ├── index.js │ └── index.test.js ├── 14-acomodando-los-renos │ ├── README.md │ ├── index.js │ └── index.test.js ├── 15-dibujando-tablas │ ├── README.md │ ├── index.js │ └── index.test.js ├── 16-limpiando-la-nieve-del-camino │ ├── README.md │ ├── index.js │ └── index.test.js ├── 17-busca-las-bombas-del-grinch │ ├── README.md │ ├── index.js │ └── index.test.js ├── 18-la-agenda-magica-de-santa │ ├── README.md │ ├── index.js │ └── index.test.js ├── 19-apila-cajas-magicas-para-repartir-regalos │ ├── README.md │ ├── index.js │ └── index.test.js ├── 20-encuentra-los-regalos-faltantes-y-duplicados │ ├── README.md │ ├── index.js │ └── index.test.js ├── 21-calcula-la-altura-del-arbol-de-navidad │ ├── README.md │ ├── index.js │ └── index.test.js ├── 22-genera-combinaciones-de-regalos │ ├── README.md │ ├── index.js │ └── index.test.js ├── 23-encuentra-los-numeros-perdidos │ ├── README.md │ ├── index.js │ └── index.test.js ├── 24-verifica-si-los-arboles-son-espejos-magicos │ ├── README.md │ ├── index.js │ └── index.test.js ├── 25-ejecuta-el-lenguaje-magico │ ├── README.md │ ├── index.js │ └── index.test.js └── 26-calcula-el-porcentaje-completado │ ├── README.md │ ├── index.js │ └── index.test.js ├── .eslintrc.js ├── .github └── workflows │ └── adventjs.yml ├── .gitignore ├── .husky ├── pre-commit └── pre-push ├── .lintstagedrc.js ├── LICENSE ├── README.md ├── create.sh ├── package-lock.json └── package.json /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | es2021: true, 6 | jest: true, 7 | }, 8 | extends: 'airbnb-base', 9 | overrides: [], 10 | parserOptions: { 11 | ecmaVersion: 'latest', 12 | }, 13 | rules: { 14 | 'max-len': ['error', { code: 120 }], 15 | 'linebreak-style': 'off', 16 | 'no-plusplus': 'off', 17 | 'no-param-reassign': 'off', 18 | 'no-unused-expressions': 'off', 19 | 'no-return-assign': 'off', 20 | 'operator-linebreak': ['error', 'before'], 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /.github/workflows/adventjs.yml: -------------------------------------------------------------------------------- 1 | name: AdventJS 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | types: [opened, synchronize] 9 | 10 | jobs: 11 | linter: 12 | name: ✅ Validate linter 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: ⬇️ Checkout project 17 | uses: actions/checkout@v3 18 | 19 | - name: 🟢 Setup NodeJS 18.x 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: 18.x 23 | cache: npm 24 | cache-dependency-path: './package-lock.json' 25 | 26 | - name: 📥 Install dependencies 27 | run: npm ci 28 | 29 | - name: 📑 Check lint 30 | run: npm run lint 31 | 32 | test: 33 | name: 🧑‍🔬 Test project 34 | runs-on: ubuntu-latest 35 | 36 | steps: 37 | - name: ⬇️ Checkout project 38 | uses: actions/checkout@v3 39 | 40 | - name: 🟢 Setup NodeJS 18.x 41 | uses: actions/setup-node@v3 42 | with: 43 | node-version: 18.x 44 | cache: npm 45 | cache-dependency-path: './package-lock.json' 46 | 47 | - name: 📥 Install dependencies 48 | run: npm ci 49 | 50 | - name: 🧪 Run challenges 51 | run: npm run test -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | export FORCE_COLOR=1 5 | 6 | echo '🎨 Formating and checking staged files before committing!' 7 | 8 | npx lint-staged || 9 | ( 10 | echo '❌ Ooops! Formating and checking process has failed!'; 11 | false; 12 | ) 13 | 14 | echo '✅ Formating and checking process has been successfully completed!'; 15 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | export FORCE_COLOR=1 5 | 6 | echo '🧪 Running tests before pushing!' 7 | 8 | npm run test || ( 9 | echo '❌ Ooops! Tests have failed!'; 10 | false; 11 | ) 12 | 13 | echo '✅ Tests have been successfully completed!'; 14 | -------------------------------------------------------------------------------- /.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '**/*.{js,jsx}': ['eslint'] 3 | } 4 | -------------------------------------------------------------------------------- /2021/01-contando-ovejas-para-dormir/README.md: -------------------------------------------------------------------------------- 1 | # Reto 1: Contando ovejas para dormir 2 | 3 | ## Problema 4 | 5 | Considera una lista/array de ovejas. Cada oveja tiene un nombre y un color. Haz una función que devuelva una lista con todas las ovejas que sean de color rojo y que además su nombre contenga tanto las letras n Y a, sin importar el orden, las mayúsculas o espacios. 6 | 7 | Por ejemplo, si tenemos las ovejas: 8 | 9 | ```js 10 | const ovejas = [ 11 | { name: "Noa", color: "azul" }, 12 | { name: "Euge", color: "rojo" }, 13 | { name: "Navidad", color: "rojo" }, 14 | { name: "Ki Na Ma", color: "rojo" }, 15 | { name: "AAAAAaaaaa", color: "rojo" }, 16 | { name: "Nnnnnnnn", color: "rojo" }, 17 | ]; 18 | ``` 19 | 20 | Al ejecutar el método debería devolver lo siguiente: 21 | 22 | ```js 23 | const ovejasFiltradas = contarOvejas(ovejas); 24 | 25 | console.log(ovejasFiltradas); 26 | 27 | // [{ name: 'Navidad', color: 'rojo' }, 28 | // { name: 'Ki Na Ma', color: 'rojo' }] 29 | ``` 30 | 31 | Recuerda. **Debe contener las dos letras 'a' y 'n' en el nombre.** No cuentes ovejas que sólo tenga una de las letras, debe tener ambas. 32 | -------------------------------------------------------------------------------- /2021/01-contando-ovejas-para-dormir/index.js: -------------------------------------------------------------------------------- 1 | const contarOvejas = (ovejas) => ovejas 2 | .filter( 3 | (oveja) => oveja.color === 'rojo' 4 | && oveja.name.match(/[aA]/) 5 | && oveja.name.match(/[nN]/), 6 | ); 7 | 8 | module.exports = contarOvejas; 9 | -------------------------------------------------------------------------------- /2021/01-contando-ovejas-para-dormir/index.test.js: -------------------------------------------------------------------------------- 1 | const contarOvejas = require('./index'); 2 | 3 | describe('01 => Contando ovejas para dormir', () => { 4 | const testCases = [ 5 | { 6 | input: [ 7 | { name: 'Noa', color: 'azul' }, 8 | { name: 'Euge', color: 'rojo' }, 9 | { name: 'Navidad', color: 'rojo' }, 10 | { name: 'Ki Na Ma', color: 'rojo' }, 11 | { name: 'AAAAAaaaaa', color: 'rojo' }, 12 | { name: 'Nnnnnnnn', color: 'rojo' }, 13 | ], 14 | output: [ 15 | { name: 'Navidad', color: 'rojo' }, 16 | { name: 'Ki Na Ma', color: 'rojo' }, 17 | ], 18 | }, 19 | { 20 | input: [ 21 | { name: 'Noa', color: 'rojo' }, 22 | { name: 'Euge', color: 'rojo' }, 23 | { name: 'Navidad', color: 'rojo' }, 24 | ], 25 | output: [ 26 | { name: 'Noa', color: 'rojo' }, 27 | { name: 'Navidad', color: 'rojo' }, 28 | ], 29 | }, 30 | ]; 31 | 32 | it('should return an array type', () => { 33 | expect(contarOvejas([])).toBeInstanceOf(Array); 34 | }); 35 | 36 | it.each(testCases)('should return a correct array', (testCase) => { 37 | expect(contarOvejas(testCase.input)).toEqual(testCase.output); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /2021/02-ayuda-al-elfo-a-listar-los-regalos/README.md: -------------------------------------------------------------------------------- 1 | # Reto 2: ¡Ayuda al elfo a listar los regalos! 2 | 3 | ## Problema 4 | 5 | Te ha llegado una carta ✉️ con todos los regalos que debes preparar. El tema es que es una cadena de texto y es muy difícil de leer 😱. ¡Menos mal que han puesto cada regalo separado por espacio! (aunque ten cuidado, porque al ser niños, igual han colado más espacios de la cuenta) 6 | 7 | Encima nos hemos dado cuenta que algunas palabras vienen con un \_ delante de la palabra, por ejemplo \_playstation, que significa que está tachado y no se tiene que contar. 8 | 9 | Transforma el texto a un objeto que contenga el nombre de cada regalo y las veces que aparece. Por ejemplo, si tenemos el texto: 10 | 11 | ```js 12 | const carta = "bici coche balón _playstation bici coche peluche"; 13 | ``` 14 | 15 | Al ejecutar el método debería devolver lo siguiente: 16 | 17 | ```js 18 | const regalos = listGifts(carta); 19 | 20 | console.log(regalos); 21 | /* 22 | { 23 | bici: 2, 24 | coche: 2, 25 | balón: 1, 26 | peluche: 1 27 | } 28 | */ 29 | ``` 30 | -------------------------------------------------------------------------------- /2021/02-ayuda-al-elfo-a-listar-los-regalos/index.js: -------------------------------------------------------------------------------- 1 | const listGifts = (letter) => letter.trim().split(' ').reduce((acc, gift) => { 2 | if (!gift.startsWith('_')) { 3 | acc[gift] = (acc[gift] || 0) + 1; 4 | } 5 | return acc; 6 | }, {}); 7 | 8 | module.exports = listGifts; 9 | -------------------------------------------------------------------------------- /2021/02-ayuda-al-elfo-a-listar-los-regalos/index.test.js: -------------------------------------------------------------------------------- 1 | const listGifts = require('./index'); 2 | 3 | describe('02 => Ayuda al elfo a listar los regalos', () => { 4 | const testCases = [ 5 | { 6 | input: 'bici coche balón _playstation bici coche peluche', 7 | output: { 8 | bici: 2, coche: 2, balón: 1, peluche: 1, 9 | }, 10 | }, 11 | { 12 | input: 'bici coche balón _playstation bici coche peluche bici coche balón _playstation bici coche peluche', 13 | output: { 14 | bici: 4, coche: 4, balón: 2, peluche: 2, 15 | }, 16 | }, 17 | ]; 18 | 19 | it('should return an object type', () => { 20 | expect(typeof listGifts('bici coche balón _playstation bici coche peluche')).toBe('object'); 21 | }); 22 | 23 | it.each(testCases)('should return an object with the correct values', (testCase) => { 24 | expect(listGifts(testCase.input)).toEqual(testCase.output); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /2021/05-contando-los-dias-para-los-regalos/README.md: -------------------------------------------------------------------------------- 1 | # Reto 5: Contando los dias para los regalos 2 | 3 | ## Problema 4 | 5 | Con la emoción, ya estamos empezando a contar los días del calendario hasta el 25 de diciembre 📆. 6 | 7 | Para ayudar a esto, vamos a crear una función que pasándole una instancia de Date nos diga el número de días que faltan. 8 | 9 | Veamos unos ejemplos: 10 | 11 | ```js 12 | const date1 = new Date("Dec 1, 2021"); 13 | daysToXmas(date1); // 24 14 | const date2 = new Date("Dec 24, 2021 00:00:01"); 15 | daysToXmas(date2); // 1 16 | const date3 = new Date("Dec 24, 2021 23:59:59"); 17 | daysToXmas(date3); // 1 18 | const date4 = new Date("December 20, 2021 03:24:00"); 19 | daysToXmas(date4); // 5 20 | ``` 21 | 22 | El resultado tiene que ser un número entero y, como ves, aunque falte un segundo hasta el siguiente día, se entiende que todavía falta un día. 23 | 24 | ¡Pero ojo! También hay que indicar si la fecha es del mismo día (devolveríamos 0) o si es una fecha futura (devolveríamos el número de días en negativo -): 25 | 26 | ```js 27 | const date = new Date("Dec 25, 2021"); 28 | daysToXmas(date); // 0 29 | const date1 = new Date("Dec 26, 2021"); 30 | daysToXmas(date1); // -1 31 | const date2 = new Date("Dec 31, 2021"); 32 | daysToXmas(date2); // -6 33 | const date3 = new Date("Jan 1, 2022 00:00:01"); 34 | daysToXmas(date3); // -7 35 | const date4 = new Date("Jan 1, 2022 23:59:59"); 36 | daysToXmas(date4); // -7 37 | ``` 38 | 39 | Por cierto, la fecha de referencia para saber si es 25 de diciembre es `Dec 25, 2021`. 40 | -------------------------------------------------------------------------------- /2021/05-contando-los-dias-para-los-regalos/index.js: -------------------------------------------------------------------------------- 1 | const daysToXmas = (date) => { 2 | const xmas = new Date(2021, 11, 25); 3 | const diff = xmas.getTime() - date.getTime(); 4 | return Math.ceil(diff / (1000 * 60 * 60 * 24)); 5 | }; 6 | 7 | module.exports = daysToXmas; 8 | -------------------------------------------------------------------------------- /2021/05-contando-los-dias-para-los-regalos/index.test.js: -------------------------------------------------------------------------------- 1 | const daysToXmas = require('./index'); 2 | 3 | describe('05 => Contando los días para los regalos', () => { 4 | const testCases = [ 5 | { 6 | input: new Date('Dec 25, 2021'), 7 | output: 0, 8 | }, 9 | { 10 | input: new Date('Dec 26, 2021'), 11 | output: -1, 12 | }, 13 | { 14 | input: new Date('Dec 31, 2021'), 15 | output: -6, 16 | }, 17 | { 18 | input: new Date('Jan 1, 2022 00:00:01'), 19 | output: -7, 20 | }, 21 | { 22 | input: new Date('Jan 1, 2022 23:59:59'), 23 | output: -7, 24 | }, 25 | ]; 26 | 27 | it('should return a number type', () => { 28 | expect(typeof daysToXmas(new Date('Dec 25, 2021'))).toBe('number'); 29 | }); 30 | 31 | it.each(testCases)('should return $output', (testCase) => { 32 | expect(daysToXmas(testCase.input)).toBe(testCase.output); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /2021/13-envuelve-regalos-con-asteriscos/README.md: -------------------------------------------------------------------------------- 1 | # Reto 13: Envuelve regalos con asteriscos 2 | 3 | ## Problema 4 | 5 | ¡Hay demasiados regalos 🎁! Y envolverlos es una locura... 6 | 7 | Vamos a crear una función que pasándole un array de regalos, nos devuelva otro array pero donde todos los regalos han sido envueltos con asteriscos tanto por arriba como por los lados. 8 | 9 | Sólo tienes que tener en cuenta unas cosillas ✌️: 10 | 11 | Si el array está vacío, devuelve un array vacío 12 | Los regalos son emojis 🎁... por lo que tenlo en cuenta a la hora de contar su longitud... 13 | Por suerte, cada posición del array siempre tiene la misma longitud... 14 | 15 | ```js 16 | wrapGifts(["📷", "⚽️"]) 17 | /* Resultado: 18 | [ '****', 19 | '*📷*', 20 | '*⚽️*', 21 | '****' 22 | ] 23 | */ 24 | 25 | wrapGifts(["🏈🎸", "🎮🧸"]) 26 | /* Resultado: 27 | [ '******', 28 | '*🏈🎸*', 29 | '*🎮🧸*', 30 | '******' 31 | ] 32 | */ 33 | 34 | wrapGifts(["📷"]) 35 | /* Resultado: 36 | [ '****', 37 | '*📷*', 38 | '****' 39 | ] 40 | */ 41 | ``` 42 | -------------------------------------------------------------------------------- /2021/13-envuelve-regalos-con-asteriscos/index.js: -------------------------------------------------------------------------------- 1 | const wrapGifts = (gifts) => { 2 | const height = Math.max(0, ...gifts.map((giftsGroup) => giftsGroup.length)); 3 | const wrapper = `*${'*'.repeat(height)}*`; 4 | const giftsWrapped = gifts.map((giftsGroup) => `*${giftsGroup}*`); 5 | return [wrapper, ...giftsWrapped, wrapper]; 6 | }; 7 | 8 | module.exports = wrapGifts; 9 | -------------------------------------------------------------------------------- /2021/13-envuelve-regalos-con-asteriscos/index.test.js: -------------------------------------------------------------------------------- 1 | const wrapGifts = require('./index'); 2 | 3 | describe('13 => Envuelve regalos con asteriscos', () => { 4 | const testCases = [ 5 | { 6 | input: ['📷', '⚽️'], 7 | output: [ 8 | '****', 9 | '*📷*', 10 | '*⚽️*', 11 | '****', 12 | ], 13 | }, 14 | { 15 | input: ['🏈🎸', '🎮🧸'], 16 | output: [ 17 | '******', 18 | '*🏈🎸*', 19 | '*🎮🧸*', 20 | '******', 21 | ], 22 | }, 23 | { 24 | input: ['📷'], 25 | output: [ 26 | '****', 27 | '*📷*', 28 | '****', 29 | ], 30 | }, 31 | ]; 32 | 33 | it('should return an array type', () => { 34 | expect(Array.isArray(wrapGifts([]))).toBe(true); 35 | }); 36 | 37 | it.each(testCases)('should return an array with the correct values', (testCase) => { 38 | expect(wrapGifts(testCase.input)).toEqual(testCase.output); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /2021/16-descifrando-los-numeros/README.md: -------------------------------------------------------------------------------- 1 | # Reto 16: Descifrando los numeros 2 | 3 | ## Problema 4 | 5 | Lara Eloft ha encontrado unos restos élficos en una cueva, cerca del Círculo Polar Ártico, a 8 km al norte de Rovaniemi. 6 | 7 | Ahora se encuentra descifrando unas misteriosas cartas que contiene información sobre unos números que le puede hacer llegar al próximo objetivo. 8 | 9 | Lara tiene un documento que contiene una serie de números que pueden ser usados para descifrarlos: 10 | 11 | ```txt 12 | Símbolo Valor 13 | . 1 14 | , 5 15 | : 10 16 | ; 50 17 | ! 100 18 | ``` 19 | 20 | Lara, además, ha notado una cosa. **Los símbolos se restan si están inmediatamente a la izquierda de otro mayor. 😱** 21 | 22 | Tenemos que crear una función que nos pasa una cadena de texto con símbolos y tenemos que transformarlo al número correcto. ¡Ojo! Si encuentras un símbolo que no entendemos, mejor que devolvamos un `NaN`: 23 | 24 | ```js 25 | decodeNumbers('...') // 3 26 | decodeNumbers('.,') // 4 (5 - 1) 27 | decodeNumbers(',.') // 6 (5 + 1) 28 | decodeNumbers(',...') // 8 (5 + 3) 29 | decodeNumbers('.........!') // 107 (1 + 1 + 1 + 1 + 1 + 1 + 1 - 1 + 100) 30 | decodeNumbers('.;') // 49 (50 - 1) 31 | decodeNumbers('..,') // 5 (-1 + 1 + 5) 32 | decodeNumbers('..,!') // 95 (1 - 1 - 5 + 100) 33 | decodeNumbers('.;!') // 49 (-1 -50 + 100) 34 | decodeNumbers('!!!') // 300 35 | decodeNumbers(';!') // 50 36 | decodeNumbers(';.W') // NaN 37 | ``` 38 | -------------------------------------------------------------------------------- /2021/16-descifrando-los-numeros/index.js: -------------------------------------------------------------------------------- 1 | const decodeNumbers = (symbols) => { 2 | const numbers = { 3 | '.': 1, 4 | ',': 5, 5 | ':': 10, 6 | ';': 50, 7 | '!': 100, 8 | }; 9 | 10 | return [...symbols].reduce((acc, currentSymbol, index, symbol) => ( 11 | acc + (numbers[symbol[index + 1]] > numbers[currentSymbol] 12 | ? -numbers[currentSymbol] 13 | : numbers[currentSymbol]) 14 | ), 0); 15 | }; 16 | 17 | module.exports = decodeNumbers; 18 | -------------------------------------------------------------------------------- /2021/16-descifrando-los-numeros/index.test.js: -------------------------------------------------------------------------------- 1 | const decodeNumbers = require('./index'); 2 | 3 | describe('16 => Descifrando los numeros', () => { 4 | const testCases = [ 5 | { 6 | input: '...', 7 | output: 3, 8 | }, 9 | { 10 | input: '.,', 11 | output: 4, 12 | }, 13 | { 14 | input: ',.', 15 | output: 6, 16 | }, 17 | { 18 | input: ',...', 19 | output: 8, 20 | }, 21 | { 22 | input: '.........!', 23 | output: 107, 24 | }, 25 | { 26 | input: '.;', 27 | output: 49, 28 | }, 29 | { 30 | input: '..,', 31 | output: 5, 32 | }, 33 | { 34 | input: '..,!', 35 | output: 95, 36 | }, 37 | { 38 | input: '.;!', 39 | output: 49, 40 | }, 41 | { 42 | input: '!!!', 43 | output: 300, 44 | }, 45 | { 46 | input: ';!', 47 | output: 50, 48 | }, 49 | { 50 | input: ';.W', 51 | output: NaN, 52 | }, 53 | ]; 54 | 55 | it('should return a number type', () => { 56 | expect(typeof decodeNumbers('...')).toBe('number'); 57 | }); 58 | 59 | it.each(testCases)('should return $output when input is $input', ({ input, output }) => { 60 | expect(decodeNumbers(input)).toBe(output); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /2021/20-una-carta-de-pangramas-que/README.md: -------------------------------------------------------------------------------- 1 | # Reto 20: Una carta de pangramas que 2 | 3 | ## Problema 4 | 5 | En la clase de español del pueblo de Laponia han creado un reto a la hora de escribir la carta a Papa Noél 🎅: la carta ✉️ tiene que contener todas las letras del alfabeto. 6 | 7 | Desde el taller de Santa 🎅 se han enterado y quieren escribir **una función** que les diga si realmente la cadena de texto que les llega tiene, efectivamente, todas las letras del abecedario español 🔎. 8 | 9 | Hay que tener en cuenta las letras en mayúscula y que las letras con acento y diéresis se consideran iguales. Por ejemplo la á y la ä cuenta como una a. 10 | 11 | Vamos a ver unos ejemplos de frases: 12 | 13 | ```js 14 | pangram('Extraño pan de col y kiwi se quemó bajo fugaz vaho') // true 15 | pangram('Jovencillo emponzoñado y con walkman: ¡qué figurota exhibes!') // true 16 | 17 | pangram('Esto es una frase larga pero no tiene todas las letras del abecedario') // false 18 | pangram('De la a a la z, nos faltan letras') // false 19 | ``` 20 | -------------------------------------------------------------------------------- /2021/20-una-carta-de-pangramas-que/index.js: -------------------------------------------------------------------------------- 1 | function pangram(str) { 2 | const cleanedStr = str 3 | .toLowerCase() 4 | .replace(/[áä]/g, 'a') 5 | .replace(/[éë]/g, 'e') 6 | .replace(/[íï]/g, 'i') 7 | .replace(/[óö]/g, 'o') 8 | .replace(/[úü]/g, 'u'); 9 | 10 | const uniqueLetters = new Set(cleanedStr); 11 | const allLetters = new Set('abcdefghijklmnñopqrstuvwxyz'); 12 | 13 | return [...allLetters].every((letter) => uniqueLetters.has(letter)); 14 | } 15 | 16 | module.exports = pangram; 17 | -------------------------------------------------------------------------------- /2021/20-una-carta-de-pangramas-que/index.test.js: -------------------------------------------------------------------------------- 1 | const pangram = require('./index'); 2 | 3 | describe('20 => Una carta de pangramas que', () => { 4 | const testCases = [ 5 | { 6 | input: 'Extraño pan de col y kiwi se quemó bajo fugaz vaho', 7 | output: true, 8 | }, 9 | { 10 | input: 'Jovencillo emponzoñado y con walkman: ¡qué figurota exhibes!', 11 | output: true, 12 | }, 13 | { 14 | input: 'Esto es una frase larga pero no tiene todas las letras del abecedario', 15 | output: false, 16 | }, 17 | { 18 | input: 'De la a a la z, nos faltan letras', 19 | output: false, 20 | }, 21 | ]; 22 | 23 | it('should return a boolean type', () => { 24 | expect(typeof pangram('')).toBe('boolean'); 25 | }); 26 | 27 | it.each(testCases)('should return $output', ({ input, output }) => { 28 | expect(pangram(input)).toBe(output); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /2022/01-regalos-de-navidad/README.md: -------------------------------------------------------------------------------- 1 | # Reto 1: ¡Automatizando envolver regalos de navidad! 2 | 3 | ## Problema 4 | 5 | Este año los elfos han comprado una máquina que envuelve regalos. Pero... ¡no viene programada! Necesitamos crear un algoritmo que le ayude en la tarea. 6 | 7 | A la máquina se le pasa un array con los regalos. Cada regalo es un string. Necesitamos que la máquina envuelva cada regalo en papel de regalo y lo coloque en un array de regalos envueltos. 8 | 9 | El papel de regalo es el símbolo _ y para envolver un regalo se coloca el símbolo _ de forma que rodee totalmente al string por todos los lados. Por ejemplo: 10 | 11 | ```js 12 | const gifts = ["book", "game", "socks"]; 13 | const wrapped = wrapping(gifts); 14 | console.log(wrapped); 15 | /* [ 16 | "******\n*book*\n******", 17 | "******\n*game*\n******", 18 | "*******\n*socks*\n*******" 19 | ] */ 20 | ``` 21 | 22 | Como ves, el papel de regalo envuelve el string. Por arriba y por abajo, para no dejar ningún hueco, las esquinas también están cubiertas por el papel de regalo. 23 | 24 | Nota:El carácter \n representa un salto de línea. 25 | 26 | ¡Ojo!Asegúrate que pones el número correcto de \* para envolver completamente el string. 27 | 28 | ¡Suerte! 29 | 30 | ## Mi Solución 31 | 32 | ```js 33 | function wrapping(gifts) { 34 | return gifts.map((gift) => { 35 | const repeat = "*".repeat(gift.length + 2); 36 | return `${repeat}\n*${gift}*\n${repeat}`; 37 | }); 38 | } 39 | ``` 40 | -------------------------------------------------------------------------------- /2022/01-regalos-de-navidad/index.js: -------------------------------------------------------------------------------- 1 | function wrapping(gifts) { 2 | return gifts.map((gift) => { 3 | const repeat = '*'.repeat(gift.length + 2); 4 | return `${repeat}\n*${gift}*\n${repeat}`; 5 | }); 6 | } 7 | 8 | module.exports = wrapping; 9 | -------------------------------------------------------------------------------- /2022/01-regalos-de-navidad/index.test.js: -------------------------------------------------------------------------------- 1 | const wrapping = require('./index'); 2 | 3 | describe('01 => Regalos de Navidad', () => { 4 | const testCases = [ 5 | { 6 | input: ['cat', 'game', 'socks'], 7 | output: ['*****\n*cat*\n*****', '******\n*game*\n******', '*******\n*socks*\n*******'], 8 | description: 'should return wrapped gifts with 3 elements', 9 | }, 10 | { 11 | input: ['cat', 'game', 'socks', 'bike'], 12 | output: ['*****\n*cat*\n*****', '******\n*game*\n******', '*******\n*socks*\n*******', '******\n*bike*\n******'], 13 | description: 'should return wrapped gifts with 4 elements', 14 | }, 15 | { 16 | input: ['midu'], 17 | output: ['******\n*midu*\n******'], 18 | description: 'should return wrapped gifts with 1 element', 19 | }, 20 | { 21 | input: ['a'], 22 | output: ['***\n*a*\n***'], 23 | description: 'should return wrapped gifts with 1 element', 24 | }, 25 | { 26 | input: [], 27 | output: [], 28 | description: 'should return a empty array', 29 | }, 30 | ]; 31 | 32 | it('should return an array type', () => { 33 | expect(wrapping([])).toBeInstanceOf(Array); 34 | }); 35 | 36 | it.each(testCases)('$description', (testCase) => { 37 | expect(wrapping(testCase.input)).toEqual(testCase.output); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /2022/02-horas-extra/README.md: -------------------------------------------------------------------------------- 1 | # Reto 2: Nadie quiere hacer horas extra 2 | 3 | ## Problema 4 | 5 | Un millonario ha comprado una red social y no trae buenas noticias. Ha anunciado que cada vez que una jornada de trabajo se pierde por un día festivo, habrá que compensarlo con dos horas extra otro día de ese mismo año. 6 | 7 | Obviamente la gente que trabaja en la empresa no le ha hecho ni pizca de gracia y están preparando un programa que les diga el número de horas extras que harían en el año si se aplicara la nueva norma. 8 | 9 | Al ser trabajo de oficina, su horario laboral es de lunes a viernes. Así que sólo tienes que preocuparte de los días festivos que caen en esos días. 10 | 11 | Dado un año y un array con las fechas de los días festivos, devuelve el número de horas extra que se harían ese año: 12 | 13 | ```js 14 | const year = 2022; 15 | const holidays = ["01/06", "04/01", "12/25"]; // formato MM/DD 16 | 17 | // 01/06 es el 6 de enero, jueves. Cuenta. 18 | // 04/01 es el 1 de abril, un viernes. Cuenta. 19 | // 12/25 es el 25 de diciembre, un domingo. No cuenta. 20 | 21 | countHours(year, holidays); // 2 días -> 4 horas extra en el año 22 | ``` 23 | 24 | Cosas a tener en cuenta y consejos: 25 | 26 | - El año puede ser bisiesto. Haz las comprobaciones que necesitas para ello, si fuese necesario. 27 | - Aunque el 31 de diciembre sea festivo, las horas extra se harán el mismo año y no el siguiente. 28 | - El método Date.getDay() te devuelve el día de la semana de una fecha. El 0 es domingo, el 1 es lunes, etc. 29 | 30 | ## Mi Solución 31 | 32 | ```js 33 | const countHours = (year, holidays) => 34 | holidays 35 | .map((day) => new Date(`${day}/${year}`).getDay()) 36 | .filter((date) => ![0, 6].includes(date)).length * 2; 37 | 38 | const year = 2022; 39 | const holidays = ["01/06", "04/01", "12/25"]; // formato MM/DD 40 | 41 | console.log(countHours(year, holidays)); // -> 4 42 | console.log(countHours(2023, ["01/06", "04/01", "12/25"])); // -> 4 43 | console.log( 44 | countHours(1985, [ 45 | "01/01", 46 | "01/06", 47 | "02/02", 48 | "02/17", 49 | "02/28", 50 | "06/03", 51 | "12/06", 52 | "12/25", 53 | ]) 54 | ); // -> 10 55 | console.log(countHours(2000, ["01/01"])); // -> 0 56 | ``` 57 | -------------------------------------------------------------------------------- /2022/02-horas-extra/index.js: -------------------------------------------------------------------------------- 1 | function countHours(year, holidays) { 2 | return holidays 3 | .map((day) => new Date(`${day}/${year}`).getDay()) 4 | .filter((date) => ![0, 6].includes(date)).length * 2; 5 | } 6 | 7 | module.exports = countHours; 8 | -------------------------------------------------------------------------------- /2022/02-horas-extra/index.test.js: -------------------------------------------------------------------------------- 1 | const countHours = require('./index'); 2 | 3 | describe('02 => Horas extra', () => { 4 | const testCases = [ 5 | { 6 | input: [2022, ['01/06', '04/01', '12/25']], 7 | output: 4, 8 | description: 'should return 4 hours', 9 | }, 10 | { 11 | input: [2023, ['01/06', '04/01', '12/25']], 12 | output: 4, 13 | description: 'should return 4 hours', 14 | }, 15 | { 16 | input: [ 17 | 1985, 18 | [ 19 | '01/01', 20 | '01/06', 21 | '02/02', 22 | '02/17', 23 | '02/28', 24 | '06/03', 25 | '12/06', 26 | '12/25', 27 | ], 28 | ], 29 | output: 10, 30 | description: 'should return 10 hours', 31 | }, 32 | { 33 | input: [2000, ['01/01']], 34 | output: 0, 35 | description: 'should return 0 hours', 36 | }, 37 | ]; 38 | 39 | it('should return a number type', () => { 40 | expect(typeof countHours(2022, [])).toBe('number'); 41 | }); 42 | 43 | it.each(testCases)('$description', (testCase) => { 44 | expect(countHours(...testCase.input)).toEqual(testCase.output); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /2022/03-cajas-de-regalo/README.md: -------------------------------------------------------------------------------- 1 | # Reto 3: ¿Cuántas cajas de regalos puede llevar Papá Noel? 2 | 3 | ## Problema 4 | 5 | Tienes una caja de regalos de Navidad que Santa Claus quiere entregar a los niños. Cada regalo está representado por una cadena. Santa Claus tiene un trineo que puede llevar un peso limitado, y cada regalo dentro de la caja tiene un peso que es igual al número de letras en el nombre del regalo. 6 | 7 | Santa Claus también tiene una lista de renos que pueden ayudarlo a entregar los regalos. Cada renos tiene un límite de peso máximo que puede llevar. El límite de peso máximo de cada reno es igual a dos veces el número de letras en su nombre. 8 | 9 | Tu tarea es implementar una función `distributeGifts(packOfGifts, reindeers)` que recibe una caja de regalos y una lista de renos y devuelve el número máximo de cajas de estos regalos que Santa Claus puede entregar a los niños. Las cajas de regalos no se pueden dividir. 10 | 11 | ```js 12 | const packOfGifts = ["book", "doll", "ball"]; 13 | const reindeers = ["dasher", "dancer"]; 14 | 15 | // el pack de regalos pesa 4 + 4 + 4 = 12 16 | // los renos pueden llevar (2 * 6) + (2 * 6) = 24 17 | // por lo tanto, Santa Claus puede entregar 2 cajas de regalos 18 | 19 | distributeGifts(packOfGifts, reindeers); // 2 20 | ``` 21 | 22 | Cosas a tener en cuenta: 23 | 24 | - Las cajas de regalos no se pueden dividir. 25 | - Los nombres de los regalos y los renos siempre serán mayores que 0. 26 | 27 | ## Mi Solución 28 | 29 | ```js 30 | const distributeGifts = (packOfGifts, reindeers) => { 31 | return Math.floor( 32 | (reindeers.join("").length * 2) / packOfGifts.join("").length 33 | ); 34 | }; 35 | 36 | const packOfGifts = ["book", "doll", "ball"]; 37 | const reindeers = ["dasher", "dancer"]; 38 | 39 | console.log(distributeGifts(packOfGifts, reindeers)); // -> 2 40 | console.log(distributeGifts(["a", "b", "c"], ["d", "e"])); // -> 1 41 | console.log(distributeGifts(["videogames", "console"], ["midu"])); // -> 0 42 | console.log( 43 | distributeGifts( 44 | ["game", "videoconsole", "computer"], 45 | [ 46 | "midudev", 47 | "pheralb", 48 | "codingwithdani", 49 | "carlosble", 50 | "blasco", 51 | "facundocapua", 52 | "madeval", 53 | "memxd", 54 | ] 55 | ) 56 | ); // -> 5 57 | console.log( 58 | distributeGifts( 59 | ["music"], 60 | [ 61 | "midudev", 62 | "pheralb", 63 | "codingwithdani", 64 | "carlosble", 65 | "blasco", 66 | "facundocapua", 67 | "madeval", 68 | "memxd", 69 | ] 70 | ) 71 | ); // -> 26 72 | ``` 73 | -------------------------------------------------------------------------------- /2022/03-cajas-de-regalo/index.js: -------------------------------------------------------------------------------- 1 | function distributeGifts(packOfGifts, reindeers) { 2 | return Math.floor( 3 | (reindeers.join('').length * 2) / packOfGifts.join('').length, 4 | ); 5 | } 6 | 7 | module.exports = distributeGifts; 8 | -------------------------------------------------------------------------------- /2022/03-cajas-de-regalo/index.test.js: -------------------------------------------------------------------------------- 1 | const distributeGifts = require('./index'); 2 | 3 | describe('03 => Cajas de regalo', () => { 4 | const testCases = [ 5 | { 6 | input: [['book', 'doll', 'ball'], ['dasher', 'dancer']], 7 | output: 2, 8 | description: 'should return 2', 9 | }, 10 | { 11 | input: [['a', 'b', 'c'], ['d', 'e']], 12 | output: 1, 13 | description: 'should return 1', 14 | }, 15 | { 16 | input: [['videogames', 'console'], ['midu']], 17 | output: 0, 18 | description: 'should return 0', 19 | }, 20 | { 21 | input: [ 22 | ['game', 'videoconsole', 'computer'], 23 | [ 24 | 'midudev', 25 | 'pheralb', 26 | 'codingwithdani', 27 | 'carlosble', 28 | 'blasco', 29 | 'facundocapua', 30 | 'madeval', 31 | 'memxd', 32 | ], 33 | ], 34 | output: 5, 35 | description: 'should return 5', 36 | }, 37 | { 38 | input: [ 39 | ['music'], 40 | [ 41 | 'midudev', 42 | 'pheralb', 43 | 'codingwithdani', 44 | 'carlosble', 45 | 'blasco', 46 | 'facundocapua', 47 | 'madeval', 48 | 'memxd', 49 | ], 50 | ], 51 | output: 26, 52 | description: 'should return 26', 53 | }, 54 | ]; 55 | 56 | it('should return a number type', () => { 57 | expect(typeof distributeGifts([], [])).toBe('number'); 58 | }); 59 | 60 | it.each(testCases)('$description', (testCase) => { 61 | expect(distributeGifts(...testCase.input)).toEqual(testCase.output); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /2022/04-caja-dentro-de-otra-caja/index.js: -------------------------------------------------------------------------------- 1 | function fitsInOneBox(boxes) { 2 | return boxes 3 | .sort((a, b) => a.l - b.l) 4 | .every((box, i) => { 5 | const beforeBox = boxes[i - 1]; 6 | return ( 7 | i === 0 8 | || (box.l > beforeBox.l && box.w > beforeBox.w && box.h > beforeBox.h) 9 | ); 10 | }); 11 | } 12 | 13 | module.exports = fitsInOneBox; 14 | -------------------------------------------------------------------------------- /2022/04-caja-dentro-de-otra-caja/index.test.js: -------------------------------------------------------------------------------- 1 | const fitsInOneBox = require('./index'); 2 | 3 | describe('04 => Caja dentro de otra caja', () => { 4 | const testCases = [ 5 | { 6 | input: [ 7 | { l: 2, w: 2, h: 2 }, 8 | { l: 1, w: 1, h: 1 }, 9 | ], 10 | output: true, 11 | description: 'should return true', 12 | }, 13 | { 14 | input: [ 15 | { l: 1, w: 1, h: 1 }, 16 | { l: 2, w: 2, h: 2 }, 17 | { l: 3, w: 1, h: 3 }, 18 | ], 19 | output: false, 20 | description: 'should return false', 21 | }, 22 | { 23 | input: [ 24 | { l: 1, w: 1, h: 1 }, 25 | { l: 3, w: 3, h: 3 }, 26 | { l: 2, w: 2, h: 2 }, 27 | ], 28 | output: true, 29 | description: 'should return true', 30 | }, 31 | { 32 | input: [ 33 | { l: 1, w: 1, h: 1 }, 34 | { l: 3, w: 3, h: 3 }, 35 | { l: 2, w: 2, h: 2 }, 36 | { l: 4, w: 4, h: 4 }, 37 | ], 38 | output: true, 39 | description: 'should return true', 40 | }, 41 | { 42 | input: [ 43 | { l: 1, w: 1, h: 1 }, 44 | { l: 2, w: 2, h: 2 }, 45 | { l: 2, w: 10, h: 2 }, 46 | ], 47 | output: false, 48 | description: 'should return false', 49 | }, 50 | ]; 51 | 52 | it('should return a boolean type', () => { 53 | expect(typeof fitsInOneBox([])).toBe('boolean'); 54 | }); 55 | 56 | it.each(testCases)('$description', (testCase) => { 57 | expect(fitsInOneBox(testCase.input)).toEqual(testCase.output); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /2022/05-optimizando-viajes/README.md: -------------------------------------------------------------------------------- 1 | # Reto 5: Optimizando viajes de Santa 2 | 3 | ## Problema 4 | 5 | Para no cansar a los renos, Papá Noel quiere dejar el máximo número de regalos haciendo el menor número posible de viajes. 6 | 7 | Tiene un array de ciudades donde cada elemento es el número de regalos que puede dejar allí. [12, 3, 11, 5, 7]. Por otro lado, el límite de regalos que caben en su saco. Y, finalmente, el número de ciudades máximo que sus renos pueden visitar. 8 | 9 | Como no quiere dejar una ciudad a medias, si no puede dejar todos los regalos que son de esa ciudad, no deja ninguno allí. 10 | 11 | Crea un programa que le diga la suma más alta de regalos que podría repartir teniendo en cuenta el máximo de regalos que puede transportar y el número máximo de ciudades que puede visitar: 12 | 13 | ```js 14 | const giftsCities = [12, 3, 11, 5, 7]; 15 | const maxGifts = 20; 16 | const maxCities = 3; 17 | 18 | // la suma más alta de regalos a repartir 19 | // visitando un máximo de 3 ciudades 20 | // es de 20: [12, 3, 5] 21 | 22 | // la suma más alta sería [12, 7, 11] 23 | // pero excede el límite de 20 regalos y tendría 24 | // que dejar alguna ciudad a medias. 25 | 26 | getMaxGifts(giftsCities, maxGifts, maxCities); // 20 27 | ``` 28 | 29 | Si no se puede realizar ningún viaje que satisface los requerimientos, el resultado debe ser 0. Más ejemplos: 30 | 31 | ```js 32 | getMaxGifts([12, 3, 11, 5, 7], 20, 3); // 20 33 | getMaxGifts([50], 15, 1); // 0 34 | getMaxGifts([50], 100, 1); // 50 35 | getMaxGifts([50, 70], 100, 1); // 70 36 | getMaxGifts([50, 70, 30], 100, 2); // 100 37 | getMaxGifts([50, 70, 30], 100, 3); // 100 38 | getMaxGifts([50, 70, 30], 100, 4); // 100 39 | ``` 40 | 41 | **A tener en cuenta:** 42 | 43 | - maxGifts >= 1 44 | - giftsCities.length >= 1 45 | - maxCities >= 1 46 | - El número de maxCities puede ser mayor a giftsCities.length 47 | 48 | ## Mi Solución 49 | 50 | ```js 51 | function getMaxGifts(giftsCities, maxGifts, maxCities) { 52 | return giftsCities 53 | .sort((a, b) => b - a) 54 | .reduce((acc, gift) => { 55 | if ( 56 | maxCities !== 0 && 57 | acc + gift <= maxGifts && 58 | acc + gift !== maxGifts - 1 59 | ) { 60 | acc += gift; 61 | maxCities--; 62 | } 63 | return acc; 64 | }, 0); 65 | } 66 | ``` 67 | -------------------------------------------------------------------------------- /2022/05-optimizando-viajes/index.js: -------------------------------------------------------------------------------- 1 | function getMaxGifts(giftsCities, maxGifts, maxCities) { 2 | return giftsCities 3 | .sort((a, b) => b - a) 4 | .reduce((acc, gift) => { 5 | if (maxCities !== 0 6 | && acc + gift <= maxGifts 7 | && acc + gift !== maxGifts - 1 8 | ) { 9 | acc += gift; 10 | maxCities--; 11 | } 12 | return acc; 13 | }, 0); 14 | } 15 | 16 | module.exports = getMaxGifts; 17 | -------------------------------------------------------------------------------- /2022/05-optimizando-viajes/index.test.js: -------------------------------------------------------------------------------- 1 | const getMaxGifts = require('./index'); 2 | 3 | describe('05 => Optimizando viajes de Santa', () => { 4 | const testCases = [ 5 | { 6 | input: [[12, 3, 11, 5, 7], 20, 3], 7 | output: 20, 8 | description: 'should return 20', 9 | }, 10 | { 11 | input: [[50], 15, 1], 12 | output: 0, 13 | description: 'should return 0', 14 | }, 15 | { 16 | input: [[50], 100, 1], 17 | output: 50, 18 | description: 'should return 50', 19 | }, 20 | { 21 | input: [[50, 70], 100, 1], 22 | output: 70, 23 | description: 'should return 70', 24 | }, 25 | { 26 | input: [[50, 70, 30], 100, 2], 27 | output: 100, 28 | description: 'should return 100', 29 | }, 30 | { 31 | input: [[50, 70, 30], 100, 3], 32 | output: 100, 33 | description: 'should return 100', 34 | }, 35 | { 36 | input: [[50, 10, 40, 1000, 500, 200], 199, 4], 37 | output: 100, 38 | description: 'should return 100', 39 | }, 40 | { 41 | input: [[50, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 1000, 1000], 42 | output: 115, 43 | description: 'should return 115', 44 | }, 45 | ]; 46 | 47 | it('should return a number type', () => { 48 | expect(typeof getMaxGifts([50], 100, 1)).toBe('number'); 49 | }); 50 | 51 | it.each(testCases)('$description', (testCase) => { 52 | expect(getMaxGifts(...testCase.input)).toBe(testCase.output); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /2022/06-adornos-navideños/index.js: -------------------------------------------------------------------------------- 1 | function createCube(size) { 2 | const topSide = []; 3 | const bottomSide = []; 4 | for (let i = 1; i <= size; i++) { 5 | topSide.push(' '.repeat(size - i) + '/\\'.repeat(i) + '_\\'.repeat(size)); 6 | bottomSide.push(' '.repeat(size - i) + '\\/'.repeat(i) + '_/'.repeat(size)); 7 | } 8 | return topSide.concat(bottomSide.reverse()).join('\n'); 9 | } 10 | 11 | module.exports = createCube; 12 | -------------------------------------------------------------------------------- /2022/06-adornos-navideños/index.test.js: -------------------------------------------------------------------------------- 1 | const createCube = require('./index'); 2 | 3 | describe('06 => Adornos navideños', () => { 4 | const testCases = [ 5 | { 6 | input: 10, 7 | output: ' /\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\\n /\\/\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\\n ' 8 | + '/\\/\\/\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\\n /\\/\\/\\/\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\\n ' 9 | + '/\\/\\/\\/\\/\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\\n /\\/\\/\\/\\/\\/\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\\n ' 10 | + '/\\/\\/\\/\\/\\/\\/\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\\n /\\/\\/\\/\\/\\/\\/\\/\\_\\_\\_\\_\\_\\_\\_\\_\\_' 11 | + '\\_\\\n /\\/\\/\\/\\/\\/\\/\\/\\/\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\\n/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\_\\_\\_' 12 | + '\\_\\_\\_\\_\\_\\_\\_\\\n\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/_/_/_/_/_/_/_/_/_/_/\n \\/\\/\\/\\/\\/\\/\\/\\/\\' 13 | + '/_/_/_/_/_/_/_/_/_/_/\n \\/\\/\\/\\/\\/\\/\\/\\/_/_/_/_/_/_/_/_/_/_/\n ' 14 | + '\\/\\/\\/\\/\\/\\/\\/_/_/_/_/_/_/_/_/_/_/\n \\/\\/\\/\\/\\/\\/_/_/_/_/_/_/_/_/_/_/\n ' 15 | + '\\/\\/\\/\\/\\/_/_/_/_/_/_/_/_/_/_/\n \\/\\/\\/\\/_/_/_/_/_/_/_/_/_/_/\n ' 16 | + '\\/\\/\\/_/_/_/_/_/_/_/_/_/_/\n \\/\\/_/_/_/_/_/_/_/_/_/_/\n \\/_/_/_/_/_/_/_/_/_/_/', 17 | description: 'should return a cube using 10 as input', 18 | }, 19 | { 20 | input: 1, 21 | output: '/\\_\\\n\\/_/', 22 | description: 'should return a cube using 1 as input', 23 | }, 24 | { 25 | input: 2, 26 | output: ' /\\_\\_\\\n/\\/\\_\\_\\\n\\/\\/_/_/\n \\/_/_/', 27 | description: 'should return a cube using 2 as input', 28 | }, 29 | { 30 | input: 3, 31 | output: ' /\\_\\_\\_\\\n /\\/\\_\\_\\_\\\n/\\/\\/\\_\\_\\_\\\n\\/\\/\\/_/_/_/\n \\/\\/_/_/_/\n \\/_/_/_/', 32 | description: 'should return a cube using 3 as input', 33 | }, 34 | ]; 35 | 36 | it('should return an array type', () => { 37 | expect(typeof createCube(10)).toBe('string'); 38 | }); 39 | 40 | it.each(testCases)('$description', (testCase) => { 41 | expect(createCube(testCase.input)).toEqual(testCase.output); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /2022/07-inventarios-de-regalos/README.md: -------------------------------------------------------------------------------- 1 | # Reto 7: Haciendo inventario de regalos 2 | 3 | ## Problema 4 | 5 | En los almacenes de Papá Noel están haciendo inventario. Hay tres almacenes (que se representa cada uno como un Array). En cada almacén hay regalos. 6 | 7 | Nos han pedido que escribamos un programa que nos diga qué regalos hay que comprar para reponer en nuestros almacénes ahora que se acerca la Navidad.. Un regalo se tiene que reponer cuando sólo hay stock en uno de los tres almacénes. 8 | 9 | Por ejemplo, si tenemos los siguientes almacenes: 10 | 11 | ```js 12 | const a1 = ["bici", "coche", "bici", "bici"]; 13 | const a2 = ["coche", "bici", "muñeca", "coche"]; 14 | const a3 = ["bici", "pc", "pc"]; 15 | 16 | /* El almacén a1 tiene "bici" y "coche". 17 | El almacén a2 tiene "coche", "bici" y "muñeca". 18 | El almacén a3 tiene "bici" y "pc". 19 | 20 | El regalo "muñeca" y "pc" sólo están en los almacenes a2 y a3 respectivamente. 21 | */ 22 | 23 | const gifts = getGiftsToRefill(a1, a2, a3); // ['muñeca', 'pc'] 24 | ``` 25 | 26 | Como ves, los almacénes pueden tener el mismo regalo repetido varias veces. Pero, por más existencias que haya en un almacén, si no tenemos en los otros dos, debemos reponerlo para tener mejor distribución. 27 | 28 | **📝 Summary** 29 | 30 | 1. Crea una función getGiftsToRefill que reciba tres Array como parámetros. 31 | 2. La función debe devolver un Array con los regalos que hay que reponer. 32 | 3. Un regalo se debe reponer cuando sólo hay stock en uno de los tres almacénes. 33 | 4. Si no hay ningún regalo que reponer, la función debe devolver un Array vacío. 34 | 5. Si hay más de un regalo que reponer, la función debe devolver un Array con todos los regalos que hay que reponer. 35 | 36 | ## Mi Solución 37 | 38 | ```js 39 | function getGiftsToRefill(a1, a2, a3) { 40 | return [...new Set(a1.concat(a2, a3))].filter( 41 | (item) => a1.includes(item) + a2.includes(item) + a3.includes(item) < 2 42 | ); 43 | } 44 | 45 | const a1 = ["bici", "coche", "bici", "bici"]; 46 | const a2 = ["coche", "bici", "muñeca", "coche"]; 47 | const a3 = ["bici", "pc", "pc"]; 48 | console.log(getGiftsToRefill(a1, a2, a3)); // -> ['muñeca', 'pc'] 49 | console.log(getGiftsToRefill(["a", "a"], ["b", "b"], ["c", "c"])); // -> ['a', 'b', 'c'] 50 | console.log(getGiftsToRefill(["a", "a"], ["a", "a"], ["a", "a"])); // -> [] 51 | ``` 52 | -------------------------------------------------------------------------------- /2022/07-inventarios-de-regalos/index.js: -------------------------------------------------------------------------------- 1 | function getGiftsToRefill(a1, a2, a3) { 2 | return [...new Set(a1.concat(a2, a3))].filter( 3 | (item) => a1.includes(item) + a2.includes(item) + a3.includes(item) < 2, 4 | ); 5 | } 6 | 7 | module.exports = getGiftsToRefill; 8 | -------------------------------------------------------------------------------- /2022/07-inventarios-de-regalos/index.test.js: -------------------------------------------------------------------------------- 1 | const getGiftsToRefill = require('./index'); 2 | 3 | describe('07 => Inventarios de regalos', () => { 4 | const testCases = [ 5 | { 6 | input: [ 7 | ['bici', 'coche', 'bici', 'bici'], 8 | ['coche', 'bici', 'muñeca', 'coche'], 9 | ['bici', 'pc', 'pc'], 10 | ], 11 | output: ['muñeca', 'pc'], 12 | description: 'should return muñeca and pc', 13 | }, 14 | { 15 | input: [[], [], []], 16 | output: [], 17 | description: 'should return an empty array', 18 | }, 19 | { 20 | input: [['a', 'a'], ['a', 'a'], ['a', 'a']], 21 | output: [], 22 | description: 'should return an empty array', 23 | }, 24 | { 25 | input: [['a', 'a'], ['b', 'b'], ['c', 'c']], 26 | output: ['a', 'b', 'c'], 27 | description: 'should return a, b and c', 28 | }, 29 | { 30 | input: [['a'], ['a'], ['a']], 31 | output: [], 32 | description: 'should return an empty array', 33 | }, 34 | ]; 35 | 36 | it('should return an array type', () => { 37 | expect(getGiftsToRefill([], [], [])).toBeInstanceOf(Array); 38 | }); 39 | 40 | it.each(testCases)('$description', (testCase) => { 41 | expect(getGiftsToRefill(...testCase.input)).toEqual(testCase.output); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /2022/08-mecanico/README.md: -------------------------------------------------------------------------------- 1 | # Reto 8: ¡Necesitamos un mecánico! 2 | 3 | ## Problema 4 | 5 | Se han estropeado algunos trineos eléctricos y los elfos están buscando piezas de respuesto para arreglarlos, pero no tienen claro si las piezas que tienen sirven. 6 | 7 | Las piezas de repuesto son cadenas de texto y el mecánico Elfon Masc ha dicho que una pieza de repuesto es válida **si la pieza puede ser un palíndromo después de eliminar, como máximo, un carácter.** 8 | 9 | _Un palíndromo es una palabra o frase que se lee igual de izquierda a derecha que de derecha a izquierda._ 10 | 11 | Nuestra función debe devolver un booleano que indique si la pieza de repuesto es válida o no con esa regla: 12 | 13 | ```js 14 | checkPart("uwu"); // true 15 | // "uwu" es un palíndromo sin eliminar ningún carácter 16 | 17 | checkPart("miidim"); // true 18 | // "miidim" puede ser un palíndromo después de eliminar la primera "i" 19 | // ya que "midim" es un palíndromo 20 | 21 | checkPart("midu"); // false 22 | // "midu" no puede ser un palíndromo después de eliminar un carácter 23 | ``` 24 | 25 | ## Mi Solución 26 | 27 | ```js 28 | function checkPart(part) { 29 | return [...part.slice(0, part.length / 2)].every( 30 | (_, i) => 31 | part[i] === part[part.length - 1 - i] || 32 | part[i] === part[part.length - 2 - i] || 33 | part[i + 1] === part[part.length - 1 - i] 34 | ); 35 | } 36 | 37 | console.log(checkPart("zzzoonzzz")); // -> true 38 | console.log(checkPart("uwu")); // -> true 39 | console.log(checkPart("midu")); // -> false 40 | console.log(checkPart("lolol")); // -> true 41 | console.log(checkPart("yolooloy")); // -> true 42 | ``` 43 | -------------------------------------------------------------------------------- /2022/08-mecanico/index.js: -------------------------------------------------------------------------------- 1 | function checkPart(part) { 2 | return [...part.slice(0, part.length / 2)].every( 3 | (_, i) => part[i] === part[part.length - 1 - i] 4 | || part[i] === part[part.length - 2 - i] 5 | || part[i + 1] === part[part.length - 1 - i], 6 | ); 7 | } 8 | 9 | module.exports = checkPart; 10 | -------------------------------------------------------------------------------- /2022/08-mecanico/index.test.js: -------------------------------------------------------------------------------- 1 | const checkPart = require('./index'); 2 | 3 | describe('08 => Mecánico', () => { 4 | const testCases = [ 5 | { 6 | input: ['zzzoonzzz'], 7 | output: true, 8 | description: 'should return true', 9 | }, 10 | { 11 | input: ['uwu'], 12 | output: true, 13 | description: 'should return true', 14 | }, 15 | { 16 | input: ['midu'], 17 | output: false, 18 | description: 'should return false', 19 | }, 20 | { 21 | input: ['lolol'], 22 | output: true, 23 | description: 'should return true', 24 | }, 25 | { 26 | input: ['yolooloy'], 27 | output: true, 28 | description: 'should return true', 29 | }, 30 | ]; 31 | 32 | it('should return a boolean type', () => { 33 | expect(typeof checkPart('zzzoonzzz')).toBe('boolean'); 34 | }); 35 | 36 | it.each(testCases)('$description', (testCase) => { 37 | expect(checkPart(...testCase.input)).toEqual(testCase.output); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /2022/09-locas-luces/README.md: -------------------------------------------------------------------------------- 1 | # Reto 9: Las locas luces de navidad 2 | 3 | ## Problema 4 | 5 | Una empresa que fabrica luces de Navidad nos ha pedido que le echemos una mano. 6 | 7 | Tienen unas tiras led que son como un Array. Cada posición es un led y puede ser estar encendido (1) o apagado (0). 8 | 9 | Cada 7 segundos, los leds cambian de estado de esta forma: 10 | 11 | - Si el led está apagado, se enciende si el led de su izquierda (index - 1) estaba encendido antes. 12 | - Si el led está encendido, se mantiene siempre encendido. 13 | 14 | Nos han pedido un programa que nos diga cuantos segundos deben pasar hasta que todos los leds están encendidos. Los segundos se expresan en un número entero. Por ejemplo: 15 | 16 | ```js 17 | const leds = [0, 1, 1, 0, 1]; 18 | countTime(leds); // 7 19 | // 7 segundos ya que en el primer cambio 20 | // todas las luces se encendieron 21 | // 0s: [0, 1, 1, 0, 1] 22 | // 7s: [1, 1, 1, 1, 1] 23 | 24 | countTime([0, 0, 0, 1]); // 21 25 | // 21 segundos ya que necesita tres cambios: 26 | // 0s: [0, 0, 0, 1] 27 | // 7s: [1, 0, 0, 1] 28 | // 14s: [1, 1, 0, 1] 29 | // 21s: [1, 1, 1, 1] 30 | 31 | countTime([0, 0, 1, 0, 0]); // 28 32 | // 28 segundos ya que necesita cuatro cambios: 33 | // 0s: [0, 0, 1, 0, 0] 34 | // 7s: [0, 0, 1, 1, 0] 35 | // 14s: [0, 0, 1, 1, 1] 36 | // 21s: [1, 0, 1, 1, 1] 37 | // 28s: [1, 1, 1, 1, 1] 38 | ``` 39 | 40 | A tener en cuenta 41 | 42 | - El array siempre tendrá al menos un led encendido. 43 | - El array puede tener cualquier longitud. 44 | - Si todos los leds están encendidos, el tiempo es 0. 45 | 46 | # Mi Solución 47 | 48 | ```js 49 | function countTime(leds) { 50 | leds = leds.join("").split("1"); 51 | leds[0] += leds.splice(-1); 52 | return leds.sort((a, b) => b.length - a.length)[0].length * 7; 53 | } 54 | 55 | const leds = [0, 1, 1, 0, 1]; 56 | console.log(countTime(leds)); // -> 7 57 | // 7 segundos ya que en el primer cambio 58 | // todas las luces se encendieron 59 | // 0s: [0, 1, 1, 0, 1] 60 | // 7s: [1, 1, 1, 1, 1] 61 | 62 | console.log(countTime([0, 0, 0, 1])); // -> 21 63 | // 21 segundos ya que necesita tres cambios: 64 | // 0s: [0, 0, 0, 1] 65 | // 7s: [1, 0, 0, 1] 66 | // 14s: [1, 1, 0, 1] 67 | // 21s: [1, 1, 1, 1] 68 | 69 | console.log(countTime([0, 0, 1, 0, 0])); // -> 28 70 | // 28 segundos ya que necesita cuatro cambios: 71 | // 0s: [0, 0, 1, 0, 0] 72 | // 7s: [0, 0, 1, 1, 0] 73 | // 14s: [0, 0, 1, 1, 1] 74 | // 21s: [1, 0, 1, 1, 1] 75 | // 28s: [1, 1, 1, 1, 1] 76 | ``` 77 | -------------------------------------------------------------------------------- /2022/09-locas-luces/index.js: -------------------------------------------------------------------------------- 1 | function countTime(leds) { 2 | leds = leds.join('').split('1'); 3 | leds[0] += leds.splice(-1); 4 | return leds.sort((a, b) => b.length - a.length)[0].length * 7; 5 | } 6 | 7 | module.exports = countTime; 8 | -------------------------------------------------------------------------------- /2022/09-locas-luces/index.test.js: -------------------------------------------------------------------------------- 1 | const countTime = require('./index'); 2 | 3 | describe('09 => Locas luces', () => { 4 | const testCases = [ 5 | { 6 | input: [[0, 1, 1, 0, 1]], 7 | output: 7, 8 | description: 'should return 7', 9 | }, 10 | { 11 | input: [[0, 0, 0, 1]], 12 | output: 21, 13 | description: 'should return 21', 14 | }, 15 | { 16 | input: [[0, 0, 1, 0, 0]], 17 | output: 28, 18 | description: 'should return 28', 19 | }, 20 | { 21 | input: [[1, 0, 0, 1, 0, 0]], 22 | output: 14, 23 | description: 'should return 14', 24 | }, 25 | { 26 | input: [[1, 1, 0, 0, 0, 0]], 27 | output: 28, 28 | description: 'should return 28', 29 | }, 30 | { 31 | input: [[1, 1, 1]], 32 | output: 0, 33 | description: 'should return 0', 34 | }, 35 | ]; 36 | 37 | it('should return a number type', () => { 38 | expect(typeof countTime([])).toBe('number'); 39 | }); 40 | 41 | it.each(testCases)('$description', (testCase) => { 42 | expect(countTime(...testCase.input)).toEqual(testCase.output); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /2022/10-salto-del-trineo/README.md: -------------------------------------------------------------------------------- 1 | # Reto 10: El salto del trineo de Papá Noel 2 | 3 | ## Problema 4 | 5 | Crea un programa que compruebe que el trineo de Santa Claus hace una parabola al saltar entre ciudades. Recibes un array de números que representan la altura en la que se encuentra el trineo en cada momento. 6 | 7 | Para que la parabola sea correcta, el viaje del trineo debe ser ascendente al principio, llegar al punto más alto y descender hasta el final. No puede volver a subir una vez que ha bajado, ni puede iniciar el viaje bajando. Veamos unos ejemplos: 8 | 9 | ```js 10 | const heights = [1, 3, 8, 5, 2] 11 | checkJump(heights) // true 12 | 13 | /* 14 | Es `true`. 15 | El salto va de abajo a arriba y luego de arriba a abajo: 16 | 17 | 8 (punto más alto) 18 | ↗ ↘ 19 | 3 5 20 | ↗ ↘ 21 | 1 2 22 | */ 23 | 24 | const heights = [1, 7, 3, 5] 25 | checkJump(heights) // false 26 | 27 | /* 28 | Es `false`. 29 | Va de abajo a arriba, de arriba a abajo y luego sube otra vez. 30 | 31 | 7 5 32 | ↗ ↘ ↗ 33 | 1 3 34 | ``` 35 | 36 | Necesitamos que el programa devuelva un boolean que indique si el trineo hace una parabola o no. 37 | 38 | **A tener en cuenta** 39 | 40 | - Para que el salto sea válido tiene que subir una vez y bajar una vez. Si durante el salto se queda en la misma altura entre dos posiciones, la parabola continua. 41 | - No hace falta que el punto de inicio y final sean el mismo (las ciudades pueden estar a diferentes alturas). 42 | 43 | ## Mi Solución 44 | 45 | ```js 46 | function checkJump(heights) { 47 | const dictionary = { 0: "", "-1": "d", 1: "u" }; // -1: down, 0: same, 1: up 48 | const str = heights 49 | .slice(1) 50 | .reduce( 51 | (acc, curr, i) => acc + dictionary[Math.sign(curr - heights[i])], 52 | "" 53 | ); 54 | return !!str.match(/^[u]+[d]+$/g); 55 | } 56 | 57 | console.log(checkJump([1, 3, 8, 5, 2])); // -> true 58 | console.log(checkJump([1, 7, 3, 5])); // -> false 59 | console.log(checkJump([1, 2, 1])); // -> true 60 | console.log(checkJump([1, 1, 1, 1, 1, 1, 1, 1, 2, 1])); // -> true 61 | console.log(checkJump([1, 2, 3, 1, 3, 1])); // -> false 62 | console.log(checkJump([1, 3, 2, 5, 4, 3, 2, 1])); // -> true 63 | ``` 64 | -------------------------------------------------------------------------------- /2022/10-salto-del-trineo/index.js: -------------------------------------------------------------------------------- 1 | function checkJump(heights) { 2 | const dictionary = { 0: '', '-1': 'd', 1: 'u' }; // -1: down, 0: same, 1: up 3 | const str = heights.slice(1).reduce((acc, curr, i) => acc + dictionary[Math.sign(curr - heights[i])], ''); 4 | return !!str.match(/^[u]+[d]+$/g); 5 | } 6 | 7 | module.exports = checkJump; 8 | -------------------------------------------------------------------------------- /2022/10-salto-del-trineo/index.test.js: -------------------------------------------------------------------------------- 1 | const checkJump = require('./index'); 2 | 3 | describe('10 => Salto del trineo', () => { 4 | const testCases = [ 5 | { 6 | input: [[1, 2, 1]], 7 | output: true, 8 | description: 'should return true', 9 | }, 10 | { 11 | input: [[1, 3, 8, 5, 2]], 12 | output: true, 13 | description: 'should return true', 14 | }, 15 | { 16 | input: [[1, 7, 3, 5]], 17 | output: false, 18 | description: 'should return false', 19 | }, 20 | { 21 | input: [[1, 2, 3, 2, 1]], 22 | output: true, 23 | description: 'should return true', 24 | }, 25 | { 26 | input: [[1, 2, 2, 2, 1]], 27 | output: true, 28 | description: 'should return true', 29 | }, 30 | { 31 | input: [[0, 1, 0]], 32 | output: true, 33 | description: 'should return true', 34 | }, 35 | { 36 | input: [[2, 2, 2, 2]], 37 | output: false, 38 | description: 'should return false', 39 | }, 40 | { 41 | input: [[1, 2, 3]], 42 | output: false, 43 | description: 'should return false', 44 | }, 45 | { 46 | input: [[1, 2, 3, 2, 1, 2, 3]], 47 | output: false, 48 | description: 'should return false', 49 | }, 50 | { 51 | input: [[1, 1000, 900, 800]], 52 | output: true, 53 | description: 'should return true', 54 | }, 55 | { 56 | input: [[1, 1000, 100, 800]], 57 | output: false, 58 | description: 'should return false', 59 | }, 60 | { 61 | input: [[1, 1, 1, 1, 1, 1, 1, 1, 2, 1]], 62 | output: true, 63 | description: 'should return true', 64 | }, 65 | { 66 | input: [[1, 2, 3, 1, 3, 1]], 67 | output: false, 68 | description: 'should return false', 69 | }, 70 | { 71 | input: [[1, 3, 2, 5, 4, 3, 2, 1]], 72 | output: false, 73 | description: 'should return false', 74 | }, 75 | ]; 76 | 77 | it('should return a boolean type', () => { 78 | expect(typeof checkJump([])).toBe('boolean'); 79 | }); 80 | 81 | it.each(testCases)('$description', (testCase) => { 82 | expect(checkJump(...testCase.input)).toEqual(testCase.output); 83 | }); 84 | }); 85 | -------------------------------------------------------------------------------- /2022/11-es-scrum-master/index.js: -------------------------------------------------------------------------------- 1 | function getCompleted(part, total) { 2 | const MCD = (a, b) => { 3 | let temp; 4 | while (b !== 0) { 5 | temp = b; 6 | b = a % b; 7 | a = temp; 8 | } 9 | return a; 10 | }; 11 | const [numerator, denominator] = [part, total] 12 | .map((time) => time.split(':') 13 | .reduce((acc, curr) => acc * 60 + +curr, 0)); 14 | const mcdResult = MCD(numerator, denominator); 15 | return `${numerator / mcdResult}/${denominator / mcdResult}`; 16 | } 17 | 18 | module.exports = getCompleted; 19 | -------------------------------------------------------------------------------- /2022/11-es-scrum-master/index.test.js: -------------------------------------------------------------------------------- 1 | const getCompleted = require('./index'); 2 | 3 | describe('11 => Es Scrum Master', () => { 4 | const testCases = [ 5 | { 6 | input: ['01:00:00', '03:00:00'], 7 | output: '1/3', 8 | description: 'should return 1/3', 9 | }, 10 | { 11 | input: ['02:00:00', '04:00:00'], 12 | output: '1/2', 13 | description: 'should return 1/2', 14 | }, 15 | { 16 | input: ['01:00:00', '01:00:00'], 17 | output: '1/1', 18 | description: 'should return 1/1', 19 | }, 20 | { 21 | input: ['00:10:00', '01:00:00'], 22 | output: '1/6', 23 | description: 'should return 1/6', 24 | }, 25 | { 26 | input: ['01:10:10', '03:30:30'], 27 | output: '1/3', 28 | description: 'should return 1/3', 29 | }, 30 | { 31 | input: ['03:30:30', '05:50:50'], 32 | output: '3/5', 33 | description: 'should return 3/5', 34 | }, 35 | ]; 36 | 37 | it('should return a string type', () => { 38 | expect(typeof getCompleted('01:00:00', '03:00:00')).toBe('string'); 39 | }); 40 | 41 | it.each(testCases)('$description', (testCase) => { 42 | expect(getCompleted(...testCase.input)).toEqual(testCase.output); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /2022/12-trineos-electricos/index.js: -------------------------------------------------------------------------------- 1 | function selectSleigh(distance, sleighs) { 2 | const selected = sleighs 3 | .filter((sleigh) => sleigh.consumption * distance <= 20); 4 | return selected.length > 0 ? selected[selected.length - 1].name : null; 5 | } 6 | 7 | module.exports = selectSleigh; 8 | -------------------------------------------------------------------------------- /2022/12-trineos-electricos/index.test.js: -------------------------------------------------------------------------------- 1 | const selectSleigh = require('./index'); 2 | 3 | describe('12 => Trineos eléctricos', () => { 4 | const testCases = [ 5 | { 6 | input: [30, [ 7 | { name: 'Dasher', consumption: 0.3 }, 8 | { name: 'Dancer', consumption: 0.5 }, 9 | { name: 'Rudolph', consumption: 0.7 }, 10 | { name: 'Midu', consumption: 1 }, 11 | ]], 12 | output: 'Dancer', 13 | description: 'should return Dancer sleigh', 14 | }, 15 | { 16 | input: [1, [ 17 | { name: 'pheralb', consumption: 0.3 }, 18 | { name: 'TMChein', consumption: 0.5 }, 19 | ]], 20 | output: 'TMChein', 21 | description: 'should return TMChein sleigh', 22 | }, 23 | { 24 | input: [10, [ 25 | { name: 'Pedrosillano', consumption: 1 }, 26 | { name: 'SamarJaffal', consumption: 5 }, 27 | ]], 28 | output: 'Pedrosillano', 29 | description: 'should return Pedrosillano sleigh', 30 | }, 31 | { 32 | input: [10, [ 33 | { name: 'Pedrosillano', consumption: 1 }, 34 | { name: 'SamarJaffal', consumption: 2 }, 35 | { name: 'marcospage', consumption: 3 }, 36 | ]], 37 | output: 'SamarJaffal', 38 | description: 'should return SamarJaffal sleigh', 39 | }, 40 | { 41 | input: [50, [ 42 | { name: 'Pedrosillano', consumption: 1 }, 43 | { name: 'SamarJaffal', consumption: 2 }, 44 | { name: 'marcospage', consumption: 3 }, 45 | ]], 46 | output: null, 47 | description: 'should return null', 48 | }, 49 | ]; 50 | 51 | it('should return a string type', () => { 52 | expect(typeof selectSleigh(...testCases[0].input)).toBe('string'); 53 | }); 54 | 55 | it.each(testCases)('$description', (testCase) => { 56 | expect(selectSleigh(...testCase.input)).toBe(testCase.output); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /2022/13-backup-de-archivos/index.js: -------------------------------------------------------------------------------- 1 | function getFilesToBackup(lastBackup, changes) { 2 | return [...new Set(changes 3 | .filter((backup) => backup.at(1) > lastBackup) 4 | .map((backup) => backup.at(0)) 5 | .sort((a, b) => a - b)), 6 | ]; 7 | } 8 | 9 | module.exports = getFilesToBackup; 10 | -------------------------------------------------------------------------------- /2022/13-backup-de-archivos/index.test.js: -------------------------------------------------------------------------------- 1 | const getFilesToBackup = require('./index'); 2 | 3 | describe('13 => Backup de archivos', () => { 4 | const testCases = [ 5 | { 6 | input: [1546300800, [ 7 | [2, 1546300800], 8 | [3, 1546301100], 9 | [1, 1546300800], 10 | [1, 1546300900], 11 | [1, 1546301000], 12 | ]], 13 | output: [1, 3], 14 | description: 'should return [1, 3]', 15 | }, 16 | { 17 | input: [1546300800, [ 18 | [2, 1546300800], 19 | [3, 1546301100], 20 | [1, 1546300800], 21 | [1, 1546300900], 22 | [1, 1546301000], 23 | ]], 24 | output: [1, 3], 25 | description: 'should return [1, 3]', 26 | }, 27 | { 28 | input: [1556300600, [ 29 | [1, 1546300800], 30 | [2, 1546300800], 31 | [1, 1546300900], 32 | [1, 1546301000], 33 | [3, 1546301100], 34 | ]], 35 | output: [], 36 | description: 'should return []', 37 | }, 38 | ]; 39 | 40 | it('should return an array type', () => { 41 | expect(getFilesToBackup(...testCases[0].input)).toBeInstanceOf(Array); 42 | }); 43 | 44 | it.each(testCases)('$description', (testCase) => { 45 | expect(getFilesToBackup(...testCase.input)).toEqual(testCase.output); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /2022/14-mejor-camino/index.js: -------------------------------------------------------------------------------- 1 | function getOptimalPath(path) { 2 | return path 3 | .reduceRight((acc, curr) => curr 4 | .map((value, index) => value + Math.min(acc[index], acc[index + 1])))[0]; 5 | } 6 | 7 | module.exports = getOptimalPath; 8 | -------------------------------------------------------------------------------- /2022/14-mejor-camino/index.test.js: -------------------------------------------------------------------------------- 1 | const getOptimalPath = require('./index'); 2 | 3 | describe('14 => Mejor camino', () => { 4 | const testCases = [ 5 | { 6 | input: [[0], [2, 3]], 7 | output: 2, 8 | description: 'should return 2', 9 | }, 10 | { 11 | input: [[0], [3, 4], [9, 8, 1]], 12 | output: 5, 13 | description: 'should return 5', 14 | }, 15 | { 16 | input: [[1], [1, 5], [7, 5, 8], [9, 4, 1, 3]], 17 | output: 8, 18 | description: 'should return 8', 19 | }, 20 | { 21 | input: [[1], [1, 5], [7, 5, 8], [9, 4, 1, 3], [-1, -1, -1, -1, -1]], 22 | output: 7, 23 | description: 'should return 7', 24 | }, 25 | { 26 | input: [[1], [1, 5], [7, 5, 8], [9, 4, 1, 3], [-1, -1, -1, -1, -1], [1, 1, 1, 1, 1, 1]], 27 | output: 8, 28 | description: 'should return 8', 29 | }, 30 | ]; 31 | 32 | it('should return a number type', () => { 33 | expect(typeof getOptimalPath(testCases[0].input)).toBe('number'); 34 | }); 35 | 36 | it.each(testCases)('$description', (testCase) => { 37 | expect(getOptimalPath(testCase.input)).toEqual(testCase.output); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /2022/15-decorando-el-arbol/index.js: -------------------------------------------------------------------------------- 1 | function decorateTree(base) { 2 | const decorators = { 3 | BB: 'B', 4 | RR: 'R', 5 | PP: 'P', 6 | PR: 'B', 7 | RP: 'B', 8 | RB: 'P', 9 | BR: 'P', 10 | PB: 'R', 11 | BP: 'R', 12 | }; 13 | const splitedBase = base.split(' '); 14 | const arr = []; 15 | splitedBase.slice(0, -1).reduce((acc) => { 16 | acc = acc.slice(0, -1).map((_, i) => decorators[acc[i] + acc[i + 1]]); 17 | arr.unshift(acc.join(' ')); 18 | return acc; 19 | }, splitedBase); 20 | return [...arr, base]; 21 | } 22 | 23 | module.exports = decorateTree; 24 | -------------------------------------------------------------------------------- /2022/15-decorando-el-arbol/index.test.js: -------------------------------------------------------------------------------- 1 | const decorateTree = require('./index'); 2 | 3 | describe('15 => Decorando el árbol', () => { 4 | const testCases = [ 5 | { 6 | input: ['B P R P'], 7 | output: ['R', 'P B', 'R B B', 'B P R P'], 8 | description: 'should return decorated tree with 4 elements', 9 | }, 10 | { 11 | input: ['B B'], 12 | output: ['B', 'B B'], 13 | description: 'should return decorated tree with 2 elements', 14 | }, 15 | { 16 | input: ['B B P R P R R'], 17 | output: ['B', 'R P', 'B P P', 'P R B R', 'P P B B P', 'B R B B B R', 'B B P R P R R'], 18 | description: 'should return decorated tree with 7 elements', 19 | }, 20 | { 21 | input: ['R R P R R P P P'], 22 | output: ['P', 'P P', 'P P P', 'R B R B', 'R R P B B', 'P B P P R P', 'R B B R B P P', 'R R P R R P P P'], 23 | description: 'should return decorated tree with 8 elements', 24 | }, 25 | ]; 26 | 27 | it('should return an array type', () => { 28 | expect(decorateTree('B P R P')).toBeInstanceOf(Array); 29 | }); 30 | 31 | it.each(testCases)('$description', (testCase) => { 32 | expect(decorateTree(...testCase.input)).toEqual(testCase.output); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /2022/16-arreglando-las-cartas/index.js: -------------------------------------------------------------------------------- 1 | function fixLetter(letter) { 2 | return letter 3 | .trim() 4 | .replace(/([.,?!\s])(?=\1)/g, '') 5 | .replace(/\s+([.,?!])/g, '$1') 6 | .replace(/\b([.?!] \w)|(^\w)/g, (match) => match.toUpperCase()) 7 | .replace(/^.*\w$/, (match) => `${match}.`) 8 | .replace(/santa claus/ig, 'Santa Claus'); 9 | } 10 | 11 | module.exports = fixLetter; 12 | -------------------------------------------------------------------------------- /2022/16-arreglando-las-cartas/index.test.js: -------------------------------------------------------------------------------- 1 | const fixLetter = require('./index'); 2 | 3 | describe('16 => Arreglando las cartas', () => { 4 | const testCases = [ 5 | { 6 | input: [' hello, how are you?? do you know if santa claus exists? i really hope he does! bye '], 7 | output: 'Hello, how are you? Do you know if Santa Claus exists? I really hope he does! Bye.', 8 | }, 9 | { 10 | input: [" Hi Santa claus. I'm a girl from Barcelona , Spain . please, send me a bike. Is it possible?"], 11 | output: 'Hi Santa Claus. I\'m a girl from Barcelona, Spain. Please, send me a bike. Is it possible?', 12 | }, 13 | { 14 | input: [' hi santa claus ??'], 15 | output: 'Hi Santa Claus?', 16 | }, 17 | { 18 | input: ['Hey santa Claus . I want a bike. I want a videogame! '], 19 | output: 'Hey Santa Claus. I want a bike. I want a videogame!', 20 | }, 21 | { 22 | input: [' hi santa claus . santa claus is the best '], 23 | output: 'Hi Santa Claus. Santa Claus is the best.', 24 | }, 25 | ]; 26 | 27 | it('should return a string type', () => { 28 | expect(typeof fixLetter('')).toBe('string'); 29 | }); 30 | 31 | it.each(testCases)('$description', (testCase) => { 32 | expect(fixLetter(...testCase.input)).toEqual(testCase.output); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /2022/17-regalos-en-sacos/index.js: -------------------------------------------------------------------------------- 1 | function carryGifts(gifts, maxWeight) { 2 | const bags = []; const bagTemp = []; 3 | let giftJoined = ''; 4 | gifts 5 | .filter((gift) => gift.length <= maxWeight) 6 | .forEach((gift) => { 7 | giftJoined += gift; 8 | if (giftJoined.length > maxWeight) { 9 | bags.push(bagTemp.join(' ')); 10 | giftJoined = gift; 11 | bagTemp.length = 0; 12 | } 13 | bagTemp.push(gift); 14 | }); 15 | bags.push(bagTemp.join(' ')); 16 | return bags.filter((gift) => gift !== ''); 17 | } 18 | 19 | module.exports = carryGifts; 20 | 21 | // this solution makes me feel like a noob jajaja 22 | // function carryGifts(gifts, maxWeight) { 23 | // const regex = new RegExp(`\\b(\\w ?){1,${maxWeight}}(?= |$)`, 'g') 24 | // const res = gifts.join(' ').match(regex); 25 | // return res ? res : []; 26 | // } 27 | -------------------------------------------------------------------------------- /2022/17-regalos-en-sacos/index.test.js: -------------------------------------------------------------------------------- 1 | const carryGifts = require('./index'); 2 | 3 | describe('17 => Regalos en sacos', () => { 4 | const testCases = [ 5 | { 6 | input: [['game', 'bike', 'book', 'toy'], 10], 7 | output: ['game bike', 'book toy'], 8 | description: 'should return 2 bags', 9 | }, 10 | { 11 | input: [['game', 'bike', 'book', 'toy'], 7], 12 | output: ['game', 'bike', 'book toy'], 13 | description: 'should return 3 bags', 14 | }, 15 | { 16 | input: [['game', 'bike', 'book', 'toy'], 4], 17 | output: ['game', 'bike', 'book', 'toy'], 18 | description: 'should return 4 bags', 19 | }, 20 | { 21 | input: [['toy', 'gamme', 'toy', 'bike'], 6], 22 | output: ['toy', 'gamme', 'toy', 'bike'], 23 | description: 'should return 4 bags', 24 | }, 25 | { 26 | input: [['toy', 'toy', 'toy', 'toy'], 2], 27 | output: [], 28 | description: 'should return a empty array', 29 | }, 30 | { 31 | input: [['toy', 'toy', 'disk', 'disk', 'toy', 'toy'], 7], 32 | output: ['toy toy', 'disk', 'disk toy', 'toy'], 33 | description: 'should return 4 bags', 34 | }, 35 | ]; 36 | 37 | it('should return an array type', () => { 38 | expect(carryGifts([], 0)).toBeInstanceOf(Array); 39 | }); 40 | 41 | it.each(testCases)('$description', (testCase) => { 42 | expect(carryGifts(...testCase.input)).toEqual(testCase.output); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /2022/18-sin-tinta/README.md: -------------------------------------------------------------------------------- 1 | # Reto 18: ¡Nos quedamos sin tinta! 2 | 3 | ## Problema 4 | 5 | Estamos imprimiendo los códigos de barra para los envíso. En la fábrica de Papá Noel usando un sistema de estampación de números donde cada número se imprime con una tinta diferente. Es un sistema antiguo pero fiable. Sin embargo, a veces nos quedamos sin tinta de un número. 6 | 7 | Escribe una función que recibe el número del que no tenemos tinta (un número que será del 0 al 9) y como segundo parámetro, el número de códigos de barras que hay que imprimir (empezamos desde 1 hasta este número que recibimos). 8 | 9 | Nos debe devolver un array con los números que incluyen el número que no tenemos tinta. Veamos un ejemplo: 10 | 11 | ```js 12 | dryNumber(1, 15); // [1, 10, 11, 12, 13, 14, 15] 13 | 14 | // no tenemos tinta para el 1 15 | // tenemos que imprimir 15 códigos de barras del 1 al 15 16 | // los códigos de barras que saldrán mal por falta de tinta son: 17 | // 1, 10, 11, 12, 13, 14, 15 18 | 19 | dryNumber(2, 20); // [2, 12, 20] 20 | 21 | // no tenemos tinta para el 2 22 | // tenemos que imprimir 20 códigos de barras del 1 al 20 23 | // los códigos de barras que saldrán mal por falta de tinta son: 24 | // 2, 12, 20 25 | ``` 26 | 27 | Ten en cuenta que: 28 | 29 | - El número del que no tenemos tinta sólo puede ser del 0 al 9. 30 | - El número del que no tenemos tinta puede estar en cualquier posición del código de barras. 31 | - El número de códigos de barras que hay que imprimir puede ser muy grande. 32 | 33 | ## Mi Solución 34 | 35 | ```js 36 | function dryNumber(dry, numbers) { 37 | const dryNumbers = []; 38 | while (numbers) { 39 | `${numbers}`.includes(dry) && dryNumbers.push(numbers); 40 | numbers--; 41 | } 42 | return dryNumbers.sort((a, b) => a - b); 43 | } 44 | ``` 45 | -------------------------------------------------------------------------------- /2022/18-sin-tinta/index.js: -------------------------------------------------------------------------------- 1 | function dryNumber(dry, numbers) { 2 | const dryNumbers = []; 3 | while (numbers) { 4 | `${numbers}`.includes(dry) && dryNumbers.push(numbers); 5 | numbers--; 6 | } 7 | return dryNumbers.sort((a, b) => a - b); 8 | } 9 | 10 | module.exports = dryNumber; 11 | -------------------------------------------------------------------------------- /2022/18-sin-tinta/index.test.js: -------------------------------------------------------------------------------- 1 | const dryNumber = require('./index'); 2 | 3 | describe('18 => Sin tinta', () => { 4 | const testCases = [ 5 | { 6 | input: [1, 15], 7 | output: [1, 10, 11, 12, 13, 14, 15], 8 | description: 'should return an array with 7 elements', 9 | }, 10 | { 11 | input: [2, 20], 12 | output: [2, 12, 20], 13 | description: 'should return an array with 3 elements', 14 | }, 15 | { 16 | input: [3, 33], 17 | output: [3, 13, 23, 30, 31, 32, 33], 18 | description: 'should return an array with 7 elements', 19 | }, 20 | { 21 | input: [4, 45], 22 | output: [4, 14, 24, 34, 40, 41, 42, 43, 44, 45], 23 | description: 'should return an array with 10 elements', 24 | }, 25 | { 26 | input: [5, 55], 27 | output: [5, 15, 25, 35, 45, 50, 51, 52, 53, 54, 55], 28 | description: 'should return an array with 11 elements', 29 | }, 30 | { 31 | input: [9, 8], 32 | output: [], 33 | description: 'should return an empty array', 34 | }, 35 | ]; 36 | 37 | it('should return an array type', () => { 38 | expect(dryNumber(1, 1)).toBeInstanceOf(Array); 39 | }); 40 | 41 | it.each(testCases)('$description', (testCase) => { 42 | expect(dryNumber(...testCase.input)).toEqual(testCase.output); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /2022/19-ordenando-los-regalos/README.md: -------------------------------------------------------------------------------- 1 | # Reto 19: Ordenando los regalos 2 | 3 | ## Problema 4 | 5 | El día se acerca y Papá Noel tiene el almacén de juguetes hecho un desastre. Ayúdale a ordenar los juguetes en el almacén para que pueda encontrarlos más fácilmente. 6 | 7 | Para ello, nos dan dos arrays. El primero es un array de juguetes, y el segundo es un array de números que indican la posición de cada juguete en el almacén. 8 | 9 | Lo único a tener en cuenta es que las posiciones pueden no empezar en 0, aunque siempre serán números consecutivos y de forma ascendente. 10 | 11 | Tenemos que devolver un array donde cada juguete esté en la posición que le corresponde. 12 | 13 | ```js 14 | const toys = ["ball", "doll", "car", "puzzle"]; 15 | const positions = [2, 3, 1, 0]; 16 | 17 | sortToys(toys, positions); 18 | // ['puzzle', 'car', 'ball', 'doll'] 19 | 20 | const moreToys = ["pc", "xbox", "ps4", "switch", "nintendo"]; 21 | const morePositions = [8, 6, 5, 7, 9]; 22 | 23 | sortToys(moreToys, morePositions); 24 | // ['ps4', 'xbox', 'switch', 'pc', 'nintendo'] 25 | ``` 26 | 27 | **A tener en cuenta** 28 | 29 | - Siempre habrá el mismo número de juguetes que de posiciones. 30 | - Ni los juguetes ni las posiciones se repiten. 31 | 32 | ## Mi Solución 33 | 34 | ```js 35 | function sortToys(toys, positions) { 36 | const oldPositions = [...positions]; 37 | return positions 38 | .sort((a, b) => a - b) 39 | .map((position) => toys[oldPositions.indexOf(position)]); 40 | } 41 | ``` 42 | -------------------------------------------------------------------------------- /2022/19-ordenando-los-regalos/index.js: -------------------------------------------------------------------------------- 1 | function sortToys(toys, positions) { 2 | const oldPositions = [...positions]; 3 | return positions.sort((a, b) => a - b) 4 | .map((position) => toys[oldPositions.indexOf(position)]); 5 | } 6 | 7 | module.exports = sortToys; 8 | -------------------------------------------------------------------------------- /2022/19-ordenando-los-regalos/index.test.js: -------------------------------------------------------------------------------- 1 | const sortToys = require('./index'); 2 | 3 | describe('19 => Ordenando los regalos', () => { 4 | const testCases = [ 5 | { 6 | input: [['ball', 'doll', 'car', 'puzzle'], [2, 3, 1, 0]], 7 | ouput: ['puzzle', 'car', 'ball', 'doll'], 8 | }, 9 | { 10 | input: [['pc', 'xbox', 'ps4', 'switch', 'nintendo'], [8, 6, 5, 7, 9]], 11 | ouput: ['ps4', 'xbox', 'switch', 'pc', 'nintendo'], 12 | }, 13 | { 14 | input: [['pc', 'xbox', 'ps4', 'switch', 'nintendo'], [3, 1, 0, 2, 4]], 15 | ouput: ['ps4', 'xbox', 'switch', 'pc', 'nintendo'], 16 | }, 17 | { 18 | input: [['pc', 'xbox', 'ps4', 'switch', 'nintendo'], [8, 6, 5, 7, 9]], 19 | ouput: ['ps4', 'xbox', 'switch', 'pc', 'nintendo'], 20 | }, 21 | { 22 | input: [ 23 | ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'l'], 24 | [1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119, 1120, 1121, 1111], 25 | ], 26 | ouput: ['l', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k'], 27 | }, 28 | ]; 29 | 30 | it('should return an array type', () => { 31 | expect(sortToys([], [])).toBeInstanceOf(Array); 32 | }); 33 | 34 | it.each(testCases)('should return toys sorted by position', (testCase) => { 35 | expect(sortToys(...testCase.input)).toEqual(testCase.ouput); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /2022/20-mas-viajes-retadores/index.js: -------------------------------------------------------------------------------- 1 | function howManyReindeers(reindeerTypes, gifts) { 2 | reindeerTypes.sort((a, b) => b.weightCapacity - a.weightCapacity); 3 | return gifts.map(({ country, weight }) => { 4 | let maxWeightCountry = weight; 5 | const reindeersUsables = reindeerTypes 6 | .filter((reindeer) => reindeer.weightCapacity < maxWeightCountry); 7 | let maxCapacity = reindeersUsables 8 | .reduce((acc, curr) => acc += curr.weightCapacity, 0); 9 | return { 10 | country, 11 | reindeers: reindeersUsables.map(({ type, weightCapacity }) => { 12 | const quantity = Math.floor(maxWeightCountry / maxCapacity); 13 | maxCapacity -= weightCapacity; 14 | maxWeightCountry -= quantity * weightCapacity; 15 | return { type, num: quantity }; 16 | }), 17 | }; 18 | }); 19 | } 20 | 21 | module.exports = howManyReindeers; 22 | -------------------------------------------------------------------------------- /2022/21-tabla-de-regalos/index.js: -------------------------------------------------------------------------------- 1 | function printTable(gifts) { 2 | const gTitle = 'Gift'; 3 | const qTitle = 'Quantity'; 4 | const [maxNameLength, maxQuantityLength] = [ 5 | Math.max(gTitle.length, ...gifts 6 | .map(({ name }) => name.length)), 7 | Math.max(qTitle.length, ...gifts 8 | .map(({ quantity }) => `${quantity}`.length)), 9 | ]; 10 | const topLine = `${'+'.repeat(maxNameLength + maxQuantityLength + 7)}`; 11 | // eslint-disable-next-line prefer-template 12 | const header = '| ' + gTitle + ' '.repeat(maxNameLength - gTitle.length) 13 | + ' | ' + qTitle 14 | + ' '.repeat(maxQuantityLength - qTitle.length) + ' |'; 15 | // eslint-disable-next-line prefer-template 16 | const separator = '| ' + '-'.repeat(maxNameLength) + ' | ' 17 | + '-'.repeat(maxQuantityLength) + ' |'; 18 | const body = gifts 19 | .map(({ name, quantity }) => { 20 | const nameGift = `${name}${' '.repeat(maxNameLength - name.length)}`; 21 | // eslint-disable-next-line prefer-template 22 | const quantityGift = quantity 23 | + ' '.repeat(maxQuantityLength - quantity.toString().length); 24 | return `| ${nameGift} | ${quantityGift} |`; 25 | }) 26 | .join('\n'); 27 | const bottomLine = `${'*'.repeat(maxNameLength + maxQuantityLength + 7)}`; 28 | const table = `${topLine}\n${header}\n${separator}\n${body}\n${bottomLine}`; 29 | return table; 30 | } 31 | 32 | module.exports = printTable; 33 | -------------------------------------------------------------------------------- /2022/21-tabla-de-regalos/index.test.js: -------------------------------------------------------------------------------- 1 | const printTable = require('./index'); 2 | 3 | describe('21 => Tabla de regalos', () => { 4 | const testCases = [ 5 | { 6 | input: [ 7 | [ 8 | { name: 'PlayStation 5', quantity: 9234782374892 }, 9 | { name: 'Book Learn Web Dev', quantity: 23531 }, 10 | ], 11 | ], 12 | output: '++++++++++++++++++++++++++++++++++++++\n| Gift | Quantity |' 13 | + '\n| ------------------ | ------------- |\n| PlayStation 5 | 9234782374892 |\n| ' 14 | + 'Book Learn Web Dev | 23531 |\n**************************************', 15 | }, 16 | { 17 | input: [ 18 | [ 19 | { name: 'Game', quantity: 2 }, 20 | { name: 'Bike', quantity: 1 }, 21 | { name: 'Book', quantity: 3 }, 22 | ], 23 | ], 24 | output: '+++++++++++++++++++\n| Gift | Quantity |\n| ---- | -------- |\n' 25 | + '| Game | 2 |\n| Bike | 1 |\n| Book | 3 |\n*******************', 26 | }, 27 | { 28 | input: [ 29 | [ 30 | { name: 'Game', quantity: 10000 }, 31 | ], 32 | ], 33 | output: '+++++++++++++++++++\n| Gift | Quantity |\n| ---- | -------- |\n| Game | 10000 |\n*******************', 34 | }, 35 | { 36 | input: [ 37 | [ 38 | { name: 'Game', quantity: 1234567890 }, 39 | ], 40 | ], 41 | output: '+++++++++++++++++++++\n| Gift | Quantity |\n| ---- | ---------- |\n' 42 | + '| Game | 1234567890 |\n*********************', 43 | }, 44 | { 45 | input: [ 46 | [ 47 | { name: 'Toy', quantity: 12 }, 48 | { name: 'Mic', quantity: 123 }, 49 | ], 50 | ], 51 | output: '+++++++++++++++++++\n| Gift | Quantity |\n| ---- | -------- |\n' 52 | + '| Toy | 12 |\n| Mic | 123 |\n*******************', 53 | }, 54 | ]; 55 | 56 | it('should return an string type', () => { 57 | expect(typeof printTable(...testCases[0].input)).toBe('string'); 58 | }); 59 | 60 | it.each(testCases)('should return the correct table', (testCase) => { 61 | expect(printTable(...testCase.input)).toBe(testCase.output); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /2022/22-iluminacion-en-sintonia/README.md: -------------------------------------------------------------------------------- 1 | # Reto 22: La iluminación en sintonía 2 | 3 | ## Problema 4 | 5 | Verifica que todas las secuencias independientes de sistemas de iluminación navideña estén en orden estrictamente creciente. Tenemos dos arrays: systemNames y stepNumbers. 6 | 7 | systemNames contiene los nombres de los sistemas de iluminación navideña, y stepNumbers contiene los números de paso de cada sistema. 8 | 9 | Debemos verificar que los stepNumbers de cada sistema estén en orden estrictamente creciente. Si esto es cierto, devuelve true; de lo contrario, devuelve false. 10 | 11 | Por ejemplo: 12 | 13 | ```js 14 | const systemNames = ["tree_1", "tree_2", "house", "tree_1", "tree_2", "house"]; 15 | const stepNumbers = [1, 33, 10, 2, 44, 20]; 16 | 17 | checkStepNumbers(systemNames, stepNumbers); // => true 18 | 19 | // tree_1 tiene los pasos: [1, 2] 20 | // tree_2 tiene los pasos: [33, 44] 21 | // house tiene los pasos: [10, 20] 22 | 23 | // true: Los pasos de cada sistema están en orden estrictamente creciente 24 | 25 | checkStepNumbers(["tree_1", "tree_1", "house"], [2, 1, 10]); // => false 26 | 27 | // tree_1 tiene los pasos: [2, 1] 28 | // house tiene los pasos: [10] 29 | 30 | // false: tree_1 tiene los pasos de forma decreciente 31 | ``` 32 | 33 | Ten en cuenta que: 34 | 35 | - La posición del nombre del sistema en systemNames y el número de paso en stepNumbers corresponden al mismo sistema. 36 | - Los pasos en stepNumbers pueden repetirse para diferentes sistemas. 37 | 38 | ## Mi Solución 39 | 40 | ```js 41 | function checkStepNumbers(systemNames, stepNumbers) { 42 | return systemNames.every( 43 | (_, i) => 44 | stepNumbers[i] <= 45 | stepNumbers[i + systemNames.slice(i + 1).indexOf(systemNames[i]) + 1] 46 | ); 47 | } 48 | ``` 49 | -------------------------------------------------------------------------------- /2022/22-iluminacion-en-sintonia/index.js: -------------------------------------------------------------------------------- 1 | function checkStepNumbers(systemNames, stepNumbers) { 2 | return systemNames 3 | .every((_, i) => stepNumbers[i] <= stepNumbers[i + systemNames 4 | .slice(i + 1).indexOf(systemNames[i]) + 1 5 | ]); 6 | } 7 | 8 | module.exports = checkStepNumbers; 9 | -------------------------------------------------------------------------------- /2022/22-iluminacion-en-sintonia/index.test.js: -------------------------------------------------------------------------------- 1 | const checkStepNumbers = require('./index'); 2 | 3 | describe('22 => Iluminación en sintonía', () => { 4 | const testCases = [ 5 | { 6 | input: [['tree_1', 'tree_2', 'house', 'tree_1', 'tree_2', 'house'], 7 | [1, 33, 10, 2, 44, 20]], 8 | output: true, 9 | description: 'should return true', 10 | }, 11 | { 12 | input: [['tree_1', 'tree_1', 'house'], [2, 1, 10]], 13 | output: false, 14 | description: 'should return false', 15 | }, 16 | { 17 | input: [['tree_1', 'tree_1', 'house'], [1, 2, 10]], 18 | output: true, 19 | description: 'should return true', 20 | }, 21 | { 22 | input: [['house', 'house', 'tree_1', 'tree_1', 'house', 'tree_2', 'tree_2', 'tree_3'], [5, 2, 1, 2, 3, 4, 5, 6]], 23 | output: false, 24 | description: 'should return false', 25 | }, 26 | ]; 27 | 28 | it('should return a boolean type', () => { 29 | expect(typeof checkStepNumbers(...testCases[0].input)).toBe('boolean'); 30 | }); 31 | 32 | it.each(testCases)('$description', (testCase) => { 33 | expect(checkStepNumbers(...testCase.input)).toBe(testCase.output); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /2022/23-compilador-de-papa-noel/index.js: -------------------------------------------------------------------------------- 1 | function executeCommands(commands) { 2 | const memory = [0, 0, 0, 0, 0, 0, 0, 0]; 3 | let pointer = 0; 4 | 5 | const cpuCommands = { 6 | MOV: (value, prompter) => { 7 | const to = +prompter.replace('V', ''); 8 | const from = +value.replace('V', ''); 9 | memory[to] = Number.isNaN(+value) ? memory[from] : memory[to] = +value; 10 | ++pointer; 11 | }, 12 | ADD: (to, valuePrompter) => { 13 | to = +to.replace('V', ''); 14 | valuePrompter = +valuePrompter.replace('V', ''); 15 | memory[to] = (memory[to] + memory[valuePrompter]) % 256; 16 | ++pointer; 17 | }, 18 | DEC: (prompter) => { 19 | prompter = +prompter.replace('V', ''); 20 | memory[prompter] = (((memory[prompter] - 1) % 256) + 256) % 256; 21 | ++pointer; 22 | }, 23 | INC: (prompter) => { 24 | prompter = +prompter.replace('V', ''); 25 | memory[prompter] = (memory[prompter] + 1) % 256; 26 | ++pointer; 27 | }, 28 | JMP: (index) => { 29 | pointer = memory[0] ? +index : pointer + 1; 30 | }, 31 | }; 32 | 33 | while (pointer < commands.length) { 34 | // eslint-disable-next-line prefer-const 35 | let [command, ...args] = commands[pointer].split(' '); 36 | args = args[0].split(','); 37 | cpuCommands[command](...args); 38 | } 39 | return memory; 40 | } 41 | 42 | module.exports = executeCommands; 43 | -------------------------------------------------------------------------------- /2022/23-compilador-de-papa-noel/index.test.js: -------------------------------------------------------------------------------- 1 | const executeCommands = require('./index'); 2 | 3 | describe('23 => Compilador de Papa Noel', () => { 4 | const testCases = [ 5 | { 6 | input: [ 7 | 'MOV 5,V00', 8 | 'MOV 10,V01', 9 | 'DEC V00', 10 | 'ADD V00,V01', 11 | ], 12 | output: [14, 10, 0, 0, 0, 0, 0, 0], 13 | }, 14 | { 15 | input: [ 16 | 'MOV 255,V00', 17 | 'INC V00', 18 | 'DEC V01', 19 | 'DEC V01', 20 | ], 21 | output: [0, 254, 0, 0, 0, 0, 0, 0], 22 | }, 23 | { 24 | input: [ 25 | 'MOV 10,V00', 26 | 'DEC V00', 27 | 'INC V01', 28 | 'JMP 1', 29 | 'INC V06', 30 | ], 31 | output: [0, 10, 0, 0, 0, 0, 1, 0], 32 | }, 33 | { 34 | input: [ 35 | 'MOV 10,V00', 36 | 'MOV V00,V01', 37 | 'MOV V01,V02', 38 | 'MOV V02,V03', 39 | 'MOV V03,V04', 40 | ], 41 | output: [10, 10, 10, 10, 10, 0, 0, 0], 42 | }, 43 | ]; 44 | 45 | it('should return an array type', () => { 46 | const result = executeCommands(testCases[0].input); 47 | expect(Array.isArray(result)).toBe(true); 48 | }); 49 | 50 | it.each(testCases)('should return the correct result', (testCase) => { 51 | const result = executeCommands(testCase.input); 52 | expect(result).toEqual(testCase.output); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /2022/24-laberinto/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-continue */ 2 | function canExit(maze) { 3 | const start = maze.findIndex((row) => row.includes('S')); 4 | const end = maze.findIndex((row) => row.includes('E')); 5 | const queue = []; 6 | const visited = new Set(); 7 | queue.push(start * maze[0].length + maze[start].indexOf('S')); 8 | 9 | while (queue.length > 0) { 10 | const current = queue.shift(); 11 | const row = Math.floor(current / maze[0].length); 12 | const col = current % maze[0].length; 13 | 14 | if (maze[row][col] === 'W' || visited.has(current)) continue; 15 | visited.add(current); 16 | 17 | if (current === end * maze[0].length + maze[end].indexOf('E')) return true; 18 | 19 | if (row > 0) queue.push(current - maze[0].length); 20 | if (row < maze.length - 1) queue.push(current + maze[0].length); 21 | if (col > 0) queue.push(current - 1); 22 | if (col < maze[0].length - 1) queue.push(current + 1); 23 | } 24 | return false; 25 | } 26 | 27 | module.exports = canExit; 28 | -------------------------------------------------------------------------------- /2023/01-primer-regalo-repetido/README.md: -------------------------------------------------------------------------------- 1 | # Reto 1: !Primer regalo repetido! 2 | 3 | ## Problema 4 | 5 | En la fábrica de juguetes del Polo Norte, cada juguete tiene un número de identificación único. 6 | 7 | Sin embargo, debido a un error en la máquina de juguetes, algunos números se han asignado a más de un juguete. 8 | 9 | ¡Encuentra el primer número de identificación que se ha repetido, **donde la segunda ocurrencia tenga el índice más pequeño!** 10 | 11 | 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. 12 | 13 | ```js 14 | const giftIds = [2, 1, 3, 5, 3, 2] 15 | const firstRepeatedId = findFirstRepeated(giftIds) 16 | console.log(firstRepeatedId) // 3 17 | // Aunque el 2 y el 3 se repiten 18 | // el 3 aparece primero por segunda vez 19 | 20 | const giftIds2 = [1, 2, 3, 4] 21 | const firstRepeatedId2 = findFirstRepeated(giftIds2) 22 | console.log(firstRepeatedId2) // -1 23 | // Es -1 ya que no se repite ningún número 24 | 25 | const giftIds3 = [5, 1, 5, 1] 26 | const firstRepeatedId3 = findFirstRepeated(giftIds3) 27 | console.log(firstRepeatedId3) // 5 28 | ``` 29 | 30 | **¡Ojo!** Los elfos dicen que esto es una prueba técnica de Google. 31 | 32 | ## Mi solución 33 | 34 | ```js 35 | const findFirstRepeated = (gifts) => gifts.find((gift, index) => gifts.indexOf(gift) !== index) ?? -1; 36 | ``` 37 | 38 | ## Explicación de mi solución 39 | 40 | 1. Utilizo el método `find` para encontrar el primer elemento que cumpla la condición de que el índice del elemento en el array sea diferente al índice del elemento en el array si se utiliza el método `indexOf` para encontrar el índice del elemento en el array. Si no se encuentra ningún elemento que cumpla la condición, se devuelve `undefined`. 41 | 42 | 2. Si el resultado de la expresión anterior es `undefined`, se devuelve `-1` utilizando el operador de encadenamiento opcional `??`. 43 | -------------------------------------------------------------------------------- /2023/01-primer-regalo-repetido/index.js: -------------------------------------------------------------------------------- 1 | // complejidad: 1 2 | const findFirstRepeated = (gifts) => gifts.find((gift, index) => gifts.indexOf(gift) !== index) ?? -1; 3 | 4 | // complejidad: 3 5 | const findFirstRepeated2 = (gifts) => { 6 | const repeated = gifts.filter((id, index) => gifts.indexOf(id) !== index); 7 | return repeated.length > 0 ? repeated[0] : -1; 8 | }; 9 | 10 | // complejidad: 3 11 | const findFirstRepeated3 = (gifts) => ( 12 | [...new Set(gifts)].length === gifts.length 13 | ? -1 14 | : gifts.find((id, index) => gifts.indexOf(id) !== index) 15 | ); 16 | 17 | module.exports = { 18 | findFirstRepeated, 19 | findFirstRepeated2, 20 | findFirstRepeated3, 21 | }; 22 | -------------------------------------------------------------------------------- /2023/01-primer-regalo-repetido/index.test.js: -------------------------------------------------------------------------------- 1 | const { findFirstRepeated, findFirstRepeated2, findFirstRepeated3 } = require('./index'); 2 | 3 | describe('01 => Primer regalo repetido', () => { 4 | const testCases = [ 5 | { 6 | input: [2, 1, 3, 5, 3, 2], 7 | output: 3, 8 | }, 9 | { 10 | input: [2, 2], 11 | output: 2, 12 | }, 13 | { 14 | input: [2, 4, 3, 5, 1], 15 | output: -1, 16 | }, 17 | { 18 | input: [1], 19 | output: -1, 20 | }, 21 | { 22 | input: [], 23 | output: -1, 24 | }, 25 | { 26 | input: [10, 20, 30], 27 | output: -1, 28 | }, 29 | { 30 | input: [1, 2, 3, 4], 31 | output: -1, 32 | }, 33 | { 34 | input: [5, 1, 2, 3, 2, 5, 1], 35 | output: 2, 36 | }, 37 | { 38 | input: [12, 20, 30, 11, 20, 12], 39 | output: 20, 40 | }, 41 | { 42 | input: [5, 1, 5, 1], 43 | output: 5, 44 | }, 45 | ]; 46 | 47 | it('should return a number type', () => { 48 | expect(typeof findFirstRepeated([])).toBe('number'); 49 | expect(typeof findFirstRepeated2([])).toBe('number'); 50 | expect(typeof findFirstRepeated3([])).toBe('number'); 51 | }); 52 | 53 | it.each(testCases)('should return $output', ({ input, output }) => { 54 | expect(findFirstRepeated(input)).toBe(output); 55 | expect(findFirstRepeated2(input)).toBe(output); 56 | expect(findFirstRepeated3(input)).toBe(output); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /2023/02-ponemos-en-marcha-la-fabrica/README.md: -------------------------------------------------------------------------------- 1 | # Reto 02: Ponemos en marcha la fabrica 2 | 3 | ## Problema 4 | 5 | En el taller de Santa, los elfos tienen una lista de regalos que desean fabricar y un conjunto limitado de materiales. 6 | 7 | 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. 8 | 9 | Un regalo se puede fabricar si contamos con todos los materiales necesarios para fabricarlo. 10 | 11 | ```js 12 | const gifts = ['tren', 'oso', 'pelota'] 13 | const materials = 'tronesa' 14 | 15 | manufacture(gifts, materials) // ["tren", "oso"] 16 | // 'tren' SÍ porque sus letras están en 'tronesa' 17 | // 'oso' SÍ porque sus letras están en 'tronesa' 18 | // 'pelota' NO porque sus letras NO están en 'tronesa' 19 | 20 | const gifts = ['juego', 'puzzle'] 21 | const materials = 'jlepuz' 22 | 23 | manufacture(gifts, materials) // ["puzzle"] 24 | 25 | const gifts = ['libro', 'ps5'] 26 | const materials = 'psli' 27 | 28 | manufacture(gifts, materials) // [] 29 | ``` 30 | 31 | ## Mi solución 32 | 33 | ```js 34 | const manufacture = (gifts, materials) => gifts.filter((gift) => ( 35 | [...gift].every((letter) => materials.includes(letter)))); 36 | ``` 37 | 38 | ## Explicación de mi solución 39 | 40 | 1. Utilizo el método `filter` para filtrar los regalos que se pueden fabricar. Para ello, utilizo el método `every` para comprobar que todas las letras del regalo están en los materiales disponibles. Para ello, convierto el regalo en un array de letras utilizando el operador de propagación `...` y compruebo que todas las letras están en los materiales utilizando el método `includes`. 41 | -------------------------------------------------------------------------------- /2023/02-ponemos-en-marcha-la-fabrica/index.js: -------------------------------------------------------------------------------- 1 | const manufacture = (gifts, materials) => gifts.filter((gift) => ( 2 | [...gift].every((letter) => materials.includes(letter)))); 3 | 4 | module.exports = manufacture; 5 | -------------------------------------------------------------------------------- /2023/02-ponemos-en-marcha-la-fabrica/index.test.js: -------------------------------------------------------------------------------- 1 | const manufacture = require('./index'); 2 | 3 | describe('02 => Ponemos en marcha la fabrica', () => { 4 | const testCases = [ 5 | { 6 | input: [['tren', 'oso', 'pelota'], 'tronesa'], 7 | output: ['tren', 'oso'], 8 | }, 9 | { 10 | input: [['coche', 'muñeca', 'balon'], 'ocmuñalb'], 11 | output: [], 12 | }, 13 | { 14 | input: [['patineta', 'robot', 'libro'], 'nopor'], 15 | output: [], 16 | }, 17 | { 18 | input: [[], 'letras'], 19 | output: [], 20 | }, 21 | { 22 | input: [['juego', 'puzzle'], 'jlepuz'], 23 | output: ['puzzle'], 24 | }, 25 | ]; 26 | 27 | it('should return an array type ', () => { 28 | expect(Array.isArray(manufacture(['a'], ['a']))).toBe(true); 29 | }); 30 | 31 | it.each(testCases)('should return $output', ({ input, output }) => { 32 | expect(manufacture(...input)).toEqual(output); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /2023/03-el-elfo-travieso/README.md: -------------------------------------------------------------------------------- 1 | # Reto 03: El elfo travieso 2 | 3 | ## Problema 4 | 5 | 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. 6 | 7 | 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. 8 | 9 | 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. 10 | 11 | ```js 12 | const original = 'abcd' 13 | const modified = 'abcde' 14 | findNaughtyStep(original, modified) // 'e' 15 | 16 | const original = 'stepfor' 17 | const modified = 'stepor' 18 | findNaughtyStep(original, modified) // 'f' 19 | 20 | const original = 'abcde' 21 | const modified = 'abcde' 22 | findNaughtyStep(original, modified) // '' 23 | ``` 24 | 25 | A tener en cuenta: 26 | 27 | - **Siempre habrá un paso de diferencia o ninguno.** 28 | - **La modificación puede ocurrir en cualquier lugar de la cadena.** 29 | - **La secuencia original puede estar vacía** 30 | -------------------------------------------------------------------------------- /2023/03-el-elfo-travieso/index.js: -------------------------------------------------------------------------------- 1 | const findNaughtyStep = (original, modified) => { 2 | const maxLength = Math.max(original.length, modified.length); 3 | 4 | for (let i = 0; i < maxLength; i++) { 5 | if (original[i] !== modified[i]) { 6 | return original.length > modified.length ? original[i] : modified[i]; 7 | } 8 | } 9 | 10 | return ''; 11 | }; 12 | 13 | module.exports = findNaughtyStep; 14 | -------------------------------------------------------------------------------- /2023/03-el-elfo-travieso/index.test.js: -------------------------------------------------------------------------------- 1 | const findNaughtyStep = require('./index'); 2 | 3 | describe('03 => El elfo travieso', () => { 4 | const testCases = [ 5 | { 6 | input: ['abcd', 'abcde'], 7 | output: 'e', 8 | }, 9 | { 10 | input: ['abcde', 'abcd'], 11 | output: 'e', 12 | }, 13 | { 14 | input: ['xxxx', 'xxoxx'], 15 | output: 'o', 16 | }, 17 | { 18 | input: ['stepfor', 'stepor'], 19 | output: 'f', 20 | }, 21 | { 22 | input: ['iiiii', 'iiiii'], 23 | output: '', 24 | }, 25 | ]; 26 | 27 | it('should return a string type', () => { 28 | expect(typeof findNaughtyStep('a', 'a')).toBe('string'); 29 | }); 30 | 31 | it.each(testCases)('should return $output', ({ input, output }) => { 32 | expect(findNaughtyStep(...input)).toBe(output); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /2023/04-dale-la-vuelta-a-los-parentesis/index.js: -------------------------------------------------------------------------------- 1 | // complejidad: 3 2 | const decode = (message) => { 3 | let result = ''; 4 | const stack = []; 5 | 6 | [...message].forEach((char) => { 7 | if (char === '(') { 8 | stack.push(result); 9 | result = ''; 10 | } else if (char === ')') { 11 | result = `${stack.pop()}${result.split('').reverse().join('')}`; 12 | } else { 13 | result += char; 14 | } 15 | }); 16 | 17 | return result; 18 | }; 19 | 20 | // complejidad: 2 21 | const decode2 = (message) => { 22 | const regex = /\(([^()]+)\)/; 23 | let match = message.match(regex); 24 | while (match) { 25 | const reversed = match[1].split('').reverse().join(''); 26 | message = message.replace(match[0], reversed); 27 | match = message.match(regex); 28 | } 29 | return message; 30 | }; 31 | 32 | module.exports = { 33 | decode, 34 | decode2, 35 | }; 36 | -------------------------------------------------------------------------------- /2023/04-dale-la-vuelta-a-los-parentesis/index.test.js: -------------------------------------------------------------------------------- 1 | const { decode, decode2 } = require('./index'); 2 | 3 | describe('04 => Dale la vuelta a los parentesis', () => { 4 | const testCases = [ 5 | { 6 | input: 'hola (odnum)', 7 | output: 'hola mundo', 8 | }, 9 | { 10 | input: '(olleh) (dlrow)!', 11 | output: 'hello world!', 12 | }, 13 | { 14 | input: 'sa(u(cla)atn)s', 15 | output: 'santaclaus', 16 | }, 17 | { 18 | input: '((nta)(sa))', 19 | output: 'santa', 20 | }, 21 | ]; 22 | 23 | it('should return a string type', () => { 24 | expect(typeof decode('a')).toBe('string'); 25 | expect(typeof decode2('a')).toBe('string'); 26 | }); 27 | 28 | it.each(testCases)('should return $output', ({ input, output }) => { 29 | expect(decode(input)).toBe(output); 30 | expect(decode2(input)).toBe(output); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /2023/05-el-cybertruck-de-santa/index.js: -------------------------------------------------------------------------------- 1 | const cyberReindeer = (road, time) => { 2 | const moves = [road]; 3 | let currentPosition = 0; 4 | let b = '.'; 5 | 6 | for (let position = 1; position < time; position++) { 7 | if (position === 5) road = road.replace(/\|/g, '*'); 8 | const newRoad = road.replace(/S[.*]/, `${b}S`); 9 | 10 | if (newRoad !== road) { 11 | currentPosition++; 12 | b = road[currentPosition]; 13 | } 14 | 15 | road = newRoad; 16 | moves.push(road); 17 | } 18 | 19 | return moves; 20 | }; 21 | 22 | module.exports = cyberReindeer; 23 | -------------------------------------------------------------------------------- /2023/05-el-cybertruck-de-santa/index.test.js: -------------------------------------------------------------------------------- 1 | const cyberReindeer = require('./index'); 2 | 3 | describe('05 => El cybertruck de santa', () => { 4 | const testCases = [ 5 | { 6 | input: ['S..|...|..', 10], 7 | output: [ 8 | 'S..|...|..', 9 | '.S.|...|..', 10 | '..S|...|..', 11 | '..S|...|..', 12 | '..S|...|..', 13 | '...S...*..', 14 | '...*S..*..', 15 | '...*.S.*..', 16 | '...*..S*..', 17 | '...*...S..', 18 | ], 19 | }, 20 | { 21 | input: ['S.|.', 4], 22 | output: [ 23 | 'S.|.', 24 | '.S|.', 25 | '.S|.', 26 | '.S|.', 27 | ], 28 | }, 29 | { 30 | input: ['S.|.|.', 7], 31 | output: [ 32 | 'S.|.|.', 33 | '.S|.|.', 34 | '.S|.|.', 35 | '.S|.|.', 36 | '.S|.|.', 37 | '..S.*.', 38 | '..*S*.', 39 | ], 40 | }, 41 | { 42 | input: ['S.|..', 6], 43 | output: [ 44 | 'S.|..', 45 | '.S|..', 46 | '.S|..', 47 | '.S|..', 48 | '.S|..', 49 | '..S..', 50 | ], 51 | }, 52 | { 53 | input: ['S.|.|.|......|.||.........', 8], 54 | output: [ 55 | 'S.|.|.|......|.||.........', 56 | '.S|.|.|......|.||.........', 57 | '.S|.|.|......|.||.........', 58 | '.S|.|.|......|.||.........', 59 | '.S|.|.|......|.||.........', 60 | '..S.*.*......*.**.........', 61 | '..*S*.*......*.**.........', 62 | '..*.S.*......*.**.........', 63 | ], 64 | }, 65 | ]; 66 | 67 | it('should return an array type', () => { 68 | expect(Array.isArray(cyberReindeer('S|.....', 5))).toBe(true); 69 | }); 70 | 71 | it.each(testCases)('should return $output', ({ input, output }) => { 72 | expect(cyberReindeer(...input)).toEqual(output); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /2023/06-los-renos-a-prueba/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-syntax */ 2 | 3 | // complejidad: 4 4 | const maxDistance1 = (movements) => { 5 | const { move, stars } = movements.split('').reduce((acc, movement) => { 6 | if (movement === '>') acc.move++; 7 | else if (movement === '<') acc.move--; 8 | else acc.stars++; 9 | return acc; 10 | }, { move: 0, stars: 0 }); 11 | 12 | return Math.abs(move) + stars; 13 | }; 14 | 15 | // complejidad: 3 16 | // para el problema es la que mas da puntos 17 | const maxDistance2 = (movements) => { 18 | let move = 0; 19 | let stars = 0; 20 | 21 | for (const movement of movements) { 22 | if (movement === '>') move++; 23 | else if (movement === '<') move--; 24 | else stars++; 25 | } 26 | 27 | return Math.abs(move) + stars; 28 | }; 29 | 30 | // complejidad: 1 31 | const maxDistance3 = (movements) => { 32 | const left = movements.match(//g)?.length ?? 0; 34 | const stars = movements.match(/\*/g)?.length ?? 0; 35 | return Math.abs(left - right) + stars; 36 | }; 37 | 38 | // complejidad: 1 39 | const maxDistance4 = (movements) => { 40 | const counts = { '<': 0, '>': 0, '*': 0 }; 41 | 42 | for (const movement of movements) { 43 | counts[movement]++; 44 | } 45 | 46 | return Math.abs(counts['<'] - counts['>']) + counts['*']; 47 | }; 48 | 49 | module.exports = { 50 | maxDistance1, 51 | maxDistance2, 52 | maxDistance3, 53 | maxDistance4, 54 | }; 55 | -------------------------------------------------------------------------------- /2023/06-los-renos-a-prueba/index.test.js: -------------------------------------------------------------------------------- 1 | const { 2 | maxDistance1, maxDistance2, maxDistance3, maxDistance4, 3 | } = require('./index'); 4 | 5 | describe('06 => Los renos a prueba', () => { 6 | const testCases = [ 7 | { 8 | input: '>>*<', 9 | output: 2, 10 | }, 11 | { 12 | input: '<<<<<', 13 | output: 5, 14 | }, 15 | { 16 | input: '>***>', 17 | output: 5, 18 | }, 19 | { 20 | input: '**********', 21 | output: 10, 22 | }, 23 | ]; 24 | 25 | it('should return a number type', () => { 26 | expect(typeof maxDistance1('')).toBe('number'); 27 | expect(typeof maxDistance2('')).toBe('number'); 28 | expect(typeof maxDistance3('')).toBe('number'); 29 | expect(typeof maxDistance4('')).toBe('number'); 30 | }); 31 | 32 | it.each(testCases)('should return $output', ({ input, output }) => { 33 | expect(maxDistance1(input)).toBe(output); 34 | expect(maxDistance2(input)).toBe(output); 35 | expect(maxDistance3(input)).toBe(output); 36 | expect(maxDistance4(input)).toBe(output); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /2023/07-las-cajas-en-3d/index.js: -------------------------------------------------------------------------------- 1 | const drawGift = (size, symbol) => { 2 | const WRAPPER = '#'; 3 | const SPACE = ' '; 4 | 5 | if (size <= 1) return `${WRAPPER}\n`; 6 | 7 | const top = [SPACE.repeat(size - 1) + WRAPPER.repeat(size)]; 8 | const bottom = [`${WRAPPER.repeat(size)}`]; 9 | const middle = `${WRAPPER.repeat(size)}${symbol.repeat(Math.abs(size - 2))}` 10 | + `${WRAPPER}\n`; 11 | for (let i = 1; i < size; i++) { 12 | const line = `${WRAPPER}${symbol.repeat(size - 2)}${WRAPPER}` 13 | + `${symbol.repeat(i - 1)}${WRAPPER}`; 14 | top.push(SPACE.repeat(size - i - 1) + line); 15 | bottom.push(line); 16 | } 17 | 18 | top.pop(); 19 | bottom.pop(); 20 | top.push(middle); 21 | bottom.reverse(); 22 | return `${top.join('\n')}${bottom.join('\n')}\n`; 23 | }; 24 | 25 | module.exports = drawGift; 26 | -------------------------------------------------------------------------------- /2023/07-las-cajas-en-3d/index.test.js: -------------------------------------------------------------------------------- 1 | const drawGift = require('./index'); 2 | 3 | describe('07 => Las cajas en 3d', () => { 4 | const testCases = [ 5 | { 6 | input: [4, '+'], 7 | output: ' ####\n #++##\n #++#+#\n####++#\n#++#+#\n#++##\n####\n', 8 | }, 9 | { 10 | input: [1, '^'], 11 | output: '#\n', 12 | }, 13 | { 14 | input: [5, '*'], 15 | output: ' #####\n #***##\n #***#*#\n #***#**#\n#####***#\n#***#**#\n#***#*#\n#***##\n#####\n', 16 | }, 17 | { 18 | input: [2, '&'], 19 | output: ' ##\n###\n##\n', 20 | }, 21 | { 22 | input: [10, '%'], 23 | output: ' ##########\n #%%%%%%%%##\n #%%%%%%%%#%#\n #%%%%%%%%#%%#' 24 | + '\n #%%%%%%%%#%%%#\n #%%%%%%%%#%%%%#\n #%%%%%%%%#%%%%%#\n #%%%%%%%%#%%%%%%#' 25 | + '\n #%%%%%%%%#%%%%%%%#\n##########%%%%%%%%#\n#%%%%%%%%#%%%%%%%#\n#%%%%%%%%#%%%%%%#' 26 | + '\n#%%%%%%%%#%%%%%#\n#%%%%%%%%#%%%%#\n#%%%%%%%%#%%%#\n#%%%%%%%%#%%#\n#%%%%%%%%#%#' 27 | + '\n#%%%%%%%%##\n##########\n', 28 | }, 29 | ]; 30 | 31 | it('should return a string type', () => { 32 | expect(typeof drawGift(1, '*')).toBe('string'); 33 | }); 34 | 35 | it.each(testCases)('should return the correct output', ({ input, output }) => { 36 | expect(drawGift(...input)).toBe(output); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /2023/08-ordenando-el-almacen/index.js: -------------------------------------------------------------------------------- 1 | const organizeGifts = (gifts) => { 2 | const PALLET_SIZE = 50; 3 | const BOX_SIZE = 10; 4 | 5 | const giftsArray = gifts.match(/\d+[a-z]/g); 6 | return giftsArray.map((gift) => { 7 | const [quantity, type] = gift.match(/(\d+)([a-z])/).slice(1); 8 | const pales = Math.floor(quantity / PALLET_SIZE); 9 | const boxes = (quantity % PALLET_SIZE) / BOX_SIZE; 10 | const bags = quantity % BOX_SIZE; 11 | 12 | return `[${type}]`.repeat(pales) 13 | + `{${type}}`.repeat(boxes) 14 | + `(${type.repeat(bags)})`.repeat(bags > 0); 15 | }).join(''); 16 | }; 17 | 18 | module.exports = organizeGifts; 19 | -------------------------------------------------------------------------------- /2023/08-ordenando-el-almacen/index.test.js: -------------------------------------------------------------------------------- 1 | const organizeGifts = require('./index'); 2 | 3 | describe('08 => Ordenando el almacen', () => { 4 | const testCases = [ 5 | { 6 | input: '76a11b', 7 | output: '[a]{a}{a}(aaaaaa){b}(b)', 8 | }, 9 | { 10 | input: '20a', 11 | output: '{a}{a}', 12 | }, 13 | { 14 | input: '70b120a4c', 15 | output: '[b]{b}{b}[a][a]{a}{a}(cccc)', 16 | }, 17 | { 18 | input: '9c', 19 | output: '(ccccccccc)', 20 | }, 21 | { 22 | input: '19d51e', 23 | output: '{d}(ddddddddd)[e](e)', 24 | }, 25 | ]; 26 | 27 | it('should return a string type', () => { 28 | expect(typeof organizeGifts('3c')).toBe('string'); 29 | }); 30 | 31 | it.each(testCases)('should return the correct output', (testCase) => { 32 | expect(organizeGifts(testCase.input)).toBe(testCase.output); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /2023/09-alterna-las-luces/README.md: -------------------------------------------------------------------------------- 1 | # Reto 09: Alterna las luces 2 | 3 | ## Problema 4 | 5 | Están encendiendo las **luces de Navidad** 🎄 en la ciudad y, como cada año, ¡hay que arreglarlas! 6 | 7 | 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. 8 | 9 | Nos han pedido que escribamos una función adjustLights que, dado un array de strings con el color de cada luz (representados con los emojis 🔴 para el rojo y 🟢 para el verde), devuelva el **número mínimo** de luces que hay que cambiar para que estén los colores alternos. 10 | 11 | ```js 12 | adjustLights(['🟢', '🔴', '🟢', '🟢', '🟢']) 13 | // -> 1 (cambias la cuarta luz a 🔴) 14 | 15 | adjustLights(['🔴', '🔴', '🟢', '🟢', '🔴']) 16 | // -> 2 (cambias la segunda luz a 🟢 y la tercera a 🔴) 17 | 18 | adjustLights(['🟢', '🔴', '🟢', '🔴', '🟢']) 19 | // -> 0 (ya están alternadas) 20 | 21 | adjustLights(['🔴', '🔴', '🔴']) 22 | // -> 1 (cambias la segunda luz a 🟢) 23 | ``` 24 | 25 | ## Mi solución 26 | 27 | ```js 28 | const adjustLights = (lights) => { 29 | const patron = ['🟢', '🔴']; 30 | let changes = 0; 31 | const lightsLength = lights.length; 32 | for (let i = 0; i < lightsLength; i++) { 33 | changes += lights[i] === patron[i % 2]; 34 | } 35 | return Math.min(changes, lights.length - changes); 36 | }; 37 | ``` 38 | 39 | ## Explicación de mi solución 40 | 41 | 1. Se crea un array con el patrón de luces alternadas. 42 | 2. Se crea una variable para contar los cambios de luces. 43 | 3. Se recorre el array de luces y se compara cada luz con el patrón de luces alternadas. 44 | 4. Si la luz es igual al patrón, se suma 1 a la variable de cambios. 45 | 5. Se retorna el mínimo entre los cambios y la longitud del array menos los cambios. 46 | -------------------------------------------------------------------------------- /2023/09-alterna-las-luces/index.js: -------------------------------------------------------------------------------- 1 | const adjustLights = (lights) => { 2 | const patron = ['🟢', '🔴']; 3 | let changes = 0; 4 | const lightsLength = lights.length; 5 | for (let i = 0; i < lightsLength; i++) { 6 | changes += lights[i] === patron[i % 2]; 7 | } 8 | return Math.min(changes, lights.length - changes); 9 | }; 10 | 11 | module.exports = adjustLights; 12 | -------------------------------------------------------------------------------- /2023/09-alterna-las-luces/index.test.js: -------------------------------------------------------------------------------- 1 | const adjustLights = require('./index'); 2 | 3 | describe('09 => Alterna las luces', () => { 4 | const testCases = [ 5 | { 6 | input: ['🟢', '🔴', '🟢', '🟢', '🟢'], 7 | output: 1, 8 | }, 9 | { 10 | input: ['🔴', '🔴', '🟢', '🟢', '🔴'], 11 | output: 2, 12 | }, 13 | { 14 | input: ['🟢', '🔴', '🟢', '🔴', '🟢'], 15 | output: 0, 16 | }, 17 | { 18 | input: ['🔴', '🔴', '🔴'], 19 | output: 1, 20 | }, 21 | ]; 22 | 23 | it('should return number type', () => { 24 | expect(typeof adjustLights(['🟢', '🔴', '🟢', '🟢', '🟢'])).toBe('number'); 25 | }); 26 | 27 | it.each(testCases)('should return $output', (testCase) => { 28 | expect(adjustLights(testCase.input)).toBe(testCase.output); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /2023/10-crea-tu-propio-arbol-de-navidad/index.js: -------------------------------------------------------------------------------- 1 | const createChristmasTree = (ornaments, height) => { 2 | const heightSucessive = (height / 2) * (height + 1); 3 | const repeatOrnaments = [ 4 | ...ornaments.repeat((heightSucessive / ornaments.length) + 1), 5 | ].join(' '); 6 | const spaces = ' '.repeat(height - 1); 7 | 8 | let tree = ''; 9 | let i = 0; 10 | let counter = 0; 11 | while (counter < height) { 12 | const ornamentsLine = repeatOrnaments.substring(i, i + 2 * counter + 1); 13 | const level = `${spaces.substring(counter)}${ornamentsLine}\n`; 14 | tree += level; 15 | i += 2 * (counter + 1); 16 | counter++; 17 | } 18 | 19 | return `${tree}${' '.repeat(height - 1)}|\n`; 20 | }; 21 | 22 | module.exports = createChristmasTree; 23 | -------------------------------------------------------------------------------- /2023/10-crea-tu-propio-arbol-de-navidad/index.test.js: -------------------------------------------------------------------------------- 1 | const createChristmasTree = require('./index'); 2 | 3 | describe('10 => Crea tu propio arbol de navidad', () => { 4 | const testCases = [ 5 | { 6 | input: ['x', 3], 7 | output: ' x\n x x\nx x x\n |\n', 8 | }, 9 | { 10 | input: ['xo', 4], 11 | output: ' x\n o x\n o x o\nx o x o\n |\n', 12 | }, 13 | { 14 | input: ['123', 5], 15 | output: ' 1\n 2 3\n 1 2 3\n 1 2 3 1\n2 3 1 2 3\n |\n', 16 | }, 17 | { 18 | input: ['*@o', 3], 19 | output: ' *\n @ o\n* @ o\n |\n', 20 | }, 21 | ]; 22 | 23 | it('should return a string type', () => { 24 | expect(typeof createChristmasTree('x', 3)).toBe('string'); 25 | }); 26 | 27 | it.each(testCases)('should return the correct output', (testCase) => { 28 | expect(createChristmasTree(...testCase.input)).toBe(testCase.output); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /2023/11-los-elfos-estudiosos/README.md: -------------------------------------------------------------------------------- 1 | # Reto 11: Los elfos estudiosos 2 | 3 | ## Problema 4 | 5 | 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. 6 | 7 | **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. 8 | 9 | Crea una función `getIndexsForPalindrome` que reciba una cadena de caracteres y devolverá: 10 | 11 | - Si ya es un palíndromo, un array vacío. 12 | - Si no es posible, null. 13 | - Si se puede formar un palíndromo con un cambio, un array con las dos posiciones (índices) que se deben intercambiar para poder crearlo. 14 | 15 | Por ejemplo: 16 | 17 | ```js 18 | getIndexsForPalindrome('anna') // [] 19 | getIndexsForPalindrome('abab') // [0, 1] 20 | getIndexsForPalindrome('abac') // null 21 | getIndexsForPalindrome('aaaaaaaa') // [] 22 | getIndexsForPalindrome('aaababa') // [1, 3] 23 | getIndexsForPalindrome('caababa') // null`` 24 | ``` 25 | 26 | Si se puede formar el palíndromo con diferentes intercambios, **siempre se debe devolver el primero que se encuentre.** 27 | 28 | ## Mi solución 29 | 30 | ```js 31 | function getIndexsForPalindrome(word) { 32 | const wordLength = word.length; 33 | const isPalindrome = (str) => str === [...str].reverse().join(''); 34 | 35 | if (isPalindrome(word)) return []; 36 | 37 | for (let i = 0; i < wordLength; i++) { 38 | for (let j = i + 1; j < wordLength; j++) { 39 | const newWord = [...word]; 40 | [newWord[i], newWord[j]] = [newWord[j], newWord[i]]; 41 | 42 | if (newWord.join('') === newWord.reverse().join('')) { 43 | return [i, j]; 44 | } 45 | } 46 | } 47 | 48 | return null; 49 | } 50 | ``` 51 | -------------------------------------------------------------------------------- /2023/11-los-elfos-estudiosos/index.js: -------------------------------------------------------------------------------- 1 | function getIndexsForPalindrome(word) { 2 | const wordLength = word.length; 3 | const isPalindrome = (str) => str === [...str].reverse().join(''); 4 | 5 | if (isPalindrome(word)) return []; 6 | 7 | for (let i = 0; i < wordLength; i++) { 8 | for (let j = i + 1; j < wordLength; j++) { 9 | const newWord = [...word]; 10 | [newWord[i], newWord[j]] = [newWord[j], newWord[i]]; 11 | 12 | if (newWord.join('') === newWord.reverse().join('')) { 13 | return [i, j]; 14 | } 15 | } 16 | } 17 | 18 | return null; 19 | } 20 | 21 | module.exports = getIndexsForPalindrome; 22 | -------------------------------------------------------------------------------- /2023/11-los-elfos-estudiosos/index.test.js: -------------------------------------------------------------------------------- 1 | const getIndexsForPalindrome = require('./index'); 2 | 3 | describe('11 => Los elfos estudiosos', () => { 4 | const testCases = [ 5 | { 6 | input: 'anna', 7 | output: [], 8 | }, 9 | { 10 | input: 'abab', 11 | output: [0, 1], 12 | }, 13 | { 14 | input: 'abac', 15 | output: null, 16 | }, 17 | { 18 | input: 'aaaaaaaa', 19 | output: [], 20 | }, 21 | { 22 | input: 'aaababa', 23 | output: [1, 3], 24 | }, 25 | { 26 | input: 'caababa', 27 | output: null, 28 | }, 29 | ]; 30 | 31 | it('should return an array type if the input is a palindrome', () => { 32 | expect(getIndexsForPalindrome('abab')).toEqual([0, 1]); 33 | }); 34 | 35 | it('should return null type if the input could not be a palindrome', () => { 36 | expect(getIndexsForPalindrome('abac')).toBeNull(); 37 | }); 38 | 39 | it.each(testCases)('should return $output', ({ input, output }) => { 40 | expect(getIndexsForPalindrome(input)).toEqual(output); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /2023/12-es-una-copia-valida/index.js: -------------------------------------------------------------------------------- 1 | function checkIsValidCopy(original, copy) { 2 | let isValidCopy = true; 3 | const symbolSequence = '#+:. '; 4 | 5 | let copyIndex = 0; 6 | 7 | // eslint-disable-next-line no-restricted-syntax 8 | for (const letter of original) { 9 | const copyLetter = copy[copyIndex]; 10 | const symbolIndex = symbolSequence.indexOf(letter); 11 | 12 | const symbols = symbolIndex !== -1 13 | ? symbolSequence.slice(symbolIndex) 14 | : symbolSequence; 15 | 16 | const isValidLetter = `${letter}${letter.toLowerCase()}${symbols}` 17 | .includes(copyLetter); 18 | 19 | const isLetterBlankSpace = letter === ' '; 20 | const isCopyLetterBlankSpace = copyLetter === ' '; 21 | 22 | const isValidCharacter = isLetterBlankSpace 23 | ? isCopyLetterBlankSpace 24 | : isValidLetter; 25 | 26 | if (!isValidCharacter) { 27 | isValidCopy = false; 28 | break; 29 | } 30 | 31 | copyIndex++; 32 | } 33 | 34 | return isValidCopy; 35 | } 36 | 37 | module.exports = checkIsValidCopy; 38 | -------------------------------------------------------------------------------- /2023/12-es-una-copia-valida/index.test.js: -------------------------------------------------------------------------------- 1 | const checkIsValidCopy = require('./index'); 2 | 3 | describe('12 => Es una copia valida', () => { 4 | const testCases = [ 5 | { 6 | input: ['Santa Claus is coming', 'sa#ta Cl#us i+ comin#'], 7 | output: true, 8 | }, 9 | { 10 | input: ['s#nta Cla#s is coming', 'p#nt: cla#s #s c+min#'], 11 | output: false, 12 | }, 13 | { 14 | input: ['Santa Claus', 's#+:. c:. s'], 15 | output: true, 16 | }, 17 | { 18 | input: ['Santa Claus', 's#+:.#c:. s'], 19 | output: false, 20 | }, 21 | { 22 | input: ['s+#:.#c:. s', 's#+:.#c:. s'], 23 | output: false, 24 | }, 25 | ]; 26 | 27 | it('should return a boolean type', () => { 28 | expect(typeof checkIsValidCopy(...testCases[0].input)).toBe('boolean'); 29 | }); 30 | 31 | it.each(testCases)('should return $output', ({ input, output }) => { 32 | expect(checkIsValidCopy(...input)).toBe(output); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /2023/13-calculando-el-tiempo/README.md: -------------------------------------------------------------------------------- 1 | # Reto 13: Calculando el tiempo 2 | 3 | ## Problema 4 | 5 | Los elfos están preparando **la víspera de Navidad** y necesitan tu ayuda para calcular si van sobrados o no de tiempo ⏳. 6 | 7 | Para ello te pasan un array con la duración de cada entrega. El formato de la duración es HH:mm:ss, las entregas empiezan a las 00:00:00 y el límite de tiempo es 07:00:00. 8 | 9 | **Tu función debe devolver el tiempo que les faltará o el tiempo que les sobrará** para terminar las entregas. El formato de la duración devuelta debe ser HH:mm:ss. 10 | 11 | Si terminan antes de las 07:00:00, el tiempo restante hasta las 07:00:00 debe ser mostrado con un signo negativo. Por ejemplo, **si sobran 1 hora y 30 minutos, devuelve -01:30:00** 12 | 13 | ```js 14 | calculateTime(['00:10:00', '01:00:00', '03:30:00']) 15 | // '-02:20:00' 16 | 17 | calculateTime(['02:00:00', '05:00:00', '00:30:00']) 18 | // '00:30:00' 19 | 20 | calculateTime([ 21 | '00:45:00', 22 | '00:45:00', 23 | '00:00:30', 24 | '00:00:30' 25 | ]) // '-05:29:00' 26 | ``` 27 | 28 | ## Mi solución 29 | 30 | ```js 31 | function calculateTime(deliveries) { 32 | const deliveryLimit = 7 * 3600; // Límite de tiempo en segundos (7 horas) 33 | let totalSeconds = 0; 34 | 35 | // eslint-disable-next-line no-restricted-syntax 36 | for (const time of deliveries) { 37 | const [hours, minutes, seconds] = time.split(':').map(Number); 38 | totalSeconds += hours * 3600 + minutes * 60 + seconds; 39 | } 40 | 41 | const remainingSeconds = deliveryLimit - totalSeconds; 42 | const pad = (value) => (value < 10 ? `0${value}` : `${value}`); 43 | const sign = remainingSeconds <= 0 ? '' : '-'; 44 | const absRemainingSeconds = Math.abs(remainingSeconds); 45 | const resultHours = pad(Math.floor(absRemainingSeconds / 3600)); 46 | const resultMinutes = pad(Math.floor((absRemainingSeconds % 3600) / 60)); 47 | const resultSeconds = pad(absRemainingSeconds % 60); 48 | return `${sign}${resultHours}:${resultMinutes}` 49 | + `:${resultSeconds}`; 50 | } 51 | ``` 52 | -------------------------------------------------------------------------------- /2023/13-calculando-el-tiempo/index.js: -------------------------------------------------------------------------------- 1 | function calculateTime(deliveries) { 2 | const deliveryLimit = 7 * 3600; // Límite de tiempo en segundos (7 horas) 3 | let totalSeconds = 0; 4 | 5 | // eslint-disable-next-line no-restricted-syntax 6 | for (const time of deliveries) { 7 | const [hours, minutes, seconds] = time.split(':').map(Number); 8 | totalSeconds += hours * 3600 + minutes * 60 + seconds; 9 | } 10 | 11 | const remainingSeconds = deliveryLimit - totalSeconds; 12 | const pad = (value) => (value < 10 ? `0${value}` : `${value}`); 13 | const sign = remainingSeconds <= 0 ? '' : '-'; 14 | const absRemainingSeconds = Math.abs(remainingSeconds); 15 | const resultHours = pad(Math.floor(absRemainingSeconds / 3600)); 16 | const resultMinutes = pad(Math.floor((absRemainingSeconds % 3600) / 60)); 17 | const resultSeconds = pad(absRemainingSeconds % 60); 18 | return `${sign}${resultHours}:${resultMinutes}` 19 | + `:${resultSeconds}`; 20 | } 21 | 22 | module.exports = calculateTime; 23 | -------------------------------------------------------------------------------- /2023/13-calculando-el-tiempo/index.test.js: -------------------------------------------------------------------------------- 1 | const calculateTime = require('./index'); 2 | 3 | describe('13 => Calculando el tiempo', () => { 4 | const testCases = [ 5 | { 6 | input: ['00:10:00', '01:00:00', '03:30:00'], 7 | output: '-02:20:00', 8 | }, 9 | { 10 | input: ['02:00:00', '05:00:00', '00:30:00'], 11 | output: '00:30:00', 12 | }, 13 | { 14 | input: ['00:45:00', '00:45:00', '00:00:30', '00:00:30'], 15 | output: '-05:29:00', 16 | }, 17 | ]; 18 | 19 | it('should return a string type', () => { 20 | expect(typeof calculateTime(testCases[0].input)).toBe('string'); 21 | }); 22 | 23 | it.each(testCases)('should return $output', (testCase) => { 24 | expect(calculateTime(testCase.input)).toBe(testCase.output); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /2023/14-evita-la-alarma/README.md: -------------------------------------------------------------------------------- 1 | # Reto 14: Evita la alarma 2 | 3 | ## Problema 4 | 5 | Con el tema de las redes sociales, Santa Claus **tiene pánico que los niños se despierten mientras él está repartiendo regalos en sus casos,** usen el móvil para grabarlo y se haga viral en TikTok. 6 | 7 | Quiere evitarlo a toda costa. Cada casa en esa calle tiene un número de regalos preparados. Sin embargo, **las casas tienen un sistema de seguridad conectado entre casas adyacentes,** por lo que **no puede dejar los regalos en dos casas seguidas,** o se activará la alarma que alertará a los niños. 8 | 9 | Dada un **array de enteros no negativos regalos** que representa la cantidad de regalos en cada casa, tu tarea es ayudar a Papá Noel a determinar la **máxima cantidad de regalos que puede entregar** en una noche sin activar ninguna alarma. 10 | 11 | ```js 12 | maxGifts([2, 4, 2]) // 4 (4) 13 | maxGifts([5, 1, 1, 5]) // 10 (5 + 5) 14 | maxGifts([4, 1, 1, 4, 2, 1]) // 9 (4 + 4 + 1) 15 | maxGifts([1, 3, 1, 3, 100]) // 103 (3 + 100) 16 | ``` 17 | 18 | ## Mi solución 19 | 20 | ```js 21 | const maxGifts = (houses) => { 22 | let incl = 0; 23 | let excl = 0; 24 | 25 | // eslint-disable-next-line no-restricted-syntax 26 | for (const current of houses) { 27 | [incl, excl] = [excl + current, Math.max(incl, excl)]; 28 | } 29 | 30 | return Math.max(incl, excl); 31 | }; 32 | ``` 33 | -------------------------------------------------------------------------------- /2023/14-evita-la-alarma/index.js: -------------------------------------------------------------------------------- 1 | const maxGifts = (houses) => { 2 | let incl = 0; 3 | let excl = 0; 4 | 5 | // eslint-disable-next-line no-restricted-syntax 6 | for (const current of houses) { 7 | [incl, excl] = [excl + current, Math.max(incl, excl)]; 8 | } 9 | 10 | return Math.max(incl, excl); 11 | }; 12 | 13 | module.exports = maxGifts; 14 | -------------------------------------------------------------------------------- /2023/14-evita-la-alarma/index.test.js: -------------------------------------------------------------------------------- 1 | const maxGifts = require('./index'); 2 | 3 | describe('14 => Evita la alarma', () => { 4 | const testCases = [ 5 | { 6 | input: [2, 4, 2], 7 | output: 4, 8 | }, 9 | { 10 | input: [5, 1, 1, 5], 11 | output: 10, 12 | }, 13 | { 14 | input: [4, 1, 1, 4, 2, 1], 15 | output: 9, 16 | }, 17 | { 18 | input: [1, 3, 1, 3, 100], 19 | output: 103, 20 | }, 21 | ]; 22 | 23 | it('should return a number type', () => { 24 | expect(typeof maxGifts([1, 2, 3])).toBe('number'); 25 | }); 26 | 27 | it.each(testCases)('should return $output', ({ input, output }) => { 28 | expect(maxGifts(input)).toBe(output); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /2023/15-robot-autonomo/README.md: -------------------------------------------------------------------------------- 1 | # Reto 15: Robot autonomo 2 | 3 | ## Problema 4 | 5 | Estamos programando unos **robots** llamados giftbot 🤖🎁 que navegan de forma autónoma por los almacenes de regalos. 6 | 7 | Estamos creando una función a la que le pasamos: el almacén 🏬 que deben navegar y los movimientos ↔️ que pueden realizar. 8 | 9 | El almacén se representa como un **array de cadenas de texto,** donde: 10 | 11 | - `.` significa que hay vía libre. 12 | - `*` significa que hay un obstáculo. 13 | - `!` es la posición inicial del robot. 14 | 15 | Los movimientos son un **array de cadenas de texto,** donde: 16 | 17 | - `R` mueve al robot una posición a la derecha. 18 | - `L` mueve al robot una posición a la izquierda. 19 | - `U` mueve al robot una posición hacia arriba. 20 | - `D` mueve al robot una posición hacia abajo. 21 | 22 | Hay que tener en cuenta que **el robot no puede superar los obstáculos ni los límites del almacén.** 23 | 24 | Dados un almacén y los movimientos, debemos devolver el array con la posición final de nuestro robot. 25 | 26 | ```js 27 | const store = ['..!....', '...*.*.'] 28 | 29 | const movements = ['R', 'R', 'D', 'L'] 30 | const result = autonomousDrive(store, movements) 31 | console.log(result) 32 | /* 33 | [ 34 | ".......", 35 | "...*!*." 36 | ] 37 | */ 38 | 39 | // El último movimiento es hacia la izquierda, pero no puede moverse porque hay un obstáculo. 40 | ``` 41 | 42 | Ten en cuenta que la store es **un array que puede ser de un número de filas que va de 1 a 100,** ya que tenemos almacenes de todos los tamaños. 43 | 44 | También que el robot **es posible que termine en su posición inicial** si no puede moverse o si está dando vueltas. 45 | 46 | ## Mi solución 47 | 48 | ```js 49 | function autonomousDrive(store, movements) { 50 | let currentRow = store.findIndex((line) => line.includes('!')); 51 | let currentCol = store[currentRow].indexOf('!'); 52 | store[currentRow] = store[currentRow].replace('!', '.'); 53 | 54 | // eslint-disable-next-line no-restricted-syntax 55 | for (const movement of movements) { 56 | const di = +(movement === 'D') - +(movement === 'U'); 57 | const dj = +(movement === 'R') - +(movement === 'L'); 58 | currentRow += +(store[currentRow + di]?.[currentCol] === '.' && di); 59 | currentCol += +(store[currentRow][currentCol + dj] === '.' && dj); 60 | } 61 | 62 | const currentLine = store[currentRow]; 63 | store[currentRow] = `${currentLine.substring(0, currentCol)}!${ 64 | currentLine.substring(currentCol + 1)}`; 65 | 66 | return store; 67 | } 68 | ``` 69 | -------------------------------------------------------------------------------- /2023/15-robot-autonomo/index.js: -------------------------------------------------------------------------------- 1 | function autonomousDrive(store, movements) { 2 | let currentRow = store.findIndex((line) => line.includes('!')); 3 | let currentCol = store[currentRow].indexOf('!'); 4 | store[currentRow] = store[currentRow].replace('!', '.'); 5 | 6 | // eslint-disable-next-line no-restricted-syntax 7 | for (const movement of movements) { 8 | const di = +(movement === 'D') - +(movement === 'U'); 9 | const dj = +(movement === 'R') - +(movement === 'L'); 10 | currentRow += +(store[currentRow + di]?.[currentCol] === '.' && di); 11 | currentCol += +(store[currentRow][currentCol + dj] === '.' && dj); 12 | } 13 | 14 | const currentLine = store[currentRow]; 15 | store[currentRow] = `${currentLine.substring(0, currentCol)}!${ 16 | currentLine.substring(currentCol + 1)}`; 17 | 18 | return store; 19 | } 20 | 21 | module.exports = autonomousDrive; 22 | -------------------------------------------------------------------------------- /2023/15-robot-autonomo/index.test.js: -------------------------------------------------------------------------------- 1 | const autonomousDrive = require('./index'); 2 | 3 | describe('15 => Robot autonomo', () => { 4 | const testCases = [ 5 | { 6 | input: [ 7 | ['..!....'], ['R', 'L'], 8 | ], 9 | output: ['..!....'], 10 | }, 11 | { 12 | input: [ 13 | ['!..', '***'], ['U', 'L'], 14 | ], 15 | output: [ 16 | '!..', 17 | '***', 18 | ], 19 | }, 20 | { 21 | input: [ 22 | [ 23 | '..!....', 24 | '......*', 25 | ], 26 | ['R', 'D', 'L'], 27 | ], 28 | output: [ 29 | '.......', 30 | '..!...*', 31 | ], 32 | }, 33 | { 34 | input: [ 35 | [ 36 | '*..!..*', 37 | '*.....*', 38 | ], 39 | ['R', 'R', 'R', 'D', 'D'], 40 | ], 41 | output: [ 42 | '*.....*', 43 | '*....!*', 44 | ], 45 | }, 46 | { 47 | input: [ 48 | ['***', '.!.', '***'], ['D', 'U', 'R', 'R', 'R'], 49 | ], 50 | output: [ 51 | '***', 52 | '..!', 53 | '***', 54 | ], 55 | }, 56 | { 57 | input: [ 58 | ['***', '*!*', '***'], ['D', 'U', 'R', 'L'], 59 | ], 60 | output: [ 61 | '***', 62 | '*!*', 63 | '***', 64 | ], 65 | }, 66 | { 67 | input: [ 68 | [ 69 | '.**.*.*.', 70 | '.***....', 71 | '..!.....', 72 | ], ['D', 'U', 'R', 'R', 'R'], 73 | ], 74 | output: 75 | [ 76 | '.**.*.*.', 77 | '.***....', 78 | '.....!..', 79 | ], 80 | 81 | }, 82 | ]; 83 | 84 | it('should return an array type', () => { 85 | expect(Array.isArray(autonomousDrive(...testCases[0].input))).toBe(true); 86 | }); 87 | 88 | it.each(testCases)('should return the correct output', (testCase) => { 89 | expect(autonomousDrive(...testCase.input)).toEqual(testCase.output); 90 | }); 91 | }); 92 | -------------------------------------------------------------------------------- /2023/16-despliegue-en-viernes/README.md: -------------------------------------------------------------------------------- 1 | # Reto 16: Despliegue en viernes 2 | 3 | ## Problema 4 | 5 | Ayer viernes alguien hizo despliegue a producción y se rompió la aplicación de montaje de árboles de Navidad. Nos han pedido que lo arreglemos lo antes posible. 6 | 7 | El problema es que el formato de los árboles ha cambiado. **Es un array de números… ¡pero debería ser un objeto!** Por ejemplo el árbol: `[3, 1, 0, 8, 12, null, 1]` se ve así: 8 | 9 | ```js 10 | // 3 11 | // / \ 12 | // 1 0 13 | // / \ \ 14 | // 8 12 1 15 | ``` 16 | 17 | Lo que necesitamos es transformar el array en un objeto donde cada nodo del árbol tiene las propiedades value, left y right. 18 | 19 | Por ejemplo, al ejecutar tu función `transformTree` con `[3, 1, 0, 8, 12, null, 1]` debería devolver esto: 20 | 21 | ```js 22 | { 23 | value: 3, 24 | left: { 25 | value: 1, 26 | left: { 27 | value: 8, 28 | left: null, 29 | right: null 30 | }, 31 | right: { 32 | value: 12, 33 | left: null, 34 | right: null 35 | } 36 | }, 37 | right: { 38 | value: 0, 39 | left: null, 40 | right: { 41 | value: 1, 42 | left: null, 43 | right: null 44 | } 45 | } 46 | } 47 | ``` 48 | 49 | El elfo que está de guardia y que intentó solucionar el problema antes de irse a casa, nos ha dejado algunas pistas: 50 | 51 | - Si un nodo no tiene valor, se representa con null. Por lo tanto, si un nodo tiene valor null, no tendrá hijos. 52 | - El nodo raíz se encuentra en el índice 0 del array. 53 | - Existe una relación entre el índice de un nodo y el índice de sus hijos. ¡Busca el patrón! 54 | 55 | ## Mi solución 56 | 57 | ```js 58 | function transformTree(tree) { 59 | // eslint-disable-next-line prefer-rest-params 60 | const index = arguments[1] ?? 0; 61 | return tree[index] == null 62 | ? null 63 | : { 64 | value: tree[index], 65 | left: transformTree(tree, 2 * index + 1), 66 | right: transformTree(tree, 2 * index + 2), 67 | }; 68 | } 69 | ``` 70 | -------------------------------------------------------------------------------- /2023/16-despliegue-en-viernes/index.js: -------------------------------------------------------------------------------- 1 | function transformTree(tree) { 2 | // eslint-disable-next-line prefer-rest-params 3 | const index = arguments[1] ?? 0; 4 | return tree[index] == null 5 | ? null 6 | : { 7 | value: tree[index], 8 | left: transformTree(tree, 2 * index + 1), 9 | right: transformTree(tree, 2 * index + 2), 10 | }; 11 | } 12 | 13 | module.exports = transformTree; 14 | -------------------------------------------------------------------------------- /2023/17-optimizando-el-alquiler/README.md: -------------------------------------------------------------------------------- 1 | # Reto 17: Optimizando el alquiler 2 | 3 | ## Problema 4 | 5 | En Rovaniemi, Finlandia 🇫🇮, los trineos 🛷 se alquilan por intervalos de tiempo. **Cada intervalo se representa como un array de dos elementos,** donde el primer elemento es el inicio del alquiler y el segundo es el final. 6 | 7 | Por ejemplo, el array [2, 7] representa un alquiler que comienza en la hora 2 y termina en la hora 7. El problema es que a veces los intervalos se superponen entre sí, haciendo que sea un lío entender de qué hora a qué hora se alquiló el trineo. 8 | 9 | Nos piden que, para simplificar la tarea de calcular el tiempo total de alquiler, **escribamos una función que fusione todos los intervalos superpuestos y devolver un array de intervalos ordenados:** 10 | 11 | ```js 12 | optimizeIntervals([ 13 | [5, 8], 14 | [2, 7], 15 | [3, 4] 16 | ]) // [[2, 8]] 17 | 18 | optimizeIntervals([ 19 | [1, 3], 20 | [8, 10], 21 | [2, 6] 22 | ]) // [[1, 6], [8, 10]] 23 | 24 | optimizeIntervals([ 25 | [3, 4], 26 | [1, 2], 27 | [5, 6] 28 | ]) // [[1, 2], [3, 4], [5, 6]] 29 | ``` 30 | 31 | Puedes asumir que **el primer elemento de cada intervalo siempre es menor o igual que el segundo elemento. Pero los intervalos no están necesariamente ordenados.** 32 | 33 | Los números de horas pueden llegar hasta la cifra 9999. 34 | 35 | ## Mi solución 36 | 37 | ```js 38 | const optimizeIntervals = (intervals) => { 39 | const result = [ 40 | intervals.sort((a, b) => a[0] - b[0])[0], 41 | ]; 42 | 43 | // eslint-disable-next-line no-restricted-syntax 44 | for (const val of intervals) { 45 | const [start, end] = val; 46 | const max = result[result.length - 1][1]; 47 | 48 | start > max 49 | ? result.push(val) 50 | : (result[result.length - 1][1] = Math.max(end, max)); 51 | } 52 | 53 | return result; 54 | }; 55 | ``` 56 | -------------------------------------------------------------------------------- /2023/17-optimizando-el-alquiler/index.js: -------------------------------------------------------------------------------- 1 | const optimizeIntervals = (intervals) => { 2 | const result = [ 3 | intervals.sort((a, b) => a[0] - b[0])[0], 4 | ]; 5 | 6 | // eslint-disable-next-line no-restricted-syntax 7 | for (const val of intervals) { 8 | const [start, end] = val; 9 | const max = result[result.length - 1][1]; 10 | 11 | start > max 12 | ? result.push(val) 13 | : (result[result.length - 1][1] = Math.max(end, max)); 14 | } 15 | 16 | return result; 17 | }; 18 | 19 | module.exports = optimizeIntervals; 20 | -------------------------------------------------------------------------------- /2023/17-optimizando-el-alquiler/index.test.js: -------------------------------------------------------------------------------- 1 | const optimizeIntervals = require('./index'); 2 | 3 | describe('17 => Optimizando el alquiler', () => { 4 | const testCases = [ 5 | { 6 | input: [ 7 | [5, 8], 8 | [2, 7], 9 | [3, 4], 10 | ], 11 | output: [[2, 8]], 12 | }, 13 | { 14 | input: [ 15 | [1, 3], 16 | [8, 10], 17 | [2, 6], 18 | ], 19 | output: [[1, 6], [8, 10]], 20 | }, 21 | { 22 | input: [ 23 | [3, 4], 24 | [1, 2], 25 | [5, 6], 26 | ], 27 | output: [[1, 2], [3, 4], [5, 6]], 28 | }, 29 | ]; 30 | 31 | it('should return an array type', () => { 32 | expect(Array.isArray(optimizeIntervals([...testCases[0].input]))).toBe(true); 33 | }); 34 | 35 | it.each(testCases)('should return the correct output', (testCase) => { 36 | expect(optimizeIntervals([...testCase.input])).toEqual(testCase.output); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /2023/18-el-reloj-digital/index.js: -------------------------------------------------------------------------------- 1 | function drawClock(time) { 2 | const digitPatterns = { 3 | 0: ['***', '* *', '* *', '* *', '* *', '* *', '***'], 4 | 1: [' *', ' *', ' *', ' *', ' *', ' *', ' *'], 5 | 2: ['***', ' *', ' *', '***', '* ', '* ', '***'], 6 | 3: ['***', ' *', ' *', '***', ' *', ' *', '***'], 7 | 4: ['* *', '* *', '* *', '***', ' *', ' *', ' *'], 8 | 5: ['***', '* ', '* ', '***', ' *', ' *', '***'], 9 | 6: ['***', '* ', '* ', '***', '* *', '* *', '***'], 10 | 7: ['***', ' *', ' *', ' *', ' *', ' *', ' *'], 11 | 8: ['***', '* *', '* *', '***', '* *', '* *', '***'], 12 | 9: ['***', '* *', '* *', '***', ' *', ' *', '***'], 13 | ':': [' ', ' ', '*', ' ', '*', ' ', ' '], 14 | }; 15 | 16 | const firstDigitPattern = digitPatterns[time[0]]; 17 | const secondDigitPattern = digitPatterns[time[1]]; 18 | const colonPattern = digitPatterns[':']; 19 | const thirdDigitPattern = digitPatterns[time[3]]; 20 | const fourthDigitPattern = digitPatterns[time[4]]; 21 | 22 | const result = [...firstDigitPattern]; 23 | let position = 0; 24 | 25 | // eslint-disable-next-line no-restricted-syntax 26 | for (const row of result) { 27 | const rowString = `${row} ${secondDigitPattern[position]} ` 28 | + `${colonPattern[position]} ${thirdDigitPattern[position]} ` 29 | + `${fourthDigitPattern[position]}`; 30 | result[position] = [...rowString]; 31 | position++; 32 | } 33 | 34 | return result; 35 | } 36 | 37 | module.exports = drawClock; 38 | -------------------------------------------------------------------------------- /2023/19-enfrenta-el-sabotaje/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-syntax */ 2 | const revealSabotage = (store) => { 3 | for (const [i, row] of store.entries()) { 4 | for (const [j, col] of row.entries()) { 5 | // Si la celda actual contiene una mina, continuamos al siguiente paso 6 | if (col !== '*') { 7 | // Obtener las filas adyacentes 8 | const nextRow = store[i + 1]; 9 | const prevRow = store[i - 1]; 10 | 11 | // Obtener las celdas adyacentes 12 | const adjacentCells = [ 13 | prevRow?.[j - 1], prevRow?.[j], prevRow?.[j + 1], 14 | row[j - 1], row[j + 1], 15 | nextRow?.[j - 1], nextRow?.[j], nextRow?.[j + 1], 16 | ]; 17 | 18 | // Contar las minas adyacentes 19 | const count = adjacentCells.reduce( 20 | (acc, curr) => acc + +(curr === '*'), 21 | 0, 22 | ); 23 | 24 | // Si hay minas adyacentes, actualizamos el valor de la celda actual 25 | if (count !== 0) { 26 | row[j] = `${count}`; 27 | } 28 | } 29 | } 30 | } 31 | 32 | return store; 33 | }; 34 | 35 | module.exports = revealSabotage; 36 | -------------------------------------------------------------------------------- /2023/20-distribuye-el-peso/index.js: -------------------------------------------------------------------------------- 1 | const distributeGifts = (weights) => { 2 | const numRows = weights.length; 3 | const numCols = weights[0].length; 4 | 5 | const averages = []; 6 | 7 | for (let i = 0; i < numRows; i++) { 8 | averages[i] = []; 9 | 10 | for (let j = 0; j < numCols; j++) { 11 | let sum = 0; 12 | let count = 0; 13 | 14 | const addToSum = (value) => { 15 | sum += value ?? 0; 16 | count += value ? 1 : 0; 17 | }; 18 | 19 | addToSum(weights[i][j - 1]); // left 20 | addToSum(weights[i][j + 1]); // right 21 | addToSum(weights[i - 1]?.[j]); // top 22 | addToSum(weights[i + 1]?.[j]); // bottom 23 | addToSum(weights[i][j]); // current 24 | 25 | averages[i][j] = Math.round(sum / count); 26 | } 27 | } 28 | 29 | return averages; 30 | }; 31 | 32 | module.exports = distributeGifts; 33 | -------------------------------------------------------------------------------- /2023/20-distribuye-el-peso/index.test.js: -------------------------------------------------------------------------------- 1 | const distributeGifts = require('./index'); 2 | 3 | describe('20 => Distribuye el peso', () => { 4 | const testCases = [ 5 | { 6 | input: [ 7 | [4, 5, 1], 8 | [6, null, 3], 9 | [8, null, 4], 10 | ], 11 | output: [ 12 | [5, 3, 3], 13 | [6, 5, 3], 14 | [7, 6, 4], 15 | ], 16 | }, 17 | { 18 | input: [ 19 | [2, null], 20 | [null, 3], 21 | ], 22 | output: [ 23 | [2, 3], 24 | [3, 3], 25 | ], 26 | }, 27 | { 28 | input: [ 29 | [2, 1, 1], 30 | [3, 4, null], 31 | ], 32 | output: [ 33 | [2, 2, 1], 34 | [3, 3, 3], 35 | ], 36 | }, 37 | { 38 | input: [ 39 | [null, 5], 40 | [3, null], 41 | ], 42 | output: [ 43 | [4, 5], 44 | [3, 4], 45 | ], 46 | }, 47 | { 48 | input: [ 49 | [1, 2, 3], 50 | [4, 5, 6], 51 | [7, 8, 9], 52 | ], 53 | output: [ 54 | [2, 3, 4], 55 | [4, 5, 6], 56 | [6, 7, 8], 57 | ], 58 | }, 59 | { 60 | input: [ 61 | [null, 1, null, 1, null], 62 | [1, null, 1, null, 1], 63 | ], 64 | output: [ 65 | [1, 1, 1, 1, 1], 66 | [1, 1, 1, 1, 1], 67 | ], 68 | }, 69 | ]; 70 | 71 | it('should return an array', () => { 72 | expect(Array.isArray(distributeGifts(...testCases[0].input))).toBe(true); 73 | }); 74 | 75 | it.each(testCases)('should return the correct output', (testCase) => { 76 | expect(distributeGifts(testCase.input)).toStrictEqual(testCase.output); 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /2023/21-mensaje-binario/README.md: -------------------------------------------------------------------------------- 1 | # Reto 21: Mensaje binario 2 | 3 | ## Problema 4 | 5 | Los elfos están recibiendo mensajes binarios extraños desde Marte 🪐. ¿Los extraterrestres están tratando de comunicarse con ellos? 👽 6 | 7 | **El mensaje que llega es un array de 0s y 1s.** Parece que han encontrado un patrón… Para asegurarse, quieren **encontrar el segmento más largo de la cadena donde el número de 0s y 1s sea igual.** 8 | 9 | ```js 10 | findBalancedSegment([1, 1, 0, 1, 1, 0, 1, 1]) 11 | // |________| 12 | // posición del segmento: [2, 5] 13 | // más largo equilibrado 14 | // de 0s y 1s 15 | 16 | findBalancedSegment([1, 1, 0]) 17 | // |__| 18 | // [1, 2] 19 | 20 | findBalancedSegment([1, 1, 1]) 21 | // no hay segmentos equilibrados: [] 22 | ``` 23 | 24 | Ten en cuenta que si hay más de un patrón equilibrado, **debes devolver el más largo y el primero que encuentres de izquierda a derecha.** 25 | 26 | Dicen que si encuentran el patrón, podrán enviar un mensaje de vuelta a Marte 🚀. Parece ser que tienen que enviarlos a https://mars.codes. 27 | 28 | ## Mi solución 29 | 30 | ```js 31 | const findBalancedSegment = (message) => { 32 | let maxLength = 0; 33 | let startIdx = -1; 34 | 35 | for (let i = 0; i < message.length; i++) { 36 | let count0 = 0; 37 | let count1 = 0; 38 | 39 | for (let j = i; j < message.length; j++) { 40 | message[j] === 0 ? count0++ : count1++; 41 | 42 | if (count0 === count1 && j - i + 1 > maxLength) { 43 | maxLength = j - i + 1; 44 | startIdx = i; 45 | } 46 | } 47 | } 48 | 49 | return startIdx !== -1 ? [startIdx, startIdx + maxLength - 1] : []; 50 | }; 51 | ``` 52 | -------------------------------------------------------------------------------- /2023/21-mensaje-binario/index.js: -------------------------------------------------------------------------------- 1 | const findBalancedSegment = (message) => { 2 | let maxLength = 0; 3 | let startIdx = -1; 4 | 5 | for (let i = 0; i < message.length; i++) { 6 | let count0 = 0; 7 | let count1 = 0; 8 | 9 | for (let j = i; j < message.length; j++) { 10 | message[j] === 0 ? count0++ : count1++; 11 | 12 | if (count0 === count1 && j - i + 1 > maxLength) { 13 | maxLength = j - i + 1; 14 | startIdx = i; 15 | } 16 | } 17 | } 18 | 19 | return startIdx !== -1 ? [startIdx, startIdx + maxLength - 1] : []; 20 | }; 21 | 22 | module.exports = findBalancedSegment; 23 | -------------------------------------------------------------------------------- /2023/21-mensaje-binario/index.test.js: -------------------------------------------------------------------------------- 1 | const findBalancedSegment = require('./index'); 2 | 3 | describe('21 => Mensaje binario', () => { 4 | const testCases = [ 5 | { 6 | input: [1, 1, 0, 1, 1, 0, 1, 1], 7 | output: [2, 5], 8 | }, 9 | { 10 | input: [1, 1, 0], 11 | output: [1, 2], 12 | }, 13 | { 14 | input: [1, 1, 1], 15 | output: [], 16 | }, 17 | { 18 | input: [1, 0, 1], 19 | output: [0, 1], 20 | }, 21 | { 22 | input: [1, 0, 1, 0], 23 | output: [0, 3], 24 | }, 25 | { 26 | input: [1, 1, 0, 1, 0, 1], 27 | output: [1, 4], 28 | }, 29 | { 30 | input: [1, 0, 0, 0, 1, 1, 1, 0, 0, 0], 31 | output: [0, 7], 32 | }, 33 | { 34 | input: [0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1], 35 | output: [5, 10], 36 | }, 37 | ]; 38 | 39 | it('should return an array type', () => { 40 | expect(Array.isArray(findBalancedSegment(testCases[0].input))).toBe(true); 41 | }); 42 | 43 | it.each(testCases)('should return $output', ({ input, output }) => { 44 | expect(findBalancedSegment(input)).toEqual(output); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /2023/22-lenguaje-de-programacion/README.md: -------------------------------------------------------------------------------- 1 | # Reto 22: Lenguaje de programacion 2 | 3 | ## Problema 4 | 5 | En la fábrica de juguetes de Santa, **los elfos están desarrollando un lenguaje de programación llamado Santa.js** 👨‍💻👩‍💻 basado en símbolos para controlar sus máquinas de juguetes 🚂. 6 | 7 | Han creado un sistema de instrucciones simple y necesitan tu ayuda para construir un **compilador que interprete estos símbolos.** 8 | 9 | El compilador trabaja con un contador que inicialmente tiene un valor de 0. **Las instrucciones modificarán el valor de este contador.** 10 | 11 | Instrucciones del lenguaje de los elfos en base a símbolos: 12 | 13 | - +: Incrementa en 1 el valor del contador. 14 | - *: Multiplica por 2 el valor del contador. 15 | - -: Resta 1 al valor del contador. 16 | - %: Marca un punto de retorno. No modifica el contador. 17 | - <: Vuelve atrás una vez a la última instrucción con el símbolo % que haya visto. Si no hay un % previo, no hace nada. 18 | - ¿: Inicia un bloque condicional que se ejecuta si el contador es mayor a 0. 19 | - ?: Finaliza un bloque condicional. 20 | 21 | Crea una función compile que reciba un string con las instrucciones del lenguaje y devuelve el resultado de ejecutarlas. Aquí tienes algunos ejemplos: 22 | 23 | ```js 24 | compile('++*-') // 3 25 | // (1 + 1) * 2 - 1 = 3 26 | 27 | compile('++%++<') // 6 28 | // 1 + 1 + 1 + 1 + 1 + 1 = 6 29 | 30 | compile('++<--') // 0 31 | // 1 + 1 - 1 - 1 = 0 32 | 33 | compile('++¿+?') // 3 34 | // 1 + 1 + 1 = 3 35 | 36 | compile('--¿+++?') // -2 37 | // - 1 - 1 = -2 38 | ``` 39 | 40 | ## Mi solución 41 | 42 | ```js 43 | const compile = (code) => { 44 | let counter = 0; 45 | let i = 0; 46 | let stack = -1; 47 | 48 | const instructions = { 49 | '+': () => counter++, 50 | '-': () => counter--, 51 | '*': () => counter *= 2, 52 | '%': () => stack = i, 53 | '<': () => { 54 | if (stack !== -1) { 55 | i = stack; 56 | stack = -1; 57 | } 58 | }, 59 | '¿': () => { 60 | if (counter <= 0) i = code.indexOf('?', i); 61 | }, 62 | '?': () => {}, 63 | }; 64 | 65 | while (i < code.length) { 66 | const instruction = code[i]; 67 | instructions[instruction](); 68 | i++; 69 | } 70 | 71 | return counter; 72 | }; 73 | ``` 74 | -------------------------------------------------------------------------------- /2023/22-lenguaje-de-programacion/index.js: -------------------------------------------------------------------------------- 1 | const compile = (code) => { 2 | let counter = 0; 3 | let i = 0; 4 | let stack = -1; 5 | 6 | const instructions = { 7 | '+': () => counter++, 8 | '-': () => counter--, 9 | '*': () => counter *= 2, 10 | '%': () => stack = i, 11 | '<': () => { 12 | if (stack !== -1) { 13 | i = stack; 14 | stack = -1; 15 | } 16 | }, 17 | '¿': () => { 18 | if (counter <= 0) i = code.indexOf('?', i); 19 | }, 20 | '?': () => {}, 21 | }; 22 | 23 | while (i < code.length) { 24 | const instruction = code[i]; 25 | instructions[instruction](); 26 | i++; 27 | } 28 | 29 | return counter; 30 | }; 31 | 32 | module.exports = compile; 33 | -------------------------------------------------------------------------------- /2023/22-lenguaje-de-programacion/index.test.js: -------------------------------------------------------------------------------- 1 | const compile = require('./index'); 2 | 3 | describe('22 => Lenguaje de programacion', () => { 4 | const testCases = [ 5 | { 6 | input: '++*-', 7 | output: 3, 8 | }, 9 | { 10 | input: '++%++<', 11 | output: 6, 12 | }, 13 | { 14 | input: '++<--', 15 | output: 0, 16 | }, 17 | { 18 | input: '++¿+?', 19 | output: 3, 20 | }, 21 | { 22 | input: '--¿+++?', 23 | output: -2, 24 | }, 25 | ]; 26 | 27 | it('should return a number type', () => { 28 | expect(typeof compile(testCases[0].input)).toBe('number'); 29 | }); 30 | 31 | it.each(testCases)('should return $output when input is $input', ({ input, output }) => { 32 | expect(compile(input)).toBe(output); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /2023/23-la-comida-de-navidad/README.md: -------------------------------------------------------------------------------- 1 | # Reto 23: La comida de navidad 2 | 3 | ## Problema 4 | 5 | ¡Santa 🎅 está organizando una gran cena navideña 🫓 y quiere asegurarse de que todos los platos sean únicos y variados! 6 | 7 | Te da una lista de platos navideños donde cada elemento consiste en una lista de strings que comienza con el nombre del plato, seguido de todos los ingredientes utilizados para su preparación. 8 | 9 | Tienes que escribir **una función que agrupe los platos por ingredientes siempre que haya al menos 2 platos que los contengan.** 10 | 11 | Así que devolvemos un array de arrays donde la primera posición es el nombre del ingrediente y el resto los nombres de los platos. 12 | 13 | Tanto la lista de ingredientes como los platos deben estar **ordenados alfabéticamente.** 14 | 15 | ```js 16 | const dishes = [ 17 | ["christmas turkey", "turkey", "sauce", "herbs"], 18 | ["cake", "flour", "sugar", "egg"], 19 | ["hot chocolate", "chocolate", "milk", "sugar"], 20 | ["pizza", "sauce", "tomato", "cheese", "ham"], 21 | ] 22 | 23 | organizeChristmasDinner(dishes) 24 | 25 | /* 26 | 27 | "sauce" está en 2 platos: "christmas turkey" y "pizza". 28 | "sugar" está en 2 platos: "cake" y "hot chocolate". 29 | El resto de ingredientes solo aparecen en un plato, por lo que no los mostramos. 30 | 31 | Enseñamos primero "sauce" porque alfabéticamente está antes que "sugar". 32 | Y los platos de cada ingrediente también están ordenados alfabéticamente. 33 | 34 | [ 35 | ["sauce", "christmas turkey", "pizza"], 36 | ["sugar", "cake", "hot chocolate"] 37 | ] 38 | */ 39 | ``` 40 | 41 | Ten en cuenta que: 42 | 43 | - Todos los nombres de los platos son diferentes. 44 | - Los nombres de los ingredientes para un plato dado son distintos entre sí. 45 | - Si no hay ingredientes repetidos, devolvemos un array vacío. 46 | 47 | ## Mi solución 48 | 49 | ```js 50 | function organizeChristmasDinner(dishes) { 51 | const ingredients = {}; 52 | 53 | for (const [dishName, ...dishIngredients] of dishes) { 54 | for (const ingredient of dishIngredients) { 55 | ingredients[ingredient] = [ 56 | ...(ingredients[ingredient] ?? []), 57 | dishName, 58 | ]; 59 | } 60 | } 61 | 62 | const output = Object.entries(ingredients) 63 | .filter(([, dishList]) => dishList.length > 1) 64 | .map(([ingredient, dishList]) => [ingredient, ...dishList.sort()]) 65 | .sort(); 66 | 67 | return output; 68 | } 69 | ``` 70 | -------------------------------------------------------------------------------- /2023/23-la-comida-de-navidad/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-syntax */ 2 | function organizeChristmasDinner(dishes) { 3 | const ingredients = {}; 4 | 5 | for (const [dishName, ...dishIngredients] of dishes) { 6 | for (const ingredient of dishIngredients) { 7 | ingredients[ingredient] = [ 8 | ...(ingredients[ingredient] ?? []), 9 | dishName, 10 | ]; 11 | } 12 | } 13 | 14 | const output = Object.entries(ingredients) 15 | .filter(([, dishList]) => dishList.length > 1) 16 | .map(([ingredient, dishList]) => [ingredient, ...dishList.sort()]) 17 | .sort(); 18 | 19 | return output; 20 | } 21 | 22 | module.exports = organizeChristmasDinner; 23 | -------------------------------------------------------------------------------- /2023/24-brincos-en-la-escalera/README.md: -------------------------------------------------------------------------------- 1 | # Reto 24: Brincos en la escalera 2 | 3 | ## Problema 4 | 5 | En la aldea de Santa, hay una escalera mágica que lleva a la fábrica de juguetes 🧸. Los elfos, siempre buscando hacer ejercicio y divertirse 🏃‍♂️, deciden saltar los peldaños de la escalera. 6 | 7 | Nos dan steps como el número de peldaños de la escalera y el número máximo de peldaños maxJump que un elfo puede saltar en un solo salto. 8 | 9 | Tu tarea es ayudar a los elfos a encontrar **todas las posibles secuencias de saltos que pueden hacer para subir la escalera, ordenadas de menos a más.** Teniendo en cuenta que los elfos pueden saltar como máximo maxJump peldaños en un solo salto (pero pueden saltar menos peldaños si así lo desean). 10 | 11 | Por ejemplo, si hay una escalera de steps = 4 y maxJump = 2 es el número máximo de peldaños que un elfo puede saltar en un solo salto, entonces hay cinco secuencias de saltos posibles: 12 | 13 | - [1, 1, 1, 1] (salta 1 peldaño 4 veces) 14 | - [1, 1, 2] (salta 1 peldaño 2 veces y luego 2 peldaños) 15 | - [1, 2, 1] (salta 1 peldaño, luego 2 peldaños y luego 1 peldaño) 16 | - [2, 1, 1] (salta 2 peldaños, luego 1 peldaño y luego 1 peldaño) 17 | - [2, 2] (salta 2 peldaños 2 veces) 18 | 19 | Más ejemplos: 20 | 21 | ```js 22 | getStaircasePaths(2, 1) // [[1, 1]] 23 | getStaircasePaths(3, 3) // [[1, 1, 1], [1, 2], [2, 1], [3]] 24 | getStaircasePaths(5, 1) // [[1, 1, 1, 1, 1]] 25 | getStaircasePaths(5, 2) 26 | /* 27 | [ 28 | [1, 1, 1, 1, 1], 29 | [1, 1, 1, 2], 30 | [1, 1, 2, 1], 31 | [1, 2, 1, 1], 32 | [1, 2, 2], 33 | [2, 1, 1, 1], 34 | [2, 1, 2], 35 | [2, 2, 1], 36 | ] 37 | */ 38 | ``` 39 | 40 | ## Mi solución 41 | 42 | ```js 43 | function getStaircasePaths(steps, maxJump) { 44 | const result = []; 45 | 46 | function generatePaths(currentPath, remainingSteps) { 47 | if (remainingSteps === 0) { 48 | result.push([...currentPath]); 49 | return; 50 | } 51 | 52 | for (let jump = 1; jump <= maxJump && jump <= remainingSteps; jump++) { 53 | currentPath.push(jump); 54 | generatePaths(currentPath, remainingSteps - jump); 55 | currentPath.pop(); 56 | } 57 | } 58 | 59 | generatePaths([], steps); 60 | return result; 61 | } 62 | ``` 63 | -------------------------------------------------------------------------------- /2023/24-brincos-en-la-escalera/index.js: -------------------------------------------------------------------------------- 1 | function getStaircasePaths(steps, maxJump) { 2 | const result = []; 3 | 4 | function generatePaths(currentPath, remainingSteps) { 5 | if (remainingSteps === 0) { 6 | result.push([...currentPath]); 7 | return; 8 | } 9 | 10 | for (let jump = 1; jump <= maxJump && jump <= remainingSteps; jump++) { 11 | currentPath.push(jump); 12 | generatePaths(currentPath, remainingSteps - jump); 13 | currentPath.pop(); 14 | } 15 | } 16 | 17 | generatePaths([], steps); 18 | return result; 19 | } 20 | 21 | module.exports = getStaircasePaths; 22 | -------------------------------------------------------------------------------- /2023/24-brincos-en-la-escalera/index.test.js: -------------------------------------------------------------------------------- 1 | const getStaircasePaths = require('./index'); 2 | 3 | describe('24 => Brincos en la escalera', () => { 4 | const testCases = [ 5 | { 6 | input: [2, 1], 7 | output: [[1, 1]], 8 | }, 9 | { 10 | input: [3, 3], 11 | output: [[1, 1, 1], [1, 2], [2, 1], [3]], 12 | }, 13 | { 14 | input: [5, 1], 15 | output: [[1, 1, 1, 1, 1]], 16 | }, 17 | { 18 | input: [5, 2], 19 | output: [ 20 | [1, 1, 1, 1, 1], 21 | [1, 1, 1, 2], 22 | [1, 1, 2, 1], 23 | [1, 2, 1, 1], 24 | [1, 2, 2], 25 | [2, 1, 1, 1], 26 | [2, 1, 2], 27 | [2, 2, 1], 28 | ], 29 | }, 30 | ]; 31 | 32 | it('should return an array type', () => { 33 | expect(Array.isArray(getStaircasePaths(...testCases[0].input))).toBe(true); 34 | }); 35 | 36 | it.each(testCases)('should return the correct output', ({ input, output }) => { 37 | expect(getStaircasePaths(...input)).toStrictEqual(output); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /2023/25-calculando-distancias/index.js: -------------------------------------------------------------------------------- 1 | function travelDistance(map) { 2 | const roadmapArr = map.split('\n'); 3 | const roadmap = roadmapArr.join(''); 4 | const rows = roadmapArr.length; 5 | const cols = roadmapArr[0].length; 6 | const len = roadmap.length; 7 | const santaPos = roadmap.indexOf('S'); 8 | 9 | let gift = 1; 10 | let movements = 0; 11 | let santaCol = santaPos % cols; 12 | let santaRow = Math.floor((santaPos * rows) / len); 13 | const numbers = roadmap.replace(/\.|S/g, ''); 14 | 15 | // eslint-disable-next-line no-restricted-syntax, no-underscore-dangle, no-unused-vars 16 | for (const _num of numbers) { 17 | const giftPos = roadmap.indexOf(`${gift}`); 18 | const giftCol = giftPos % cols; 19 | const giftRow = Math.floor((giftPos * rows) / len); 20 | movements += Math.abs(santaRow - giftRow); 21 | movements += Math.abs(santaCol - giftCol); 22 | santaCol = giftCol; 23 | santaRow = giftRow; 24 | gift++; 25 | } 26 | return movements; 27 | } 28 | 29 | module.exports = travelDistance; 30 | -------------------------------------------------------------------------------- /2023/25-calculando-distancias/index.test.js: -------------------------------------------------------------------------------- 1 | const travelDistance = require('./index'); 2 | 3 | describe('25 => Calculando distancias', () => { 4 | const testCases = [ 5 | { 6 | input: '.....1....\n..S.......\n..........\n....3.....\n......2...', 7 | output: 12, 8 | }, 9 | { 10 | input: '..S.1...', 11 | output: 2, 12 | }, 13 | { 14 | input: '.....2....\n..S.......\n..........\n....1.....\n......3...', 15 | output: 13, 16 | }, 17 | { 18 | input: '3....1....\n..S.......\n.........2\n..........\n......4...', 19 | output: 31, 20 | }, 21 | { 22 | input: 'S1', 23 | output: 1, 24 | }, 25 | { 26 | input: '1....S', 27 | output: 5, 28 | }, 29 | { 30 | input: 'S12....3', 31 | output: 7, 32 | }, 33 | ]; 34 | 35 | it('should return a number type', () => { 36 | expect(typeof travelDistance(testCases[0].input)).toBe('number'); 37 | }); 38 | 39 | it.each(testCases)('should return $output', ({ input, output }) => { 40 | expect(travelDistance(input)).toBe(output); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /2024/01-primer-regalo-repetido/README.md: -------------------------------------------------------------------------------- 1 | # Reto 01: 🎁 ¡Primer regalo repetido! 2 | 3 | **Santa Claus** 🎅 ha recibido una lista de números mágicos que representan regalos 🎁, pero algunos de ellos están duplicados y deben ser eliminados para evitar confusiones. Además, **los regalos deben ser ordenados en orden ascendente antes de entregárselos a los elfos.** 4 | 5 | Tu tarea es escribir una función que reciba una lista de números enteros (que pueden incluir duplicados) y devuelva una nueva lista sin duplicados, ordenada en orden ascendente. 6 | 7 | ```js 8 | const gifts1 = [3, 1, 2, 3, 4, 2, 5] 9 | const preparedGifts1 = prepareGifts(gifts1) 10 | console.log(preparedGifts1) // [1, 2, 3, 4, 5] 11 | 12 | const gifts2 = [6, 5, 5, 5, 5] 13 | const preparedGifts2 = prepareGifts(gifts2) 14 | console.log(preparedGifts2) // [5, 6] 15 | 16 | const gifts3 = [] 17 | const preparedGifts3 = prepareGifts(gifts3) 18 | console.log(preparedGifts3) // [] 19 | // No hay regalos, la lista queda vacía 20 | ``` 21 | 22 | ## Mi solución explicada 23 | 24 | ```js 25 | function prepareGifts(gifts) { 26 | return [...new Set(gifts)].sort((a, b) => a - b); 27 | } 28 | ``` 29 | 30 | Primero, creamos un nuevo `Set` a partir de la lista de regalos. Un `Set` es una colección de valores únicos, por lo que automáticamente elimina los duplicados. 31 | 32 | ```js 33 | [...new Set(gifts)] 34 | ``` 35 | 36 | Después, convertimos el `Set` en un array con el operador de propagación (`...`) y lo ordenamos en orden ascendente con el método `sort`. 37 | 38 | Hay que tener en cuenta que el trabajar con números, el método `sort` por defecto ordena los elementos como si fueran cadenas de texto. Por lo tanto, es necesario pasar una función de comparación que convierta los elementos a números antes de compararlos. 39 | 40 | ```js 41 | [...new Set(gifts)].sort((a, b) => a - b) 42 | ``` 43 | 44 | Finalmente, devolvemos el array resultante. 45 | -------------------------------------------------------------------------------- /2024/01-primer-regalo-repetido/index.js: -------------------------------------------------------------------------------- 1 | function prepareGifts(gifts) { 2 | return [...new Set(gifts)].sort((a, b) => a - b); 3 | } 4 | 5 | module.exports = prepareGifts; 6 | -------------------------------------------------------------------------------- /2024/01-primer-regalo-repetido/index.test.js: -------------------------------------------------------------------------------- 1 | const prepareGifts = require('./index'); 2 | 3 | describe('01 => Primer-regalo-repetido', () => { 4 | const TEST_CASES = [ 5 | { 6 | input: [3, 1, 2, 3, 4, 2, 5], 7 | output: [1, 2, 3, 4, 5], 8 | }, 9 | { 10 | input: [5, 5, 5, 5], 11 | output: [5], 12 | }, 13 | { 14 | input: [1, 2, 3], 15 | output: [1, 2, 3], 16 | }, 17 | { 18 | input: [], 19 | output: [], 20 | }, 21 | { 22 | input: [10, 20, 10, 30, 20, 30, 40], 23 | output: [10, 20, 30, 40], 24 | }, 25 | { 26 | input: [3, 1, 2, 3, 1, 2], 27 | output: [1, 2, 3], 28 | }, 29 | ]; 30 | 31 | it('should return an array', () => { 32 | const { input } = TEST_CASES[0]; 33 | expect(prepareGifts(input)).toBeInstanceOf(Array); 34 | }); 35 | 36 | it.each(TEST_CASES)('should return $output', ({ input, output }) => { 37 | expect(prepareGifts(input)).toEqual(output); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /2024/02-enmarcando-nombres/index.js: -------------------------------------------------------------------------------- 1 | function createFrame(names) { 2 | const maxLength = Math.max(...names.map((name) => name.length)); 3 | const border = '*'.repeat(maxLength + 4); 4 | const framedNames = names.map((name) => `* ${name.padEnd(maxLength, ' ')} *`); 5 | 6 | return [border, ...framedNames, border].join('\n'); 7 | } 8 | 9 | module.exports = createFrame; 10 | -------------------------------------------------------------------------------- /2024/02-enmarcando-nombres/index.test.js: -------------------------------------------------------------------------------- 1 | const createFrame = require('./index'); 2 | 3 | describe('02 => Enmarcando-nombres', () => { 4 | const TEST_CASES = [ 5 | { 6 | input: ['midu'], 7 | output: '********\n* midu *\n********', 8 | }, 9 | { 10 | input: ['midu', 'madeval', 'educalvolpz'], 11 | output: 12 | '***************\n* midu *\n* madeval *\n* educalvolpz *\n***************', 13 | }, 14 | { 15 | input: ['a', 'bb', 'ccc'], 16 | output: '*******\n* a *\n* bb *\n* ccc *\n*******', 17 | }, 18 | { 19 | input: ['midu', 'madeval', 'educalvolpz', 'midu'], 20 | output: 21 | '***************\n* midu *\n* madeval *\n* educalvolpz *\n* midu *\n***************', 22 | }, 23 | ]; 24 | 25 | it('should return an array type', () => { 26 | const { input } = TEST_CASES[0]; 27 | expect(typeof createFrame(input)).toBe('string'); 28 | }); 29 | 30 | it.each(TEST_CASES)( 31 | 'should return the correct frame', 32 | ({ input, output }) => { 33 | expect(createFrame(input)).toBe(output); 34 | }, 35 | ); 36 | }); 37 | -------------------------------------------------------------------------------- /2024/03-organizando-el-inventario/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-bitwise */ 2 | /* eslint-disable no-sequences */ 3 | function organizeInventory(inventory) { 4 | return inventory.reduce( 5 | (result, { category, name, quantity }) => ( 6 | (result[category] ??= {}), 7 | (result[category][name] = ~~result[category][name] + quantity), 8 | result 9 | ), 10 | {}, 11 | ); 12 | } 13 | 14 | module.exports = organizeInventory; 15 | -------------------------------------------------------------------------------- /2024/03-organizando-el-inventario/index.test.js: -------------------------------------------------------------------------------- 1 | const organizeInventory = require('./index'); 2 | 3 | describe('03 => Organizando-el-inventario', () => { 4 | const TEST_CASES = [ 5 | { 6 | input: [], 7 | output: {}, 8 | }, 9 | { 10 | input: [{ name: 'doll', quantity: 5, category: 'toys' }], 11 | output: { toys: { doll: 5 } }, 12 | }, 13 | { 14 | input: [ 15 | { name: 'book', quantity: 10, category: 'education' }, 16 | { name: 'book', quantity: 5, category: 'education' }, 17 | { name: 'paint', quantity: 3, category: 'art' }, 18 | ], 19 | output: { 20 | education: { 21 | book: 15, 22 | }, 23 | art: { 24 | paint: 3, 25 | }, 26 | }, 27 | }, 28 | { 29 | input: [ 30 | { name: 'doll', quantity: 5, category: 'toys' }, 31 | { name: 'car', quantity: 3, category: 'toys' }, 32 | { name: 'ball', quantity: 2, category: 'sports' }, 33 | { name: 'car', quantity: 2, category: 'toys' }, 34 | { name: 'racket', quantity: 4, category: 'sports' }, 35 | ], 36 | output: { 37 | toys: { 38 | doll: 5, 39 | car: 5, 40 | }, 41 | sports: { 42 | ball: 2, 43 | racket: 4, 44 | }, 45 | }, 46 | }, 47 | ]; 48 | 49 | it('should return an object', () => { 50 | const { input } = TEST_CASES[0]; 51 | const result = organizeInventory(input); 52 | expect(typeof result).toBe('object'); 53 | }); 54 | 55 | it.each(TEST_CASES)( 56 | 'should return the expected value', 57 | ({ input, output }) => { 58 | const result = organizeInventory(input); 59 | expect(result).toEqual(output); 60 | }, 61 | ); 62 | }); 63 | -------------------------------------------------------------------------------- /2024/04-decorando-el-arbol-de-navidad/index.js: -------------------------------------------------------------------------------- 1 | function createXmasTree(height, ornament) { 2 | const totalWidth = 2 * height - 1; 3 | const trunkPadding = '_'.repeat((totalWidth - 1) / 2); 4 | 5 | const tree = Array.from({ length: height }, (_, i) => { 6 | const layerWidth = 2 * i + 1; 7 | const spaces = '_'.repeat((totalWidth - layerWidth) / 2); 8 | return spaces + ornament.repeat(layerWidth) + spaces; 9 | }); 10 | 11 | const trunk = `${trunkPadding}#${trunkPadding}`; 12 | tree.push(trunk, trunk); 13 | 14 | return tree.join('\n'); 15 | } 16 | 17 | module.exports = createXmasTree; 18 | -------------------------------------------------------------------------------- /2024/04-decorando-el-arbol-de-navidad/index.test.js: -------------------------------------------------------------------------------- 1 | const createXmasTree = require('./index'); 2 | 3 | describe('04 => Decorando-el-arbol-de-navidad', () => { 4 | const TEST_CASES = [ 5 | { 6 | input: [3, '*'], 7 | output: '__*__\n_***_\n*****\n__#__\n__#__', 8 | }, 9 | { 10 | input: [5, '+'], 11 | output: 12 | '____+____\n___+++___\n__+++++__\n_+++++++_\n+++++++++\n____#____\n____#____', 13 | }, 14 | { 15 | input: [6, '@'], 16 | output: 17 | '_____@_____\n____@@@____\n___@@@@@___\n__@@@@@@@__\n_@@@@@@@@@_\n@@@@@@@@@@@\n_____#_____\n_____#_____', 18 | }, 19 | { 20 | input: [1, '*'], 21 | output: '*\n#\n#', 22 | }, 23 | { 24 | input: [4, '#'], 25 | output: '___#___\n__###__\n_#####_\n#######\n___#___\n___#___', 26 | }, 27 | ]; 28 | 29 | it('should return a string', () => { 30 | expect(typeof createXmasTree(3, '*')).toBe('string'); 31 | }); 32 | 33 | it.each(TEST_CASES)( 34 | 'should return the expected output', 35 | ({ input, output }) => { 36 | expect(createXmasTree(...input)).toBe(output); 37 | }, 38 | ); 39 | }); 40 | -------------------------------------------------------------------------------- /2024/05-emparejando-botas/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable function-paren-newline */ 2 | /* eslint-disable implicit-arrow-linebreak */ 3 | /* eslint-disable no-restricted-syntax */ 4 | function organizeShoes(shoes) { 5 | const sizeCount = new Map(); 6 | 7 | for (const { type, size } of shoes) { 8 | const counts = sizeCount.get(size) || { I: 0, R: 0 }; 9 | counts[type]++; 10 | sizeCount.set(size, counts); 11 | } 12 | 13 | return Array.from(sizeCount.entries()).flatMap(([size, { I, R }]) => 14 | Array(Math.min(I, R)).fill(size), 15 | ); 16 | } 17 | 18 | module.exports = organizeShoes; 19 | -------------------------------------------------------------------------------- /2024/05-emparejando-botas/index.test.js: -------------------------------------------------------------------------------- 1 | const organizeShoes = require('./index'); 2 | 3 | describe('05 => Emparejando-botas', () => { 4 | const testCases = [ 5 | { 6 | input: [ 7 | { type: 'I', size: 38 }, 8 | { type: 'R', size: 38 }, 9 | { type: 'R', size: 42 }, 10 | { type: 'I', size: 41 }, 11 | { type: 'I', size: 42 }, 12 | ], 13 | output: [38, 42], 14 | }, 15 | { 16 | input: [ 17 | { type: 'I', size: 38 }, 18 | { type: 'R', size: 38 }, 19 | { type: 'I', size: 38 }, 20 | { type: 'I', size: 38 }, 21 | { type: 'R', size: 38 }, 22 | ], 23 | output: [38, 38], 24 | }, 25 | { 26 | input: [ 27 | { type: 'I', size: 38 }, 28 | { type: 'R', size: 36 }, 29 | { type: 'R', size: 42 }, 30 | { type: 'I', size: 41 }, 31 | { type: 'I', size: 42 }, 32 | ], 33 | output: [42], 34 | }, 35 | { 36 | input: [ 37 | { type: 'I', size: 40 }, 38 | { type: 'R', size: 40 }, 39 | { type: 'I', size: 40 }, 40 | { type: 'R', size: 40 }, 41 | ], 42 | output: [40, 40], 43 | }, 44 | { 45 | input: [ 46 | { type: 'I', size: 39 }, 47 | { type: 'R', size: 39 }, 48 | { type: 'R', size: 39 }, 49 | ], 50 | output: [39], 51 | }, 52 | ]; 53 | 54 | it('should return an array', () => { 55 | const result = organizeShoes([]); 56 | expect(result).toBeInstanceOf(Array); 57 | }); 58 | 59 | it.each(testCases)('should return $output', ({ input, output }) => { 60 | const result = organizeShoes(input); 61 | expect(result).toEqual(output); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /2024/06-regalo-dentro-de-la-caja/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable operator-linebreak */ 3 | function inBox(box) { 4 | return box 5 | .slice(1, -1) 6 | .some( 7 | (line) => 8 | line.includes('*') && 9 | line.indexOf('*') > 0 && 10 | line.indexOf('*') < line.length - 1, 11 | ); 12 | } 13 | 14 | module.exports = inBox; 15 | -------------------------------------------------------------------------------- /2024/06-regalo-dentro-de-la-caja/index.test.js: -------------------------------------------------------------------------------- 1 | const inBox = require('./index'); 2 | 3 | describe('06 => Regalo-dentro-de-la-caja', () => { 4 | const TEST_CASES = [ 5 | { 6 | input: ['###', '#*#', '###'], 7 | output: true, 8 | }, 9 | { 10 | input: ['#*#', '###', '###'], 11 | output: false, 12 | }, 13 | { 14 | input: ['###', '# #', '###'], 15 | output: false, 16 | }, 17 | { 18 | input: ['####', '#* #', '# #', '####'], 19 | output: true, 20 | }, 21 | { 22 | input: ['#####', '# #', '# #*', '####'], 23 | output: false, 24 | }, 25 | { 26 | input: ['#####', '# #', '# #', '# #', '#####'], 27 | output: false, 28 | }, 29 | { 30 | input: ['#####', '# #', '# #', '*# #', '#####'], 31 | output: false, 32 | }, 33 | { 34 | input: ['##*##', '# #', '# #', '# #', '#####'], 35 | output: false, 36 | }, 37 | { 38 | input: ['####', '# #', '##*#'], 39 | output: false, 40 | }, 41 | { 42 | input: ['###', '###', '#*#'], 43 | output: false, 44 | }, 45 | ]; 46 | 47 | it('should return a boolean type', () => {}); 48 | 49 | it.each(TEST_CASES)('should return $output', ({ input, output }) => { 50 | expect(inBox(input)).toBe(output); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /2024/07-el-ataque-del-grinch/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable function-paren-newline */ 2 | /* eslint-disable implicit-arrow-linebreak */ 3 | function fixPackages(packages) { 4 | const regex = /\(([^()]+)\)/; 5 | while (regex.test(packages)) { 6 | packages = packages.replace(regex, (_, inner) => 7 | [...inner].reverse().join(''), 8 | ); 9 | } 10 | return packages; 11 | } 12 | 13 | module.exports = fixPackages; 14 | -------------------------------------------------------------------------------- /2024/07-el-ataque-del-grinch/index.test.js: -------------------------------------------------------------------------------- 1 | const fixPackages = require('./index'); 2 | 3 | describe('07 => El-ataque-del-grinch', () => { 4 | const TEST_CASES = [ 5 | { 6 | input: 'a(bc)de', 7 | output: 'acbde', 8 | }, 9 | { 10 | input: 'a(bc(def)g)h', 11 | output: 'agdefcbh', 12 | }, 13 | { 14 | input: 'abc(def(gh)i)jk', 15 | output: 'abcighfedjk', 16 | }, 17 | { 18 | input: 'a(b(c))e', 19 | output: 'acbe', 20 | }, 21 | { 22 | input: 'a(b(cd(efg)))h', 23 | output: 'acdgfebh', 24 | }, 25 | { 26 | input: '(abc(def(ghi)))', 27 | output: 'defihgcba', 28 | }, 29 | ]; 30 | 31 | it('should return string type', () => { 32 | const testCase = TEST_CASES[0]; 33 | expect(typeof fixPackages(testCase.input)).toBe('string'); 34 | }); 35 | 36 | it.each(TEST_CASES)('should return $output', ({ input, output }) => { 37 | expect(fixPackages(input)).toBe(output); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /2024/08-la-carrera-de-renos/index.js: -------------------------------------------------------------------------------- 1 | function drawRace(indices, length) { 2 | return indices 3 | .map((progress, laneIndex) => { 4 | const trackLine = [...'~'.repeat(length)]; 5 | 6 | if (progress !== 0) { 7 | const renoPosition = progress > 0 ? progress : length + progress; 8 | trackLine[renoPosition] = 'r'; 9 | } 10 | 11 | const isometricOffset = ' '.repeat(indices.length - laneIndex - 1); 12 | const trackLineStr = trackLine.join(''); 13 | return `${isometricOffset}${trackLineStr} /${laneIndex + 1}`; 14 | }) 15 | .join('\n'); 16 | } 17 | 18 | module.exports = drawRace; 19 | -------------------------------------------------------------------------------- /2024/08-la-carrera-de-renos/index.test.js: -------------------------------------------------------------------------------- 1 | const drawRace = require('./index'); 2 | 3 | describe('08 => La-carrera-de-renos', () => { 4 | const TEST_CASES = [ 5 | { 6 | input: [[0, 5, -3], 10], 7 | output: ' ~~~~~~~~~~ /1\n ~~~~~r~~~~ /2\n~~~~~~~r~~ /3', 8 | }, 9 | { 10 | input: [[2, -1, 0, 5], 8], 11 | output: ' ~~r~~~~~ /1\n ~~~~~~~r /2\n ~~~~~~~~ /3\n~~~~~r~~ /4', 12 | }, 13 | { 14 | input: [[3, 7, -2], 12], 15 | output: ' ~~~r~~~~~~~~ /1\n ~~~~~~~r~~~~ /2\n~~~~~~~~~~r~ /3', 16 | }, 17 | { 18 | input: [[0, 0, 0], 6], 19 | output: ' ~~~~~~ /1\n ~~~~~~ /2\n~~~~~~ /3', 20 | }, 21 | { 22 | input: [[5, 3, -4], 9], 23 | output: ' ~~~~~r~~~ /1\n ~~~r~~~~~ /2\n~~~~~r~~~ /3', 24 | }, 25 | ]; 26 | 27 | it('should return string type', () => { 28 | const result = drawRace(...TEST_CASES[0].input); 29 | expect(typeof result).toBe('string'); 30 | }); 31 | 32 | it.each(TEST_CASES)('should return expected output', (testCase) => { 33 | const result = drawRace(...testCase.input); 34 | expect(result).toBe(testCase.output); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /2024/09-el-tren-magico/index.js: -------------------------------------------------------------------------------- 1 | function moveTrain(board, mov) { 2 | const directions = { 3 | U: (row, col) => board[row - 1]?.[col], 4 | D: (row, col) => board[row + 1]?.[col], 5 | R: (row, col) => board[row][col + 1], 6 | L: (row, col) => board[row][col - 1], 7 | }; 8 | 9 | const flatBoard = board.join(''); 10 | const trainHeadIndex = flatBoard.indexOf('@'); 11 | 12 | const totalColumns = board[0].length; 13 | const currentRow = Math.floor(trainHeadIndex / totalColumns); 14 | const currentColumn = trainHeadIndex % totalColumns; 15 | 16 | const nextCell = directions[mov](currentRow, currentColumn); 17 | 18 | const results = { 19 | '*': 'eat', 20 | '·': 'none', 21 | }; 22 | 23 | return results[nextCell] || 'crash'; 24 | } 25 | 26 | module.exports = moveTrain; 27 | -------------------------------------------------------------------------------- /2024/09-el-tren-magico/index.test.js: -------------------------------------------------------------------------------- 1 | const moveTrain = require('./index'); 2 | 3 | describe('09 => El-tren-magico', () => { 4 | const TEST_CASES = [ 5 | { 6 | input: [['·····', '··*··', '··@··', '··o··', '··o··'], 'U'], 7 | output: 'eat', 8 | }, 9 | { 10 | input: [['·····', '··*··', '··@··', '··o··', '··o··'], 'D'], 11 | output: 'crash', 12 | }, 13 | { 14 | input: [['·····', '··*··', '··@··', '··o··', '··o··'], 'R'], 15 | output: 'none', 16 | }, 17 | { 18 | input: [['··@··', '··o··', '·*o··', '··o··', '··o··'], 'U'], 19 | output: 'crash', 20 | }, 21 | { 22 | input: [['··@··', '··o··', '·*o··', '··o··', '··o··'], 'L'], 23 | output: 'none', 24 | }, 25 | { 26 | input: [['·····', '··@··', '··*··', '·····', '·····'], 'D'], 27 | output: 'eat', 28 | }, 29 | ]; 30 | 31 | it('should return string type', () => { 32 | const testCase = TEST_CASES[0]; 33 | const result = moveTrain(...testCase.input); 34 | expect(typeof result).toBe('string'); 35 | }); 36 | 37 | it.each(TEST_CASES)('should return $output', ({ input, output }) => { 38 | expect(moveTrain(...input)).toBe(output); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /2024/10-el-ensamblador-elfico/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-bitwise */ 2 | function compile(instructions) { 3 | const registers = {}; 4 | let pointer = 0; 5 | 6 | const operations = { 7 | MOV: (arg1, arg2) => { 8 | registers[arg2] = registers[arg1] ?? arg1; 9 | }, 10 | INC: (arg1) => { 11 | registers[arg1] = ~~registers[arg1] + 1; 12 | }, 13 | DEC: (arg1) => { 14 | registers[arg1] = ~~registers[arg1] - 1; 15 | }, 16 | JMP: (arg1, arg2) => !registers[arg1] && (pointer = arg2 - 1), 17 | }; 18 | 19 | while (pointer < instructions.length) { 20 | const [command, arg1, arg2] = instructions[pointer].split(' '); 21 | operations[command]?.(arg1, arg2); 22 | 23 | pointer++; 24 | } 25 | 26 | return registers.A; 27 | } 28 | 29 | module.exports = compile; 30 | -------------------------------------------------------------------------------- /2024/10-el-ensamblador-elfico/index.test.js: -------------------------------------------------------------------------------- 1 | const compile = require('./index'); 2 | 3 | describe('10 => El-ensamblador-elfico', () => { 4 | const TEST_CASES = [ 5 | { 6 | input: ['MOV 0 A', 'INC A'], 7 | output: 1, 8 | }, 9 | { 10 | input: ['INC A', 'INC A', 'DEC A', 'MOV A B'], 11 | output: 1, 12 | }, 13 | { 14 | input: ['MOV 5 B', 'DEC B', 'MOV B A', 'INC A'], 15 | output: 5, 16 | }, 17 | { 18 | input: ['INC C', 'DEC B', 'MOV C Y', 'INC Y'], 19 | output: undefined, 20 | }, 21 | { 22 | input: ['MOV 2 X', 'DEC X', 'DEC X', 'JMP X 1', 'MOV X A'], 23 | output: -2, 24 | }, 25 | { 26 | input: ['MOV 3 C', 'DEC C', 'DEC C', 'DEC C', 'JMP C 3', 'MOV C A'], 27 | output: -1, 28 | }, 29 | ]; 30 | 31 | it('should return number type', () => { 32 | const result = compile(['MOV 0 A', 'INC A']); 33 | expect(typeof result).toBe('number'); 34 | }); 35 | 36 | it.each(TEST_CASES)('should return $output', ({ input, output }) => { 37 | const result = compile(input); 38 | expect(result).toBe(output); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /2024/11-nombres-de-archivos-codificados/index.js: -------------------------------------------------------------------------------- 1 | function decodeFilename(filename) { 2 | const underscoreIndex = filename.indexOf('_'); 3 | const lastDotIndex = filename.lastIndexOf('.'); 4 | return filename.slice(underscoreIndex + 1, lastDotIndex); 5 | } 6 | 7 | module.exports = decodeFilename; 8 | -------------------------------------------------------------------------------- /2024/11-nombres-de-archivos-codificados/index.test.js: -------------------------------------------------------------------------------- 1 | const decodeFilename = require('./index'); 2 | 3 | describe('11 => Nombres-de-archivos-codificados', () => { 4 | const TEST_CASES = [ 5 | { 6 | input: '2023122512345678_sleighDesign.png.grinchwa', 7 | output: 'sleighDesign.png', 8 | }, 9 | { 10 | input: '42_chimney_dimensions.pdf.hack2023', 11 | output: 'chimney_dimensions.pdf', 12 | }, 13 | { 14 | input: '987654321_elf-roster.csv.tempfile', 15 | output: 'elf-roster.csv', 16 | }, 17 | { 18 | input: '2024120912345678_magic_wand-blueprint.jpg.grinchbackup', 19 | output: 'magic_wand-blueprint.jpg', 20 | }, 21 | { 22 | input: '51231_trainSchedule.txt.extra', 23 | output: 'trainSchedule.txt', 24 | }, 25 | ]; 26 | 27 | it('should return string type', () => { 28 | const testCase = TEST_CASES[0]; 29 | expect(typeof decodeFilename(testCase.input)).toBe('string'); 30 | }); 31 | 32 | it.each(TEST_CASES)('should return $output', ({ input, output }) => { 33 | expect(decodeFilename(input)).toBe(output); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /2024/12-cuanto-cuesta-el-arbol/index.js: -------------------------------------------------------------------------------- 1 | function calculatePrice(ornaments) { 2 | const ornamentValues = { 3 | '*': 1, 4 | o: 5, 5 | '^': 10, 6 | '#': 50, 7 | '@': 100, 8 | }; 9 | 10 | if (!/^[*o^#@]*$/.test(ornaments)) return undefined; 11 | 12 | return [...ornaments].reduce((totalPrice, current, index) => { 13 | const currentValue = ornamentValues[current]; 14 | const nextValue = ornamentValues[ornaments[index + 1]]; 15 | 16 | const isNextGreater = nextValue > currentValue; 17 | const valueToAdd = isNextGreater ? -currentValue : currentValue; 18 | 19 | return totalPrice + valueToAdd; 20 | }, 0); 21 | } 22 | 23 | module.exports = calculatePrice; 24 | -------------------------------------------------------------------------------- /2024/12-cuanto-cuesta-el-arbol/index.test.js: -------------------------------------------------------------------------------- 1 | const calculatePrice = require('./index'); 2 | 3 | describe('12 => Cuanto-cuesta-el-arbol', () => { 4 | const TEST_CASES = [ 5 | { 6 | input: '***', 7 | output: 3, 8 | }, 9 | { 10 | input: '*o', 11 | output: 4, 12 | }, 13 | { 14 | input: 'o*', 15 | output: 6, 16 | }, 17 | { 18 | input: '*o@', 19 | output: 94, 20 | }, 21 | { 22 | input: '^#', 23 | output: 40, 24 | }, 25 | { 26 | input: '*@Z', 27 | output: undefined, 28 | }, 29 | ]; 30 | 31 | it('should return number type', () => { 32 | const testCase = TEST_CASES[0]; 33 | const result = calculatePrice(testCase.input); 34 | expect(typeof result).toBe('number'); 35 | }); 36 | 37 | it.each(TEST_CASES)('should return $output', ({ input, output }) => { 38 | const result = calculatePrice(input); 39 | expect(result).toBe(output); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /2024/13-el-robot-esta-de-vuelta/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable nonblock-statement-body-position */ 2 | /* eslint-disable curly */ 3 | function isRobotBack(moves) { 4 | let x = 0; 5 | let y = 0; 6 | 7 | const moveCount = { 8 | L: 0, 9 | R: 0, 10 | U: 0, 11 | D: 0, 12 | }; 13 | const inverted = { 14 | L: 'R', 15 | R: 'L', 16 | U: 'D', 17 | D: 'U', 18 | }; 19 | const directions = { 20 | U: [0, 1], 21 | D: [0, -1], 22 | R: [1, 0], 23 | L: [-1, 0], 24 | }; 25 | 26 | const applyMove = (move, factor = 1) => { 27 | moveCount[move] += factor; 28 | x += directions[move][0] * factor; 29 | y += directions[move][1] * factor; 30 | }; 31 | 32 | moves.replace(/([*!?])?([LRUD])/g, (_, operator, move) => { 33 | if (operator === '*') applyMove(move, 2); 34 | else if (operator === '!') applyMove(inverted[move]); 35 | else if (!operator || (operator === '?' && !moveCount[move])) 36 | applyMove(move); 37 | }); 38 | 39 | return x || y ? [x, y] : true; 40 | } 41 | 42 | module.exports = isRobotBack; 43 | -------------------------------------------------------------------------------- /2024/13-el-robot-esta-de-vuelta/index.test.js: -------------------------------------------------------------------------------- 1 | const isRobotBack = require('./index'); 2 | 3 | describe('13 => El-robot-esta-de-vuelta', () => { 4 | const TEST_CASES = [ 5 | { 6 | input: 'R', 7 | output: [1, 0], 8 | }, 9 | { 10 | input: 'RL', 11 | output: true, 12 | }, 13 | { 14 | input: 'RLUD', 15 | output: true, 16 | }, 17 | { 18 | input: '*RU', 19 | output: [2, 1], 20 | }, 21 | { 22 | input: 'R*U', 23 | output: [1, 2], 24 | }, 25 | { 26 | input: 'LLL!R', 27 | output: [-4, 0], 28 | }, 29 | { 30 | input: 'R?R', 31 | output: [1, 0], 32 | }, 33 | { 34 | input: 'U?D', 35 | output: true, 36 | }, 37 | { 38 | input: 'R!L', 39 | output: [2, 0], 40 | }, 41 | { 42 | input: 'U!D', 43 | output: [0, 2], 44 | }, 45 | { 46 | input: 'R?L', 47 | output: true, 48 | }, 49 | { 50 | input: 'U?U', 51 | output: [0, 1], 52 | }, 53 | ]; 54 | 55 | it('should return boolean type', () => { 56 | const testCase = TEST_CASES[1]; 57 | const result = isRobotBack(testCase.input); 58 | const expected = testCase.output; 59 | expect(result).toEqual(expected); 60 | }); 61 | 62 | it('should return array type', () => { 63 | const testCase = TEST_CASES[0]; 64 | const result = isRobotBack(testCase.input); 65 | const expected = testCase.output; 66 | expect(result).toEqual(expected); 67 | }); 68 | 69 | it.each(TEST_CASES)('should return $output', ({ input, output }) => { 70 | const result = isRobotBack(input); 71 | expect(result).toEqual(output); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /2024/14-acomodando-los-renos/index.js: -------------------------------------------------------------------------------- 1 | function minMovesToStables(reindeer, stables) { 2 | reindeer.sort(); 3 | stables.sort(); 4 | 5 | return reindeer.reduce((totalMoves, currentReindeerPosition, index) => { 6 | const currentStablePosition = stables[index]; 7 | const distance = Math.abs(currentReindeerPosition - currentStablePosition); 8 | return totalMoves + distance; 9 | }, 0); 10 | } 11 | 12 | module.exports = minMovesToStables; 13 | -------------------------------------------------------------------------------- /2024/14-acomodando-los-renos/index.test.js: -------------------------------------------------------------------------------- 1 | const minMovesToStables = require('./index'); 2 | 3 | describe('14 => Acomodando-los-renos', () => { 4 | const TEST_CASES = [ 5 | { 6 | input: [ 7 | [2, 6, 9], 8 | [3, 8, 5], 9 | ], 10 | output: 3, 11 | }, 12 | { 13 | input: [ 14 | [1, 1, 3], 15 | [1, 4, 8], 16 | ], 17 | output: 8, 18 | }, 19 | { 20 | input: [ 21 | [5, 15, 10], 22 | [8, 18, 12], 23 | ], 24 | output: 8, 25 | }, 26 | { 27 | input: [ 28 | [30, 1, 2], 29 | [1, 2, 30], 30 | ], 31 | output: 0, 32 | }, 33 | { 34 | input: [ 35 | [30, 20, 10], 36 | [35, 25, 15], 37 | ], 38 | output: 15, 39 | }, 40 | { 41 | input: [ 42 | [100, 1], 43 | [50, 75], 44 | ], 45 | output: 74, 46 | }, 47 | ]; 48 | 49 | it('should return number type', () => { 50 | const testCase = TEST_CASES[0]; 51 | const result = minMovesToStables(...testCase.input); 52 | expect(typeof result).toBe('number'); 53 | }); 54 | 55 | it.each(TEST_CASES)('should return $output', ({ input, output }) => { 56 | expect(minMovesToStables(...input)).toBe(output); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /2024/15-dibujando-tablas/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable function-paren-newline */ 2 | /* eslint-disable implicit-arrow-linebreak */ 3 | function drawTable(data) { 4 | const headers = Object.keys(data[0]); 5 | 6 | const columnWidths = headers.map((header) => 7 | Math.max(header.length, ...data.map((row) => `${row[header]}`.length)), 8 | ); 9 | 10 | const separator = `+${columnWidths 11 | .map((width) => '-'.repeat(width + 2)) 12 | .join('+')}+`; 13 | 14 | const headerRowFormatted = `| ${headers 15 | .map((header, i) => { 16 | const headerFormatted = header.charAt(0).toUpperCase() + header.slice(1); 17 | return headerFormatted.padEnd(columnWidths[i]); 18 | }) 19 | .join(' | ')} |`; 20 | 21 | const rows = data.map( 22 | (row) => 23 | `| ${headers 24 | .map((key, i) => `${row[key]}`.padEnd(columnWidths[i])) 25 | .join(' | ')} |`, 26 | ); 27 | 28 | return [separator, headerRowFormatted, separator, ...rows, separator].join( 29 | '\n', 30 | ); 31 | } 32 | 33 | module.exports = drawTable; 34 | -------------------------------------------------------------------------------- /2024/16-limpiando-la-nieve-del-camino/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-syntax */ 2 | function removeSnow(s) { 3 | const stack = []; 4 | 5 | for (const snow of s) { 6 | if (stack.at(-1) === snow) { 7 | stack.pop(); 8 | } else { 9 | stack.push(snow); 10 | } 11 | } 12 | 13 | return stack.join(''); 14 | } 15 | 16 | module.exports = removeSnow; 17 | -------------------------------------------------------------------------------- /2024/16-limpiando-la-nieve-del-camino/index.test.js: -------------------------------------------------------------------------------- 1 | const removeSnow = require('./index'); 2 | 3 | describe('16 => Limpiando-la-nieve-del-camino', () => { 4 | const TEST_CASES = [ 5 | { 6 | input: 'abbaca', 7 | output: 'ca', 8 | }, 9 | { 10 | input: 'azxxzy', 11 | output: 'ay', 12 | }, 13 | { 14 | input: 'aabccba', 15 | output: 'a', 16 | }, 17 | { 18 | input: 'aaabbaacc', 19 | output: 'a', 20 | }, 21 | { 22 | input: 'xyzzy', 23 | output: 'x', 24 | }, 25 | { 26 | input: 'abbacddce', 27 | output: 'e', 28 | }, 29 | { 30 | input: '', 31 | output: '', 32 | }, 33 | { 34 | input: 'a', 35 | output: 'a', 36 | }, 37 | { 38 | input: 'aa', 39 | output: '', 40 | }, 41 | { 42 | input: 'ab', 43 | output: 'ab', 44 | }, 45 | { 46 | input: 'abc', 47 | output: 'abc', 48 | }, 49 | ]; 50 | 51 | it('should return string type', () => { 52 | const testCase = TEST_CASES[0]; 53 | const result = removeSnow(testCase.input); 54 | expect(typeof result).toBe('string'); 55 | }); 56 | 57 | it.each(TEST_CASES)('should return $output', ({ input, output }) => { 58 | const result = removeSnow(input); 59 | expect(result).toBe(output); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /2024/17-busca-las-bombas-del-grinch/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable function-paren-newline */ 2 | /* eslint-disable operator-linebreak */ 3 | /* eslint-disable implicit-arrow-linebreak */ 4 | function detectBombs(grid) { 5 | const neighborOffsets = [-1, 0, 1]; 6 | 7 | return grid.map((row, rowIndex) => 8 | row.map((_, colIndex) => { 9 | const adjacentBombsCount = neighborOffsets.reduce( 10 | (totalBombs, rowOffset) => 11 | totalBombs + 12 | neighborOffsets.reduce((bombsInColumn, colOffset) => { 13 | const neighborRow = rowIndex + rowOffset; 14 | const neighborCol = colIndex + colOffset; 15 | const isBomb = grid[neighborRow]?.[neighborCol]; 16 | return bombsInColumn + (isBomb ? 1 : 0); 17 | }, 0), 18 | 0, 19 | ); 20 | 21 | return adjacentBombsCount - (grid[rowIndex][colIndex] ? 1 : 0); 22 | }), 23 | ); 24 | } 25 | 26 | module.exports = detectBombs; 27 | -------------------------------------------------------------------------------- /2024/17-busca-las-bombas-del-grinch/index.test.js: -------------------------------------------------------------------------------- 1 | const detectBombs = require('./index'); 2 | 3 | describe('17 => Busca-las-bombas-del-grinch', () => { 4 | const TEST_CASES = [ 5 | { 6 | input: [ 7 | [true, false, false], 8 | [false, true, false], 9 | [false, false, false], 10 | ], 11 | output: [ 12 | [1, 2, 1], 13 | [2, 1, 1], 14 | [1, 1, 1], 15 | ], 16 | }, 17 | { 18 | input: [ 19 | [false, true, false], 20 | [true, false, true], 21 | [false, true, false], 22 | ], 23 | output: [ 24 | [2, 2, 2], 25 | [2, 4, 2], 26 | [2, 2, 2], 27 | ], 28 | }, 29 | { 30 | input: [ 31 | [true, true], 32 | [true, true], 33 | [false, false], 34 | ], 35 | output: [ 36 | [3, 3], 37 | [3, 3], 38 | [2, 2], 39 | ], 40 | }, 41 | { 42 | input: [ 43 | [true, true], 44 | [true, true], 45 | ], 46 | output: [ 47 | [3, 3], 48 | [3, 3], 49 | ], 50 | }, 51 | { 52 | input: [ 53 | [false, false, false], 54 | [false, true, false], 55 | [false, false, false], 56 | ], 57 | output: [ 58 | [1, 1, 1], 59 | [1, 0, 1], 60 | [1, 1, 1], 61 | ], 62 | }, 63 | { 64 | input: [ 65 | [true, false], 66 | [false, false], 67 | ], 68 | output: [ 69 | [0, 1], 70 | [1, 1], 71 | ], 72 | }, 73 | ]; 74 | 75 | it('should return array type', () => { 76 | const testCase = TEST_CASES[0]; 77 | const result = detectBombs(testCase.input); 78 | expect(Array.isArray(result)).toBe(true); 79 | }); 80 | 81 | it.each(TEST_CASES)('should return expected result', (testCase) => { 82 | const result = detectBombs(testCase.input); 83 | expect(result).toEqual(testCase.output); 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /2024/18-la-agenda-magica-de-santa/index.js: -------------------------------------------------------------------------------- 1 | function findInAgenda(agenda, phone) { 2 | const kidsList = agenda.split('\n'); 3 | const foundKids = kidsList.filter((kid) => kid.includes(phone)); 4 | 5 | const matchingCount = foundKids?.length; 6 | 7 | if (matchingCount === 1) { 8 | const [firstKid] = foundKids; 9 | const name = firstKid.split('<')[1].split('>')[0]; 10 | const address = firstKid 11 | .split(' ') 12 | .slice(1, -1) 13 | .join(' ') 14 | .split('<')[0] 15 | .trim(); 16 | 17 | return { name, address }; 18 | } 19 | 20 | return null; 21 | } 22 | 23 | module.exports = findInAgenda; 24 | -------------------------------------------------------------------------------- /2024/18-la-agenda-magica-de-santa/index.test.js: -------------------------------------------------------------------------------- 1 | const findInAgenda = require('./index'); 2 | 3 | describe('18 => La-agenda-magica-de-santa', () => { 4 | const agenda = `+34-600-123-456 Calle Gran Via 12 5 | Plaza Mayor 45 Madrid 28013 +34-600-987-654 6 | +1-800-555-0199 Fifth Ave New York`; 7 | 8 | const testCases = [ 9 | { 10 | input: [agenda, '34-600-123-456'], 11 | output: { name: 'Juan Perez', address: 'Calle Gran Via 12' }, 12 | }, 13 | { 14 | input: [agenda, '600-987'], 15 | output: { 16 | name: 'Maria Gomez', 17 | address: 'Mayor 45 Madrid 28013', 18 | }, 19 | }, 20 | { 21 | input: [agenda, '111'], 22 | output: null, 23 | }, 24 | { 25 | input: [agenda, '1'], 26 | output: null, 27 | }, 28 | ]; 29 | 30 | it('should return an object', () => { 31 | const result = findInAgenda(agenda, '34-600-123-456'); 32 | expect(typeof result).toBe('object'); 33 | }); 34 | 35 | it.each(testCases)( 36 | 'should return the correct name and address', 37 | (testCase) => { 38 | const result = findInAgenda(...testCase.input); 39 | expect(result).toEqual(testCase.output); 40 | }, 41 | ); 42 | }); 43 | -------------------------------------------------------------------------------- /2024/19-apila-cajas-magicas-para-repartir-regalos/index.js: -------------------------------------------------------------------------------- 1 | function distributeWeight(weight) { 2 | const boxLayouts = { 3 | 1: [' _ ', '|_|'], 4 | 2: [' ___ ', '|___|'], 5 | 5: [' _____ ', '| |', '|_____|'], 6 | 10: [' _________ ', '| |', '|_________|'], 7 | }; 8 | 9 | const stackedBoxes = []; 10 | const numbers = Object.keys(boxLayouts).map(Number).reverse(); 11 | 12 | function findTheBiggestBox(boxWeight) { 13 | return numbers.find((number) => number <= boxWeight); 14 | } 15 | 16 | while (weight > 0) { 17 | const boxWeight = findTheBiggestBox(weight); 18 | const [bottom, ...rest] = boxLayouts[boxWeight].slice().reverse(); 19 | const last = stackedBoxes.shift(); 20 | const newBottom = `${bottom}${last?.slice(bottom.length, -1) ?? ''}`; 21 | stackedBoxes.unshift(...[newBottom, ...rest].reverse()); 22 | 23 | weight -= boxWeight; 24 | } 25 | 26 | return stackedBoxes.join('\n'); 27 | } 28 | 29 | module.exports = distributeWeight; 30 | -------------------------------------------------------------------------------- /2024/19-apila-cajas-magicas-para-repartir-regalos/index.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable operator-linebreak */ 2 | const distributeWeight = require('./index'); 3 | 4 | describe('19 => Apila-cajas-magicas-para-repartir-regalos', () => { 5 | const TEST_CASES = [ 6 | { 7 | input: 1, 8 | output: ' _ \n|_|', 9 | }, 10 | { 11 | input: 2, 12 | output: ' ___ \n|___|', 13 | }, 14 | { 15 | input: 3, 16 | output: ' _ \n|_|_\n|___|', 17 | }, 18 | { 19 | input: 4, 20 | output: ' ___ \n|___|\n|___|', 21 | }, 22 | { 23 | input: 5, 24 | output: ' _____ \n| |\n|_____|', 25 | }, 26 | { 27 | input: 6, 28 | output: ' _ \n|_|___\n| |\n|_____|', 29 | }, 30 | { 31 | input: 7, 32 | output: ' ___ \n|___|_\n| |\n|_____|', 33 | }, 34 | { 35 | input: 18, 36 | output: 37 | ' _ \n|_|_\n|___|_\n| |\n|_____|___\n| |\n|_________|', 38 | }, 39 | { 40 | input: 121, 41 | output: 42 | ' _ \n|_|_______\n| |\n|_________|\n| |\n|_________|\n| |\n|_________|' + 43 | '\n| |\n|_________|\n| |\n|_________|\n| |\n|_________|\n| |\n' + 44 | '|_________|\n| |\n|_________|\n| |\n|_________|\n| |\n|_________|\n' + 45 | '| |\n|_________|\n| |\n|_________|', 46 | }, 47 | ]; 48 | 49 | it('should return string type', () => { 50 | const testCase = TEST_CASES[0]; 51 | const { input } = testCase; 52 | const result = distributeWeight(input); 53 | expect(typeof result).toBe('string'); 54 | }); 55 | 56 | it.each(TEST_CASES)('should return expected output', (testCase) => { 57 | const { input, output } = testCase; 58 | const result = distributeWeight(input); 59 | expect(result).toBe(output); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /2024/20-encuentra-los-regalos-faltantes-y-duplicados/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-bitwise */ 2 | /* eslint-disable no-restricted-syntax */ 3 | function fixGiftList(received, expected) { 4 | const counts = {}; 5 | 6 | for (const gift of received) counts[gift] = ~~counts[gift] + 1; 7 | for (const gift of expected) counts[gift] = ~~counts[gift] - 1; 8 | 9 | const missing = {}; 10 | const extra = {}; 11 | for (const [gift, count] of Object.entries(counts)) { 12 | if (count > 0) extra[gift] = count; 13 | else if (count < 0) missing[gift] = -count; 14 | } 15 | 16 | return { missing, extra }; 17 | } 18 | 19 | module.exports = fixGiftList; 20 | -------------------------------------------------------------------------------- /2024/21-calcula-la-altura-del-arbol-de-navidad/index.js: -------------------------------------------------------------------------------- 1 | function treeHeight(tree) { 2 | if (!tree) return 0; 3 | 4 | const leftHeight = treeHeight(tree.left); 5 | const rightHeight = treeHeight(tree.right); 6 | 7 | return Math.max(leftHeight, rightHeight) + 1; 8 | } 9 | 10 | module.exports = treeHeight; 11 | -------------------------------------------------------------------------------- /2024/21-calcula-la-altura-del-arbol-de-navidad/index.test.js: -------------------------------------------------------------------------------- 1 | const treeHeight = require('./index'); 2 | 3 | describe('21 => Calcula-la-altura-del-arbol-de-navidad', () => { 4 | const TEST_CASES = [ 5 | { 6 | input: { 7 | value: '🎁', 8 | left: null, 9 | right: null, 10 | }, 11 | output: 1, 12 | }, 13 | { 14 | input: { 15 | value: '🎁', 16 | left: { 17 | value: '🎄', 18 | left: null, 19 | right: null, 20 | }, 21 | right: { 22 | value: '❄️', 23 | left: null, 24 | right: null, 25 | }, 26 | }, 27 | output: 2, 28 | }, 29 | { 30 | input: { 31 | value: '🎁', 32 | left: { 33 | value: '🎄', 34 | left: { 35 | value: '⭐', 36 | left: null, 37 | right: null, 38 | }, 39 | right: null, 40 | }, 41 | right: { 42 | value: '❄️', 43 | left: null, 44 | right: null, 45 | }, 46 | }, 47 | output: 3, 48 | }, 49 | { 50 | input: null, 51 | output: 0, 52 | }, 53 | { 54 | input: { 55 | value: '🎁', 56 | left: { 57 | value: '🎄', 58 | left: { 59 | value: '⭐', 60 | left: { 61 | value: '🎅', 62 | left: null, 63 | right: null, 64 | }, 65 | right: null, 66 | }, 67 | right: null, 68 | }, 69 | right: null, 70 | }, 71 | output: 4, 72 | }, 73 | ]; 74 | 75 | it('should return number type', () => { 76 | const testCase = TEST_CASES[0]; 77 | 78 | expect(typeof treeHeight(testCase.input)).toBe('number'); 79 | }); 80 | 81 | it.each(TEST_CASES)('should return $output', (testCase) => { 82 | const received = treeHeight(testCase.input); 83 | 84 | expect(received).toBe(testCase.output); 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /2024/22-genera-combinaciones-de-regalos/index.js: -------------------------------------------------------------------------------- 1 | function generateGiftSets(gifts) { 2 | const results = []; 3 | function backtracking(start, currentSet) { 4 | if (currentSet.length > 0) { 5 | results.push([...currentSet]); 6 | } 7 | 8 | for (let i = start; i < gifts.length; i++) { 9 | currentSet.push(gifts[i]); 10 | backtracking(i + 1, currentSet); 11 | currentSet.pop(); 12 | } 13 | } 14 | 15 | backtracking(0, []); 16 | 17 | return results.sort((a, b) => a.length - b.length); 18 | } 19 | module.exports = generateGiftSets; 20 | -------------------------------------------------------------------------------- /2024/23-encuentra-los-numeros-perdidos/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable func-names */ 2 | /* eslint-disable no-extend-native */ 3 | Set.prototype.difference = function (set) { 4 | return new Set([...this].filter((x) => !set.has(x))); 5 | }; 6 | 7 | // La función de set.difference() esta disponible desde la version 22 de nodejs 8 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/difference#browser_compatibility 9 | // Para poder utilizarla en versiones anteriores de nodejs, se debe de agregar la función al prototipo de Set 10 | 11 | function findMissingNumbers(nums) { 12 | const maxNumber = Math.max(...nums); 13 | const fullSetOfNumbers = new Set( 14 | Array.from({ length: maxNumber }, (_, index) => index + 1), 15 | ); 16 | const uniqueNumbers = new Set(nums); 17 | const missingNumbers = fullSetOfNumbers.difference(uniqueNumbers); 18 | return [...missingNumbers]; 19 | } 20 | 21 | module.exports = findMissingNumbers; 22 | -------------------------------------------------------------------------------- /2024/23-encuentra-los-numeros-perdidos/index.test.js: -------------------------------------------------------------------------------- 1 | const findMissingNumbers = require('./index'); 2 | 3 | describe('23 => Encuentra-los-numeros-perdidos', () => { 4 | const TEST_CASES = [ 5 | { 6 | input: [1, 2, 4, 6], 7 | output: [3, 5], 8 | }, 9 | { 10 | input: [4, 8, 7, 2], 11 | output: [1, 3, 5, 6], 12 | }, 13 | { 14 | input: [3, 2, 1, 1], 15 | output: [], 16 | }, 17 | { 18 | input: [5, 5, 5, 3, 3, 2, 1], 19 | output: [4], 20 | }, 21 | { 22 | input: [1, 2, 3, 4, 5], 23 | output: [], 24 | }, 25 | ]; 26 | 27 | it('should return an array', () => { 28 | const result = findMissingNumbers([1, 2, 4, 6]); 29 | expect(Array.isArray(result)).toBe(true); 30 | }); 31 | 32 | it.each(TEST_CASES)( 33 | 'should return the missing numbers', 34 | ({ input, output }) => { 35 | const result = findMissingNumbers(input); 36 | expect(result).toEqual(output); 37 | }, 38 | ); 39 | }); 40 | -------------------------------------------------------------------------------- /2024/24-verifica-si-los-arboles-son-espejos-magicos/index.js: -------------------------------------------------------------------------------- 1 | function isTreesSynchronized(tree1, tree2) { 2 | function areMirrors(node1, node2) { 3 | if (!node1 && !node2) return true; 4 | if (!node1 || !node2 || node1.value !== node2.value) return false; 5 | 6 | const leftAndRightAreMirrors = areMirrors(node1.left, node2.right); 7 | const rightAndLeftAreMirrors = areMirrors(node1.right, node2.left); 8 | 9 | return leftAndRightAreMirrors && rightAndLeftAreMirrors; 10 | } 11 | 12 | const synchronized = tree1.value === tree2.value && areMirrors(tree1, tree2); 13 | 14 | return [synchronized, tree1.value]; 15 | } 16 | 17 | module.exports = isTreesSynchronized; 18 | -------------------------------------------------------------------------------- /2024/24-verifica-si-los-arboles-son-espejos-magicos/index.test.js: -------------------------------------------------------------------------------- 1 | const isTreesSynchronized = require('./index'); 2 | 3 | describe('24 => Verifica-si-los-arboles-son-espejos-magicos', () => { 4 | const TEST_CASES = [ 5 | { 6 | input: [{ value: '🎄' }, { value: '🎄' }], 7 | output: [true, '🎄'], 8 | }, 9 | { 10 | input: [ 11 | { value: '🎄', left: { value: '⭐' }, right: { value: '🎅' } }, 12 | { value: '🎄', left: { value: '🎅' }, right: { value: '⭐' } }, 13 | ], 14 | output: [true, '🎄'], 15 | }, 16 | { 17 | input: [ 18 | { value: '✨', left: { value: '⭐' }, right: { value: '🎅' } }, 19 | { value: '✨', left: { value: '🎅' }, right: { value: '🎁' } }, 20 | ], 21 | output: [false, '✨'], 22 | }, 23 | { 24 | input: [{ value: '🎁' }, { value: '🎁' }], 25 | output: [true, '🎁'], 26 | }, 27 | { 28 | input: [{ value: '🎄' }, { value: '🎁' }], 29 | output: [false, '🎄'], 30 | }, 31 | { 32 | input: [ 33 | { value: '🎄', left: { value: '⭐' } }, 34 | { value: '🎄', right: { value: '⭐' } }, 35 | ], 36 | output: [true, '🎄'], 37 | }, 38 | ]; 39 | 40 | it('should return an array', () => { 41 | expect(Array.isArray(isTreesSynchronized({}, {}))).toBe(true); 42 | }); 43 | 44 | it.each(TEST_CASES)( 45 | 'should return the correct value', 46 | ({ input, output }) => { 47 | expect(isTreesSynchronized(...input)[0]).toBe(output[0]); 48 | }, 49 | ); 50 | }); 51 | -------------------------------------------------------------------------------- /2024/25-ejecuta-el-lenguaje-magico/index.js: -------------------------------------------------------------------------------- 1 | function execute(code) { 2 | code = [...code]; 3 | let value = 0; 4 | const actions = { 5 | '>': () => {}, 6 | '+': () => value++, 7 | '-': () => value--, 8 | '[': () => { 9 | code = code.slice(code.indexOf(']') - 1); 10 | }, 11 | ']': () => { 12 | value = 0; 13 | }, 14 | '{': () => { 15 | if (value === 0) code = code.slice(code.indexOf('}')); 16 | }, 17 | '}': () => {}, 18 | }; 19 | 20 | while (code.length) actions[code.shift()](); 21 | 22 | return value; 23 | } 24 | 25 | module.exports = execute; 26 | -------------------------------------------------------------------------------- /2024/25-ejecuta-el-lenguaje-magico/index.test.js: -------------------------------------------------------------------------------- 1 | const execute = require('./index'); 2 | 3 | describe('25 => Ejecuta-el-lenguaje-magico', () => { 4 | const TEST_CASES = [ 5 | { 6 | input: '+++', 7 | output: 3, 8 | }, 9 | { 10 | input: '+--', 11 | output: -1, 12 | }, 13 | { 14 | input: '>+++[-]', 15 | output: 0, 16 | }, 17 | { 18 | input: '>>>+{++}', 19 | output: 3, 20 | }, 21 | { 22 | input: '+{[-]+}', 23 | output: 1, 24 | }, 25 | { 26 | input: '-[+>]++', 27 | output: 2, 28 | }, 29 | { 30 | input: '-[+{++}]++{[-]}++', 31 | output: 2, 32 | }, 33 | { 34 | input: '{+}{+}{+}', 35 | output: 0, 36 | }, 37 | { 38 | input: '', 39 | output: 0, 40 | }, 41 | { 42 | input: '+++{[-]+++[-]+}', 43 | output: 1, 44 | }, 45 | { 46 | input: '{>++>++}', 47 | output: 0, 48 | }, 49 | { 50 | input: '++++[-->]>++', 51 | output: 2, 52 | }, 53 | ]; 54 | 55 | it('should return a number', () => { 56 | const result = execute('+++'); 57 | expect(typeof result).toBe('number'); 58 | }); 59 | 60 | it.each(TEST_CASES)('should return $output', ({ input, output }) => { 61 | const result = execute(input); 62 | expect(result).toBe(output); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /2024/26-calcula-el-porcentaje-completado/index.js: -------------------------------------------------------------------------------- 1 | function getCompleted(timeWorked, totalTime) { 2 | const timeToSeconds = (time) => { 3 | const [hours, minutes, seconds] = time.split(':').map(Number); 4 | return hours * 3600 + minutes * 60 + seconds; 5 | }; 6 | 7 | const partSeconds = timeToSeconds(timeWorked); 8 | const totalSeconds = timeToSeconds(totalTime); 9 | 10 | return `${Math.round((partSeconds / totalSeconds) * 100)}%`; 11 | } 12 | 13 | module.exports = getCompleted; 14 | -------------------------------------------------------------------------------- /2024/26-calcula-el-porcentaje-completado/index.test.js: -------------------------------------------------------------------------------- 1 | const getCompleted = require('./index'); 2 | 3 | describe('26 => Calcula-el-porcentaje-completado', () => { 4 | const TEST_CASES = [ 5 | { 6 | input: ['01:00:00', '03:00:00'], 7 | output: '33%', 8 | }, 9 | { 10 | input: ['02:00:00', '04:00:00'], 11 | output: '50%', 12 | }, 13 | { 14 | input: ['02:00:00', '04:00:00'], 15 | output: '50%', 16 | }, 17 | { 18 | input: ['01:00:00', '01:00:00'], 19 | output: '100%', 20 | }, 21 | { 22 | input: ['00:10:00', '01:00:00'], 23 | output: '17%', 24 | }, 25 | { 26 | input: ['01:10:10', '03:30:30'], 27 | output: '33%', 28 | }, 29 | { 30 | input: ['03:30:30', '05:50:50'], 31 | output: '60%', 32 | }, 33 | { 34 | input: ['00:00:00', '01:00:00'], 35 | output: '0%', 36 | }, 37 | { 38 | input: ['00:00:01', '00:00:02'], 39 | output: '50%', 40 | }, 41 | { 42 | input: ['23:59:59', '24:00:00'], 43 | output: '100%', 44 | }, 45 | ]; 46 | 47 | it('should return a string', () => { 48 | const result = getCompleted(...TEST_CASES[0].input); 49 | expect(typeof result).toBe('string'); 50 | }); 51 | 52 | it.each(TEST_CASES)('should return $output', ({ input, output }) => { 53 | const result = getCompleted(...input); 54 | expect(result).toBe(output); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Marco Antonio Cruz Gabino marco24cruz08@gmail.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /create.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # script for creating a new problem in a directory with the name of the problem 4 | # and the files index.js, index.test.js, and README.md 5 | echo '📁 Which directory do you want to create the solution? ' 6 | read directory 7 | 8 | # validate if the directory exists, if not, create it 9 | if [ ! -d "$directory" ]; then 10 | echo '📁 The directory does not exist, do you want to create it? (y/n)' 11 | read createDirectory 12 | if [ "$createDirectory" = "y" ]; then 13 | mkdir "$directory" 14 | else 15 | echo '❌ You must create the directory' 16 | exit 1 17 | fi 18 | fi 19 | 20 | cd "$directory" 21 | 22 | echo '🎄 What is the name of the problem? ' 23 | read problem 24 | 25 | 26 | directoryName=$(echo "$problem" | tr ' ' '-') 27 | mkdir "$directoryName" 28 | 29 | cd "$directoryName" 30 | 31 | touch "index.js" 32 | touch "index.test.js" 33 | touch "README.md" 34 | 35 | problemNumber="${problem:0:2}" 36 | 37 | # primera letra en mayuscula 38 | problemName="${problem:3}" 39 | problemName="${problemName^}" 40 | 41 | echo "# Reto $problemNumber: $problemName" >> README.md 42 | echo "" >> README.md 43 | echo "## Problema" >> README.md 44 | echo "describe('$problemNumber => $problemName', () => { 45 | const testCases = []; 46 | 47 | it('should return a ', () => {}); 48 | 49 | it.each(testCases)('', (testCase) => {}); 50 | });" >> index.test.js 51 | 52 | echo '✅ The problem has been created' 53 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "adventjs-solutions", 3 | "version": "1.0.0", 4 | "description": "Solutions for AdventJS 2022", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest", 8 | "test:2021": "jest --testPathPattern=2021", 9 | "test:2022": "jest --testPathPattern=2022", 10 | "test:2023": "jest --testPathPattern=2023", 11 | "test:2024": "jest --testPathPattern=2024", 12 | "lint": "eslint **/*.js", 13 | "lint:fix": "eslint **/*.js --fix", 14 | "prepare": "husky install", 15 | "postinstall": "npm run prepare" 16 | }, 17 | "prettier": { 18 | "singleQuote": true, 19 | "trailingComma": "all" 20 | }, 21 | "keywords": [ 22 | "midudev", 23 | "javascript", 24 | "adventjs", 25 | "adventjs-2022", 26 | "adventjs-2022-solutions", 27 | "adventjs-2022-solutions-javascript", 28 | "adventjs-2023", 29 | "adventjs-2023-solutions", 30 | "adventjs-2023-solutions-javascript" 31 | ], 32 | "author": "marcode24", 33 | "license": "MIT", 34 | "devDependencies": { 35 | "eslint": "^8.30.0", 36 | "eslint-config-airbnb-base": "^15.0.0", 37 | "eslint-plugin-import": "^2.26.0", 38 | "husky": "^8.0.3", 39 | "jest": "^29.3.1", 40 | "lint-staged": "^13.1.2" 41 | }, 42 | "repository": { 43 | "type": "git", 44 | "url": "git+https://github.com/marcode24/adventjs-solutions" 45 | }, 46 | "bugs": { 47 | "url": "git+https://github.com/marcode24/adventjs-solutions/issues" 48 | }, 49 | "homepage": "https://github.com/marcode24/adventjs-solutions#README.md" 50 | } 51 | --------------------------------------------------------------------------------