Skip to content

✨ Add challenge-20 solution #36

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
251 changes: 251 additions & 0 deletions 2024/20-encuentra-los-regalos-faltantes-y-duplicados/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
# Reto 20: Encuentra-los-regalos-faltantes-y-duplicados

**Santa Claus** 🎅 está revisando la lista de regalos que debe entregar esta Navidad. Sin embargo, **algunos regalos faltan, otros están duplicados, y algunos tienen cantidades incorrectas.** Necesita tu ayuda para resolver el problema.

Recibirás dos arrays:

- `received`: Lista con los regalos que Santa tiene actualmente.
- `expected`: Lista con los regalos que Santa debería tener.

Tu tarea es escribir una función que, dado received y expected, devuelva un objeto con dos propiedades:

- `missing`: Un objeto donde las claves son los nombres de los regalos faltantes y los valores son las cantidades que faltan.
- `extra`: Un objeto donde las claves son los nombres de los regalos extra o duplicados y los valores son las cantidades que sobran.

Ten en cuenta que:

- Los regalos pueden repetirse en ambas listas.
- Las listas de regalos están desordenadas.
- Si no hay regalos que falten o sobren, las propiedades correspondientes (`missing o extra`) deben ser objetos vacíos.

```js
fixGiftList(['puzzle', 'car', 'doll', 'car'], ['car', 'puzzle', 'doll', 'ball'])
// Devuelve:
// {
// missing: { ball: 1 },
// extra: { car: 1 }
// }

fixGiftList(
['book', 'train', 'kite', 'train'],
['train', 'book', 'kite', 'ball', 'kite']
)
// Devuelve:
// {
// missing: { ball: 1, kite: 1 },
// extra: { train: 1 }
// }

fixGiftList(
['bear', 'bear', 'car'],
['bear', 'car', 'puzzle', 'bear', 'car', 'car']
)
// Devuelve:
// {
// missing: { puzzle: 1, car: 2 },
// extra: {}
// }

fixGiftList(['bear', 'bear', 'car'], ['car', 'bear', 'bear'])
// Devuelve:
// {
// missing: {},
// extra: {}
// }
```

## Mi solución explicada

```js
function fixGiftList(received, expected) {
const counts = {};

for (const gift of received) counts[gift] = ~~counts[gift] + 1;
for (const gift of expected) counts[gift] = ~~counts[gift] - 1;

const missing = {};
const extra = {};
for (const [gift, count] of Object.entries(counts)) {
if (count > 0) extra[gift] = count;
else if (count < 0) missing[gift] = -count;
}

return { missing, extra };
}

module.exports = fixGiftList;
```

Para resolver este reto, hay que tener en cuenta que necesitamos comparar dos listas de regalos: `received` y `expected`. Para ello, vamos a recorrer ambas listas y vamos a ir contando cuántas veces aparece cada regalo en cada una de ellas.

Para llevar la cuenta de los regalos, vamos a utilizar un objeto llamado `counts`. Este objeto va a tener como claves los nombres de los regalos y como valores la cantidad de veces que aparece cada regalo en la lista.

Como no sabemos si un regalo se repite en ambas listas, vamos a recorrer la lista `received` y vamos a incrementar en 1 la cantidad de veces que aparece cada regalo en el objeto `counts`. Luego, vamos a recorrer la lista `expected` y vamos a decrementar en 1 la cantidad de veces que aparece cada regalo en el objeto `counts`. De esta forma, si un regalo aparece en ambas listas, la cantidad de veces que aparece en el objeto `counts` será 0. Si un regalo aparece más veces en `received` que en `expected`, la cantidad será positiva. Si un regalo aparece más veces en `expected` que en `received`, la cantidad será negativa. Si un regalo no aparece en ninguna de las listas, la cantidad será 0.

Una vez que tenemos el objeto `counts` con la cantidad de veces que aparece cada regalo en ambas listas, vamos a recorrer este objeto y vamos a crear dos objetos: `missing` y `extra`. En el objeto `missing` vamos a guardar los regalos que faltan y en el objeto `extra` vamos a guardar los regalos que sobran. Para ello, vamos a recorrer el objeto `counts` y vamos a verificar si la cantidad de veces que aparece un regalo es mayor que 0. Si es así, significa que ese regalo sobra y lo agregamos al objeto `extra`. Si la cantidad es menor que 0, significa que ese regalo falta y lo agregamos al objeto `missing`.

Finalmente, devolvemos un objeto con las propiedades `missing` y `extra`, que contienen los regalos que faltan y sobran, respectivamente.

**Veamos con un ejemplo cómo funciona el código:**

Supongamos que recibimos las listas `received` y `expected` siguientes:

```js
const received = ['car', 'puzzle', 'car'];
const expected = ['puzzle', 'car', 'doll'];
```

Utilizaremos un ciclo `for...of` para recorrer la lista `received` y vamos a incrementar en 1 la cantidad de veces que aparece cada regalo en el objeto `counts`.

