JavaScript: Set vs Array, quando e como usar?
Tags: ptbr, javascript, webdev, node
Published: 2019-07-08 06:57:36

# O que é Set e o que é Array?

Todos que trabalham com JS até agora estão familiarizados com o Array (não me diga que não!). Mas o que exatamente é um Array?
Bem, em geral, Array é um tipo de estrutura que representa um bloco de dados (números, objetos, etc…) **alocados em memória de modo consecutivo.**

Por exemplo, `[1, 2, 3, 2]` 

E o que é Set?

Set, um conceito mais familiar da matemática, é um **tipo de dados abstrato** que contém **apenas** elementos/objetos **distintos**, **sem a necessidade de ser alocado por ordem por índice**.

Por exemplo, `{1, 2, 3}` 

Sim, por definição, Array e Set são conceitos tecnicamente diferentes.

Uma das maiores diferenças aqui, você pode perceber, é que os elementos no Array podem ser duplicados (a menos que você diga que não deve ser), e no Set, eles simplesmente não podem ser (independentemente do que você decidir).

Além disso, Array é considerado como tipo de estrutura de dados chamada de **"coleção indexada"** (_indexed collection_), enquanto Set é considerado como "coleção com chaves" (_keyed collection_).

Um lembrete rápido para quem não se lembra:

- **Coleções indexadas** são coleções de dados que são ordenadas por um valor de índice
- **Coleções com chaves** são coleções que usam chaves, estes contêm elementos que são iteráveis **na ordem de inserção**.

Agora ficou fácil, certo? Podemos nos perguntar: "...se são diferentes, por que nos incomodamos em comparar um com o outro?"

No mundo da programação, com o mesmo conjunto de dados (sem duplicatas), podemos usar Array ou Set para ser nossa estrutura escolhida para armazenar esse conjunto de dados. No entanto, dependendo do caso de uso, a escolha da estrutura correta contribui para fornecer a solução ideal - e queremos alcançar isso. Para entender qual escolher, precisamos entender primeiro quem são, como são construídos e do que são capazes cada uma dessas estruturas. Aprendemos com "quem são", vamos passar para "como inicializar um" em JS.

# Inicilizando

## Array

Array é muito direto. Para declarar um novo array no JS, você pode usar a forma literal diretamente:

```js 
var arr = []; // array vazio 
var arr = [1,2,3]; // array que contém 1,2,3
``` 

Ou use o construtor embutido:

```js
var arr = new Array(); // array vazio 
var arr = new Array(1,2,3); // array que contém 1,2,3
```

Ou ainda mais legal:

```js
var arr = Array.from ("123"); // ["1", "2", "3"]
``` 

Apenas um conselho - não use `new Array ()` a menos que você realmente precise, já que:

- ele executa muito mais lento que o literal `[]` (isso será explicado em um artigo diferente - talvez;)).
- `[]` economiza mais tempo de digitação (experimente :))
- você pode acabar cometendo alguns erros clássicos , como:

```js
var arr1 = new Array(10); // arr1[0] = undefined, mas arr1.length = 10
var arr2 = [10]; // arr2[0] = 10 e arr2.length = 1;
var arr3 = new Array(1,2,3); // [1,2,3]
var arr4 = [1,2,3]; // [1,2,3] 
``` 

Então, regra geral Nº1 - **mantenha a simplicidade!**

## Set

Set tem um construtor embutido. Sim, não há nenhum atalho como em arrays.

> Set([iterável])

Para criar um novo conjunto, precisamos usar uma nova sintaxe, por exemplo:

```js
var emptySet = new Set(); 
var exampleSet = new Set([1,2,3]);
```

Mas definitivamente não:

```js
new Set(1);
``` 

Set recebe o **objeto iterável** como seu parâmetro de entrada e criará o objeto Set respectivamente. Portanto, podemos construir um conjunto a partir de um array - mas ele só incluirá **elementos distintos desse array/não duplicados.**

E, claro, também podemos converter um Set de volta para array usando o método `Array.from()`.

```js
var set = new Set([1,2,3]); // {1,2,3}
var arr = Array.from(set); // [1,2,3]
```

OK, agora que sabemos como criá-los, e as suas capacidades? Vamos fazer uma pequena comparação entre os métodos mais básicos fornecidos pelo Array e Set, que são:

# Localizando e acessando um elemento

- Primeiro de tudo, **Set não suporta acesso aleatório a um elemento por índice** como em Array, o que significa:

```js
console.log(set[0]); //undefined
console.log(arr[0]); //1
```

- Mais importante, porque os dados do array são armazenados na memória consecutivamente, a CPU poderá acessar os dados muito mais rápido devido à pré-busca. Portanto, em geral, acessar um elemento no **array (um após o outro, como em um loop for) é mais rápido e mais eficiente se** comparado com outros tipos de dados abstratos.
- Verificar se um elemento existe em um Set tem uma sintaxe mais simples do que em um Array, usando **Set.prototype.has(value)** vs **Array.prototype.indexOf(value)**.

```js
console.log(set.has(0)); // false
console.log(arr.indexOf(0)); // -1

console.log(set.has(1)); // true
console.log(arr.indexOf(1)); //0
```

