Skip to content

Commit 7fb3f84

Browse files
authored
Merge pull request #30 from marcode24/2024-12
✨ Add challenge-12 solution
2 parents d2ed47b + 8bbe553 commit 7fb3f84

File tree

4 files changed

+276
-0
lines changed

4 files changed

+276
-0
lines changed
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
# Reto 12: Cuanto-cuesta-el-arbol
2+
3+
Estás en un mercado muy especial en el que se venden árboles de Navidad 🎄. Cada uno viene decorado con una serie de adornos muy peculiares, y el precio del árbol se determina en función de los adornos que tiene.
4+
5+
- `*`: Copo de nieve - Valor: 1
6+
- `o`: Bola de Navidad - Valor: 5
7+
- `^`: Arbolito decorativo - Valor: 10
8+
- `#`: Guirnalda brillante - Valor: 50
9+
- `@`: Estrella polar - Valor: 100
10+
Normalmente se sumarían todos los valores de los adornos y ya está…
11+
12+
Pero, ¡ojo! **Si un adorno se encuentra inmediatamente a la izquierda de otro de mayor valor, en lugar de sumar, se resta su valor.**
13+
14+
```js
15+
calculatePrice('***') // 3 (1 + 1 + 1)
16+
calculatePrice('*o') // 4 (5 - 1)
17+
calculatePrice('o*') // 6 (5 + 1)
18+
calculatePrice('*o*') // 5 (-1 + 5 + 1)
19+
calculatePrice('**o*') // 6 (1 - 1 + 5 + 1)
20+
calculatePrice('o***') // 8 (5 + 3)
21+
calculatePrice('*o@') // 94 (-5 - 1 + 100)
22+
calculatePrice('*#') // 49 (-1 + 50)
23+
calculatePrice('@@@') // 300 (100 + 100 + 100)
24+
calculatePrice('#@') // 50 (-50 + 100)
25+
calculatePrice('#@Z') // undefined (Z es desconocido)
26+
```
27+
28+
## Mi solución explicada
29+
30+
```js
31+
function calculatePrice(ornaments) {
32+
const ornamentValues = {
33+
'*': 1,
34+
'o': 5,
35+
'^': 10,
36+
'#': 50,
37+
'@': 100,
38+
};
39+
40+
if (!/^[*o^#@]*$/.test(ornaments)) return undefined;
41+
42+
return [...ornaments].reduce((totalPrice, current, index) => {
43+
const currentValue = ornamentValues[current];
44+
const nextValue = ornamentValues[ornaments[index + 1]];
45+
46+
const isNextGreater = nextValue > currentValue;
47+
const valueToAdd = isNextGreater ? -currentValue : currentValue;
48+
49+
return totalPrice + valueToAdd;
50+
}, 0);
51+
}
52+
53+
```
54+
55+
Para resolver este reto, primero definimos un objeto `ornamentValues` que contiene los valores de cada adorno.
56+
57+
```js
58+
const ornamentValues = {
59+
'*': 1,
60+
'o': 5,
61+
'^': 10,
62+
'#': 50,
63+
'@': 100,
64+
};
65+
```
66+
67+
Este objeto nos permitirá obtener el valor de cada adorno de forma más sencilla. Por ejemplo, `ornamentValues['*']` nos devolverá `1`.
68+
69+
Luego, verificamos si la cadena de adornos contiene solo los caracteres válidos. Si no es así, devolvemos `undefined`. Esto lo realizamos primero para no realizar cálculos innecesarios si la cadena no es válida.
70+
71+
La expresión regular `/^[*o^#@]*$/` verifica que la cadena contenga solo los caracteres `*`, `o`, `^`, `#` y `@`.
72+
73+
Desmenuzando la expresión regular:
74+
75+
- `^`: Indica que la cadena debe comenzar con uno de los caracteres dentro de los corchetes.
76+
- `[*o^#@]`: Indica que la cadena debe contener uno de los caracteres dentro de los corchetes.
77+
- `*`: Indica que el carácter anterior puede aparecer 0 o más veces.
78+
- `$`: Indica que la cadena debe terminar con uno de los caracteres dentro de los corchetes.
79+
80+
```js
81+
if (!/^[*o^#@]*$/.test(ornaments)) return undefined;
82+
```
83+
84+
Después, usamos el método `reduce` para iterar sobre cada adorno. En cada iteración, obtenemos el valor del adorno actual y el siguiente. Luego, comparamos si el valor del siguiente adorno es mayor que el actual. Si es así, restamos el valor actual, de lo contrario, sumamos el valor actual.
85+
86+
Finalmente, devolvemos el precio total de los adornos.
87+
88+
**Veamos con un ejemplo cómo funciona:**
89+
90+
Supongamos que tenemos la siguiente entrada: `'*o*'`, que nos tendría que devolver `5`.
91+
92+
```js
93+
calculatePrice('*o*') // 5
94+
```
95+
96+
Primero validamos que la cadena sea válida
97+
98+
```js
99+
if (!/^[*o^#@]*$/.test('*o*')) // true
100+
```
101+
102+
Como la cadena es válida, continuamos con el cálculo.
103+
104+
En la primera iteración, tenemos lo siguiente:
105+
106+
Tengamos presente que ornaments es `'*o*'`.
107+
108+
```js
109+
// const currentValue = ornamentValues[current];
110+
// const currentValue = ornamentValues['*'];
111+
const currentValue = 1;
112+
113+
// const nextValue = ornamentValues[ornaments[index + 1]];
114+
// const nextValue = ornamentValues[ornaments[0 + 1]];
115+
// const nextValue = ornamentValues[ornaments[1]];
116+
const nextValue = ornamentValues['o'];
117+
118+
// const isNextGreater = nextValue > currentValue;
119+
// const isNextGreater = 5 > 1;
120+
const isNextGreater = true;
121+
122+
// const valueToAdd = isNextGreater ? -currentValue : currentValue;
123+
// const valueToAdd = true ? -1 : 1;
124+
const valueToAdd = -1;
125+
126+
// return totalPrice + valueToAdd;
127+
// return 0 + -1;
128+
return -1;
129+
```
130+
131+
En la segunda iteración, tenemos lo siguiente:
132+
133+
```js
134+
// const currentValue = ornamentValues[current];
135+
// const currentValue = ornamentValues['o'];
136+
const currentValue = 5;
137+
138+
// const nextValue = ornamentValues[ornaments[index + 1]];
139+
// const nextValue = ornamentValues[ornaments[1 + 1]];
140+
// const nextValue = ornamentValues[ornaments[2]];
141+
const nextValue = ornamentValues['*'];
142+
143+
// const isNextGreater = nextValue > currentValue;
144+
// const isNextGreater = 1 > 5;
145+
const isNextGreater = false;
146+
147+
// const valueToAdd = isNextGreater ? -currentValue : currentValue;
148+
// const valueToAdd = false ? -5 : 5;
149+
const valueToAdd = 5;
150+
151+
// return totalPrice + valueToAdd;
152+
// return -1 + 5;
153+
return 4;
154+
```
155+
156+
Para la tercera iteración, tenemos lo siguiente:
157+
158+
```js
159+
// const currentValue = ornamentValues[current];
160+
// const currentValue = ornamentValues['*'];
161+
const currentValue = 1;
162+
163+
// const nextValue = ornamentValues[ornaments[index + 1]];
164+
// const nextValue = ornamentValues[ornaments[2 + 1]];
165+
// const nextValue = ornamentValues[ornaments[3]];
166+
const nextValue = ornamentValues[undefined];
167+
168+
// como ornaments[undefined] es undefined, nextValue es undefined
169+
170+
// const isNextGreater = nextValue > currentValue;
171+
// const isNextGreater = undefined > 1;
172+
const isNextGreater = false; // undefined se convierte en 0
173+
174+
// const valueToAdd = isNextGreater ? -currentValue : currentValue;
175+
// const valueToAdd = false ? -1 : 1;
176+
const valueToAdd = 1;
177+
178+
// return totalPrice + valueToAdd;
179+
// return 4 + 1;
180+
return 5;
181+
```
182+
183+
Como no hay más adornos, la función termina.
184+
185+
Finalmente, obtenemos el resultado esperado.
186+
187+
```js
188+
calculatePrice('*o*') // 5
189+
```
190+
191+
**Veamos un caso donde la cadena no es válida:**
192+
193+
```js
194+
calculatePrice('#@Z') // undefined
195+
```
196+
197+
Primero validamos que la cadena sea válida
198+
199+
```js
200+
if (!/^[*o^#@]*$/.test('#@Z')) // false
201+
```
202+
203+
Al tener un carácter no válido (`Z`), la expresión regular devuelve `false`.
204+
205+
Como la cadena no es válida, devolvemos `undefined`.
206+
207+
```js
208+
return undefined;
209+
```
210+
211+
Y eso es todo. Hemos resuelto el reto 🎉.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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+
});

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ npm run test 'year'/'challenge'/index.test.js
7070
| 09 | [🚂 El tren mágico](https://adventjs.dev/es/challenges/2024/9) | 🟡 | [here](./2024/09-el-tren-magico/index.js) | ⭐⭐⭐⭐⭐ |
7171
| 10 | [🧑‍💻 El ensamblador élfico](https://adventjs.dev/es/challenges/2024/10) | 🟡 | [here](./2024/10-el-ensamblador-elfico/index.js) | ⭐⭐⭐⭐⭐ |
7272
| 11 | [📂 Nombres de archivos codificados](https://adventjs.dev/es/challenges/2024/11) | 🟢 | [here](./2024/11-nombres-de-archivos-codificados/index.js) | ⭐⭐⭐⭐⭐ |
73+
| 12 | [🎄 ¿Cuánto cuesta el árbol?](https://adventjs.dev/es/challenges/2024/12) | 🟢 | [here](./2024/12-cuanto-cuesta-el-arbol/index.js) | ⭐⭐⭐⭐⭐ |
7374

7475
Difficulties legend:
7576
🟢 Easy 🟡 Medium 🔴 Hard

0 commit comments

Comments
 (0)