Recorremos la lista `received` y como esta lista tiene 3 elementos, el ciclo se ejecutará 3 veces. En la primera iteración, el regalo es `'car'`. Como el objeto `counts` está vacío, la cantidad de veces que aparece el regalo `'car'` es `undefined`. Al utilizar el operador `~~` para convertir el valor a un número entero, obtenemos `0`. Luego, incrementamos en 1 la cantidad de veces que aparece el regalo `'car'` en el objeto `counts`. Ahora, el objeto `counts` es `{ car: 1 }`.

```js
// counts[gift] = ~~counts[gift] + 1;
// counts['car'] = ~~counts['car'] + 1;
// counts['car'] = ~~undefined + 1;
// counts['car'] = 0 + 1;
counts['car'] = 1;

const counts = { car: 1 };
```

En la segunda iteración tenemos el regalo `'puzzle'`. Al igual que en la iteración anterior, la cantidad de veces que aparece el regalo `'puzzle'` es `undefined`. Al convertir este valor a un número entero, obtenemos `0`. Luego, incrementamos en 1 la cantidad de veces que aparece el regalo `'puzzle'` en el objeto `counts`. Ahora, el objeto `counts` es `{ car: 1, puzzle: 1 }`.

```js
// counts[gift] = ~~counts[gift] + 1;
// counts['puzzle'] = ~~counts['puzzle'] + 1;
// counts['puzzle'] = ~~undefined + 1;
// counts['puzzle'] = 0 + 1;
counts['puzzle'] = 1;

const counts = { car: 1, puzzle: 1 };
```

En la tercera y última iteración, el regalo es `'car'`. La cantidad de veces que aparece el regalo `'car'` en el objeto `counts` es `1`. Al incrementar en 1 esta cantidad, obtenemos `2`. Ahora, el objeto `counts` es `{ car: 2, puzzle: 1 }`.

```js
// counts[gift] = ~~counts[gift] + 1;
// counts['car'] = ~~counts['car'] + 1;
// counts['car'] = ~~1 + 1;
// counts['car'] = 1 + 1;
counts['car'] = 2;

const counts = { car: 2, puzzle: 1 };
```

Luego, recorremos la lista `expected` y vamos a decrementar en 1 la cantidad de veces que aparece cada regalo en el objeto `counts`.

Recorremos la lista `expected` y como esta lista tiene 3 elementos, el ciclo se ejecutará 3 veces. En la primera iteración, el regalo es `'puzzle'`. La cantidad de veces que aparece el regalo `'puzzle'` en el objeto `counts` es `1`. Al decrementar en 1 esta cantidad, obtenemos `0`. Ahora, el objeto `counts` es `{ car: 2, puzzle: 0 }`.

```js
// counts[gift] = ~~counts[gift] - 1;
// counts['puzzle'] = ~~counts['puzzle'] - 1;
// counts['puzzle'] = ~~1 - 1;
// counts['puzzle'] = 1 - 1;
counts['puzzle'] = 0;

const counts = { car: 2, puzzle: 0 };
```

En la segunda iteración tenemos el regalo `'car'`. La cantidad de veces que aparece el regalo `'car'` en el objeto `counts` es `2`. Al decrementar en 1 esta cantidad, obtenemos `1`. Ahora, el objeto `counts` es `{ car: 1, puzzle: 0 }`.

```js
// counts[gift] = ~~counts[gift] - 1;
// counts['car'] = ~~counts['car'] - 1;
// counts['car'] = ~~2 - 1;
// counts['car'] = 2 - 1;
counts['car'] = 1;

const counts = { car: 1, puzzle: 0 };
```

En la tercera y última iteración, el regalo es `'doll'`. La cantidad de veces que aparece el regalo `'doll'` en el objeto `counts` es `undefined`. Al convertir este valor a un número entero, obtenemos `0`. Al decrementar en 1 esta cantidad, obtenemos `-1`. Ahora, el objeto `counts` es `{ car: 1, puzzle: 0, doll: -1 }`.

```js
// counts[gift] = ~~counts[gift] - 1;
// counts['doll'] = ~~counts['doll'] - 1;
// counts['doll'] = ~~undefined - 1;
// counts['doll'] = 0 - 1;
counts['doll'] = -1;

const counts = { car: 1, puzzle: 0, doll: -1 };
```

Una vez que tenemos el objeto `counts` con la cantidad de veces que aparece cada regalo en ambas listas, vamos a recorrer este objeto y vamos a crear dos objetos: `missing` y `extra`.

Nuevamente, utilizaremos un ciclo `for...of` para recorrer el objeto `counts`. Aquí utilizaremos el método `Object.entries()` para obtener un array con las claves y valores del objeto `counts`. Luego, recorreremos este array con un ciclo `for...of` y vamos a verificar si la cantidad de veces que aparece un regalo es mayor que 0. Si es así, significa que ese regalo sobra y lo agregamos al objeto `extra`. Si la cantidad es menor que 0, significa que ese regalo falta y lo agregamos al objeto `missing`.

