Files

238 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id: 5956795bc9e2c415eb244de1
title: União de hashes
challengeType: 5
forumTopicId: 302284
dashedName: hash-join
---
# --description--
Uma [inner join](https://www.freecodecamp.org/news/sql-join-types-inner-join-vs-outer-join-example/#how-to-use-an-inner-join-in-sql "news: SQL Join Types Inner Join VS Outer Join Example#How to Use an INNER JOIN in SQL") (união interna) é uma operação que combina duas tabelas de dados em uma tabela, com base em valores de coluna correspondentes. A maneira mais simples de implementar essa operação é o algoritmo de [união de laços aninhados](https://en.wikipedia.org/wiki/Nested loop join "wp: Nested loop join"), mas uma alternativa mais escalável é o algoritmo de [união de hashes](https://en.wikipedia.org/wiki/hash join "wp: hash join").
O algoritmo de união de hashes (ou "hash join") consiste em duas etapas:
<ol>
<li><strong>Fase de hash:</strong> criar um <a href='https://en.wikipedia.org/wiki/Multimap' title='wp: Multimap' target='_blank'>multimapa</a> de uma das tabelas, mapear a partir do valor de cada coluna de join para todas as linhas que a contêm.</li>
<ul>
<li>O multimapa deve suportar uma pesquisa baseada em hash, que seja melhor dimensionada do que uma simples pesquisa linear, pois esse é o sentido desse algoritmo.</li>
<li>Idealmente, devemos criar o multimapa para a tabela menor, assim minimizando seu tempo de criação e o tamanho da memória.</li>
</ul>
<li><strong>Fase de união:</strong> escaneie a outra tabela e encontre linhas correspondentes buscando no multimapa criado anteriormente.</li>
</ol>
Em pseudocódigo, o algoritmo poderia ser expresso da seguinte forma:
<pre><strong>permita que</strong> <i>A</i> = a primeira tabela de entrada (ou idealmente, a maior)
<strong>permita que</strong> <i>B</i> = a segunda tabela de entrada (ou idealmente, a menor)
<strong>permita que</strong> <i>j<sub>A</sub></i> = o ID da coluna de união da tabela <i>A</i>
<strong>permita que</strong> <i>j<sub>B</sub></i> = o ID da coluna de união da tabela <i>B</i>
<strong>permita que</strong> <i>M<sub>B</sub></i> = um multimapa para o mapeamento de valores únicos para múltiplas linhas da tabela <i>B</i> (começa vazio)
<strong>permita que</strong> <i>C</i> = a tabela de saída (começa vazia)
<strong>para cada</strong> linha <i>b</i> na tabela <i>B</i>:
<strong>coloque</strong> <i>b</i> no multimapa <i>M<sub>B</sub></i> com a chave <i>b(j<sub>B</sub>)</i>
<strong>para cada</strong> linha <i>a</i> na tabela <i>A</i>:
<strong>para cada</strong> linha <i>b</i> no multimapa <i>M<sub>B</sub></i> com a chave <i>a(j<sub>A</sub>)</i>:
<strong>permita que</strong> <i>c</i> = a concatenação da linha <i>a</i> e da linha <i>b</i>
<strong>coloque</strong> a linha <i>c</i> na tabela <i>C</i>
</pre>
# --instructions--
Implemente o algoritmo de "hash join" como uma função e demonstre que ele passa pelo caso de teste listado abaixo. A função deve aceitar dois arrays de objetos e retornar um array de objetos combinados.
**Entrada**
<table>
<tr>
<td style="padding: 4px; margin: 5px;">
<table style="border:none; border-collapse:collapse;">
<tr>
<td style="border:none"><i>A =</i></td>
<td style="border:none">
<table>
<tr>
<th style="padding: 4px; margin: 5px;">Idade</th>
<th style="padding: 4px; margin: 5px;">Nome</th>
</tr>
<tr>
<td style="padding: 4px; margin: 5px;">27</td>
<td style="padding: 4px; margin: 5px;">Jonah</td>
</tr>
<tr>
<td style="padding: 4px; margin: 5px;">18</td>
<td style="padding: 4px; margin: 5px;">Alan</td>
</tr>
<tr>
<td style="padding: 4px; margin: 5px;">28</td>
<td style="padding: 4px; margin: 5px;">Glory</td>
</tr>
<tr>
<td style="padding: 4px; margin: 5px;">18</td>
<td style="padding: 4px; margin: 5px;">Popeye</td>
</tr>
<tr>
<td style="padding: 4px; margin: 5px;">28</td>
<td style="padding: 4px; margin: 5px;">Alan</td>
</tr>
</table>
</td>
<td style="border:none; padding-left:1.5em;" rowspan="2"></td>
<td style="border:none"><i>B =</i></td>
<td style="border:none">
<table>
<tr>
<th style="padding: 4px; margin: 5px;">Personagem</th>
<th style="padding: 4px; margin: 5px;">Inimigo</th>
</tr>
<tr>
<td style="padding: 4px; margin: 5px;">Jonah</td>
<td style="padding: 4px; margin: 5px;">Whales</td>
</tr>
<tr>
<td style="padding: 4px; margin: 5px;">Jonah</td>
<td style="padding: 4px; margin: 5px;">Spiders</td>
</tr>
<tr>
<td style="padding: 4px; margin: 5px;">Alan</td>
<td style="padding: 4px; margin: 5px;">Ghosts</td>
</tr>
<tr>
<td style="padding: 4px; margin: 5px;">Alan</td>
<td style="padding: 4px; margin: 5px;">Zombies</td>
</tr>
<tr>
<td style="padding: 4px; margin: 5px;">Glory</td>
<td style="padding: 4px; margin: 5px;">Buffy</td>
</tr>
</table>
</td>
</tr>
<tr>
<td style="border:none">
<i>j<sub>A</sub> =</i>
</td>
<td style="border:none">
<i><code>Name</code> (ou seja, a coluna 1)</i>
</td>
<td style="border:none">
<i>j<sub>B</sub> =</i>
</td>
<td style="border:none">
<i><code>Character</code> (ou seja, a coluna 0)</i>
</td>
</tr>
</table>
</td>
</tr>
</table>
**Saída**
| A_age | A_name | B_character | B_nemesis |
| ----- | ------ | ----------- | --------- |
| 27 | Jonah | Jonah | Whales |
| 27 | Jonah | Jonah | Spiders |
| 18 | Alan | Alan | Ghosts |
| 18 | Alan | Alan | Zombies |
| 28 | Glory | Glory | Buffy |
| 28 | Alan | Alan | Ghosts |
| 28 | Alan | Alan | Zombies |
A ordem das linhas na tabela de saída não é significativa.
# --hints--
`hashJoin` deve ser uma função.
```js
assert(typeof hashJoin === 'function');
```
`hashJoin([{ age: 27, name: "Jonah" }, { age: 18, name: "Alan" }, { age: 28, name: "Glory" }, { age: 18, name: "Popeye" }, { age: 28, name: "Alan" }], [{ character: "Jonah", nemesis: "Whales" }, { character: "Jonah", nemesis: "Spiders" }, { character: "Alan", nemesis: "Ghosts" }, { character:"Alan", nemesis: "Zombies" }, { character: "Glory", nemesis: "Buffy" }, { character: "Bob", nemesis: "foo" }])` deve retornar `[{"A_age": 27,"A_name": "Jonah", "B_character": "Jonah", "B_nemesis": "Whales"}, {"A_age": 27,"A_name": "Jonah", "B_character": "Jonah", "B_nemesis": "Spiders"}, {"A_age": 18,"A_name": "Alan", "B_character": "Alan", "B_nemesis": "Ghosts"}, {"A_age": 18,"A_name": "Alan", "B_character": "Alan", "B_nemesis": "Zombies"}, {"A_age": 28,"A_name": "Glory", "B_character": "Glory", "B_nemesis": "Buffy"}, {"A_age": 28,"A_name": "Alan", "B_character": "Alan", "B_nemesis": "Ghosts"}, {"A_age": 28,"A_name": "Alan", "B_character": "Alan", "B_nemesis": "Zombies"}]`
```js
assert.deepEqual(hashJoin(hash1, hash2), res);
```
# --seed--
## --after-user-code--
```js
const hash1 = [
{ age: 27, name: 'Jonah' },
{ age: 18, name: 'Alan' },
{ age: 28, name: 'Glory' },
{ age: 18, name: 'Popeye' },
{ age: 28, name: 'Alan' }
];
const hash2 = [
{ character: 'Jonah', nemesis: 'Whales' },
{ character: 'Jonah', nemesis: 'Spiders' },
{ character: 'Alan', nemesis: 'Ghosts' },
{ character: 'Alan', nemesis: 'Zombies' },
{ character: 'Glory', nemesis: 'Buffy' },
{ character: 'Bob', nemesis: 'foo' }
];
const res = [
{ A_age: 27, A_name: 'Jonah', B_character: 'Jonah', B_nemesis: 'Whales' },
{ A_age: 27, A_name: 'Jonah', B_character: 'Jonah', B_nemesis: 'Spiders' },
{ A_age: 18, A_name: 'Alan', B_character: 'Alan', B_nemesis: 'Ghosts' },
{ A_age: 18, A_name: 'Alan', B_character: 'Alan', B_nemesis: 'Zombies' },
{ A_age: 28, A_name: 'Glory', B_character: 'Glory', B_nemesis: 'Buffy' },
{ A_age: 28, A_name: 'Alan', B_character: 'Alan', B_nemesis: 'Ghosts' },
{ A_age: 28, A_name: 'Alan', B_character: 'Alan', B_nemesis: 'Zombies' }
];
const bench1 = [{ name: 'u2v7v', num: 1 }, { name: 'n53c8', num: 10 }, { name: 'oysce', num: 9 }, { name: '0mto2s', num: 1 }, { name: 'vkh5id', num: 4 }, { name: '5od0cf', num: 8 }, { name: 'uuulue', num: 10 }, { name: '3rgsbi', num: 9 }, { name: 'kccv35r', num: 4 }, { name: '80un74', num: 9 }, { name: 'h4pp3', num: 6 }, { name: '51bit', num: 7 }, { name: 'j9ndf', num: 8 }, { name: 'vf3u1', num: 10 }, { name: 'g0bw0om', num: 10 }, { name: 'j031x', num: 7 }, { name: 'ij3asc', num: 9 }, { name: 'byv83y', num: 8 }, { name: 'bjzp4k', num: 4 }, { name: 'f3kbnm', num: 10 }];
const bench2 = [{ friend: 'o8b', num: 8 }, { friend: 'ye', num: 2 }, { friend: '32i', num: 5 }, { friend: 'uz', num: 3 }, { friend: 'a5k', num: 4 }, { friend: 'uad', num: 7 }, { friend: '3w5', num: 10 }, { friend: 'vw', num: 10 }, { friend: 'ah', num: 4 }, { friend: 'qv', num: 7 }, { friend: 'ozv', num: 2 }, { friend: '9ri', num: 10 }, { friend: '7nu', num: 4 }, { friend: 'w3', num: 9 }, { friend: 'tgp', num: 8 }, { friend: 'ibs', num: 1 }, { friend: 'ss7', num: 6 }, { friend: 'g44', num: 9 }, { friend: 'tab', num: 9 }, { friend: 'zem', num: 10 }];
```
## --seed-contents--
```js
function hashJoin(hash1, hash2) {
return [];
}
```
# --solutions--
```js
function hashJoin(hash1, hash2) {
const hJoin = (tblA, tblB, strJoin) => {
const [jA, jB] = strJoin.split('=');
const M = tblB.reduce((a, x) => {
const id = x[jB];
return (
a[id] ? a[id].push(x) : (a[id] = [x]),
a
);
}, {});
return tblA.reduce((a, x) => {
const match = M[x[jA]];
return match ? (
a.concat(match.map(row => dictConcat(x, row)))
) : a;
}, []);
};
const dictConcat = (dctA, dctB) => {
const ok = Object.keys;
return ok(dctB).reduce(
(a, k) => (a[`B_${k}`] = dctB[k]) && a,
ok(dctA).reduce(
(a, k) => (a[`A_${k}`] = dctA[k]) && a, {}
)
);
};
return hJoin(hash1, hash2, 'name=character');
}
```