O que significa que em Array, precisamos fazer uma verificação extra se queremos fazer uma condição em que o elemento está em Array:

```js
var isExist = arr.indexOf (1)! == -1;
```

O ES6 fornece `Array.prototype.includes()` que se comporta de forma semelhante ao `.has()`, no entanto, não é suportado em todos os navegadores, ou em outras palavras, no IE (surpresa :)!).

# Adicionando/Inserindo novo elemento

- Adicionar um novo elemento ao Array pode ser feito rapidamente em **O(1)** usando **Array.prototype.push()** - o elemento será adicionado ao final do array.

```js
arr.push(4); //[1,2,3,4]
```

- Ou também pode ser feito em **O(n)** usando **Array.prototype.unshift()** - adicionando o elemento ao início do array, onde _n_ é o comprimento do array atual.

```js
arr.unshift(3); // [3,1,2,3]
arr.unshift(5, 6); // [5,6,3,1,2,3]
```

- Em Set, **há apenas uma maneira** de adicionar novo elemento, **Set.prototype.add()**. Como Set precisa manter as propriedades **"distintas"** entre os membros do conjunto, em cada vez que usamos **.add()**, o Set precisa verificar todos os membros antes de continuar, para garantir que não haja duplicados. Geralmente **add()** terá **O(n)** em tempo de execução. No entanto, graças à **abordagem de implementação da tabela de hash** (_hash table_), **add()** em Set provavelmente será apenas **O(1)**.

```js
set.add(3); // {1,2,3} 
set.add(4); // {1,2,3,4}
```

Aprendemos que na adição de elementos, Set executa quase que as mesmas operações que Array. E como fica a remoção?

# Removendo elemento

- Uma das coisas boas que tornam o Array tão popular é porque ele fornece muitos métodos diferentes para remover um elemento, por exemplo:

**.pop()** - remove e retorna o último elemento. Isso leva **O(1)**.

```js
var arr = [5,6,1,2,3,4]
arr.pop (); // retorna 4
```

**.shift()** - remove e retorna o primeiro elemento. Isso leva **O(n)**.

```js
var arr = [5,6,1,2,3,4]
arr.shift (); // retorna 5
```

**.splice(index, deleteCount)** - remove um **número de elementos (_deleteCount_) a partir do índice (_index_)**. Isso pode levar até **O(n)**.

```js
var arr = [5,6,1,2,3,4]
arr.splice (0, 3); // retorna [5,6,1]
```

Enquanto isso, em Set, vamos usar:

**.delete(element)** - remove um **determinado elemento específico** do Set.

```js
var set = new Set([1,2,3,4])
set.delete(4); // retorna `true`, agora "set" é {1,2,3}
```

**.clear()** - remove **todos os elementos** do Set.

```js
var set = new Set([1,2,3,4])
set.clear(); // retorna `undefined`, agora "set" é {}
```

- Enquanto o Array não suporta um método nativo para remover um dado elemento específico (exceto se conhecermos o seu índice), precisaremos da ajuda de uma função externa extra para procurar pelo índice desse elemento e executar o **.splice()**, já o Set tem **.delete(element)** - simples e fácil de usar.

Além disso, o Array nos fornece muito mais funcionalidades nativas (**.reduce()**, **.reverse()**, **.sort()**, etc…), comparado ao Set, que atualmente possui apenas as funcionalidades mais básicas mencionadas acima. Então, alguns de vocês podem pensar, por que preferiríamos Set ao invés de Array?

# Então, qual é melhor? E quando é melhor utilziar Array?

- Em primeiro lugar, Set é diferente de Array. Não se destina a substituir totalmente o Array, mas fornecer um tipo de suporte adicional para concluir o que falta no Array.
- Como o Set contém apenas elementos distintos, deixa a vida muito mais fácil se soubermos antecipadamente que queremos evitar salvar dados duplicados em nossa estrutura.
- Operações básicas de Set como **union()**, **intersect()**, **difference()**, etc ... são facilmente implementadas de forma eficaz com base nas operações internas nativas fornecidas. Devido ao método **delete()**, fazer **intersect/union** entre 2 Sets é muito mais confortável do que fazer o mesmo com 2 Arrays.
- Array destina-se a cenários em que queremos manter elementos ordenados para acesso rápido ou fazer modificações pesadas (remover e adicionar elementos) ou qualquer ação necessária para acesso direto ao índice de elementos (por exemplo, tente fazer Pesquisa Binária em Sets ao invés de Arrays - como você acessa o elemento localizado no meio?)

---

# Conclusão

Em geral, na minha opinião, Set realmente não tem uma grande vantagem sobre o Array, exceto em cenários específicos, como quando queremos manter dados distintos/não duplicados com o mínimo de esforço, ou trabalhar com vários conjuntos de dados distintos juntos usando as operações mais básicas do Set, sem a necessidade de acessar elementos diretamente.

Caso contrário, Array sempre! Por qual razão? Menos trabalho para CPU em buscar o elemento quando for necessário.

Você concorda? Sinta-se livre para discutir ;).

# Créditos 

- [ES6 — Set vs Array — What and when?](https://medium.com/front-end-weekly/es6-set-vs-array-what-and-when-efc055655e1a), escrito originalmente por [@mayashavin](https://twitter.com/MayaShavin)