Entonces para el objeto `counts` que tenemos, el array que obtenemos con `Object.entries(counts)` es el siguiente:

```js
const entries = [
['car', 1],
['puzzle', 0],
['doll', -1]
];
```

En la primera iteración, la clave es `'car'` y el valor es `1`. Como el valor es mayor que 0, agregamos el regalo `'car'` al objeto `extra` con la cantidad `1`.

```js
gift = 'car';
count = 1;

// if (count > 0) extra[gift] = count;
// if (1 > 0) extra['car'] = 1;
// if (true) extra['car'] = 1;
extra['car'] = 1;

const extra = { car: 1 };
const missing = {};
```

En la segunda iteración, la clave es `'puzzle'` y el valor es `0`. Como el valor es igual a 0, no hacemos nada.

```js
gift = 'puzzle';
count = 0;

// if (count > 0) extra[gift] = count;
// if (0 > 0) extra['puzzle'] = 0;
// if (false) extra['puzzle'] = 0; (no se ejecuta)

// else if (count < 0) missing[gift] = -count;
// else if (0 < 0) missing['puzzle'] = 0;
// else if (false) missing['puzzle'] = 0; (no se ejecuta)

const extra = { car: 1 };
const missing = {};
```

En la tercera y última iteración, la clave es `'doll'` y el valor es `-1`. Como el valor es menor que 0, agregamos el regalo `'doll'` al objeto `missing` con la cantidad `1`.

```js
gift = 'doll';
count = -1;

// if (count > 0) extra[gift] = count;
// if (-1 > 0) extra['doll'] = -1;
// if (false) extra['doll'] = -1; (no se ejecuta)

// else if (count < 0) missing[gift] = -count;
// else if (-1 < 0) missing['doll'] = -(-1);
// else if (true) missing['doll'] = 1;
missing['doll'] = 1;

const extra = { car: 1 };
const missing = { doll: 1 };
```

Finalmente, devolvemos un objeto con las propiedades `missing` y `extra`, que contienen los regalos que faltan y sobran, respectivamente.

```js
{
missing: { doll: 1 },
extra: { car: 1 }
}
```

En este caso, el regalo `'doll'` falta y el regalo `'car'` sobra.

Y con esto hemos terminado de resolver el reto. 🎉
19 changes: 19 additions & 0 deletions 2024/20-encuentra-los-regalos-faltantes-y-duplicados/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/* eslint-disable no-bitwise */
/* eslint-disable no-restricted-syntax */
function fixGiftList(received, expected) {
const counts = {};

for (const gift of received) counts[gift] = ~~counts[gift] + 1;
for (const gift of expected) counts[gift] = ~~counts[gift] - 1;

const missing = {};
const extra = {};
for (const [gift, count] of Object.entries(counts)) {
if (count > 0) extra[gift] = count;
else if (count < 0) missing[gift] = -count;
}

return { missing, extra };
}

module.exports = fixGiftList;
109 changes: 109 additions & 0 deletions 2024/20-encuentra-los-regalos-faltantes-y-duplicados/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
const fixGiftList = require('./index');

describe('20 => Encuentra-los-regalos-faltantes-y-duplicados', () => {
const TEST_CASES = [
{
input: [
['puzzle', 'car'],
['puzzle', 'car', 'ball'],
],
output: {
missing: {
ball: 1,
},
extra: {},
},
},
{
input: [
['car', 'puzzle', 'car'],
['puzzle', 'car', 'doll'],
],
output: {
missing: {
doll: 1,
},
extra: {
car: 1,
},
},
},
{
input: [
['train', 'book', 'kite'],
['train', 'kite', 'book', 'kite'],
],
output: {
missing: {
kite: 1,
},
extra: {},
},
},
{
input: [
['bear', 'car'],
['bear', 'car', 'car'],
],
output: {
missing: {
car: 1,
},
extra: {},
},
},
{
input: [[], ['bear', 'car', 'car']],
output: {
missing: {
bear: 1,
car: 2,
},
extra: {},
},
},
{
input: [
['puzzle', 'puzzle', 'car'],
['puzzle', 'car'],
],
output: {
missing: {},
extra: {
puzzle: 1,
},
},
},
{
input: [['car'], ['car', 'puzzle', 'ball']],
output: {
missing: {
puzzle: 1,
ball: 1,
},
extra: {},
},
},
{
input: [
['bear', 'bear', 'car'],
['bear', 'bear', 'car'],
],
output: {
missing: {},
extra: {},
},
},
];

it('should return an object', () => {
const testCase = TEST_CASES[0];
const received = fixGiftList(...testCase.input);
expect(received).toEqual(expect.any(Object));
});

it.each(TEST_CASES)('should return the correct object', (testCase) => {
const received = fixGiftList(...testCase.input);
expect(received).toEqual(testCase.output);
});
});
Loading
Loading