|
| 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 🎉. |
0 commit comments