As quatro operações (união, interseção, diferença, complemento) e as leis que as governam, e o segredo: cada operação de conjunto é um operador lógico disfarçado.
Cada conceito da matemática aqui vem com o mesmo conceito em JavaScript, lado a lado. Aqui os conjuntos deixam de ser estáticos e passam a se combinar: juntar permissões, achar o que dois usuários têm em comum, descobrir o que falta. E tem um segredo no fim: cada operação de conjunto é um operador lógico disfarçado, quem entende isso simplifica query e filtro sem esforço.
Este post cobre: as quatro operações (união, interseção, diferença, complemento) e as leis que as governam (comutativa, associativa, distributiva, De Morgan, identidades, absorção). Apoia-se nos fundamentos e em subconjuntos.
Parte 1: as operações
1. União (∪): "tudo, junto"
Tudo que está em A ou em B (ou nos dois):
A ∪ B = {x | x ∈ A ou x ∈ B}
Aquele "ou" é o ∨ da lógica, inclusivo (vale até se estiver nos dois).
A = {1,2,3}, B = {3,4,5}, A ∪ B = {1,2,3,4,5}.
O 3 estava nos dois mas aparece uma vez só, a Regra 1 (sem repetição) continua valendo.
const A = new Set([1, 2, 3]);
const B = new Set([3, 4, 5]);
const uniao = new Set([...A, ...B]);
console.log(uniao); // Set(5) { 1, 2, 3, 4, 5 }
Caso real: juntar permissões de dois papéis, quem é "editor" e "revisor" tem a união das permissões dos dois.
2. Interseção (∩): "só o que é comum"
O que está em A e em B ao mesmo tempo:
A ∩ B = {x | x ∈ A e x ∈ B}
Agora é o "e" (∧ da lógica).
A = {1,2,3}, B = {3,4,5}, A ∩ B = {3}.
const inter = new Set([...A].filter(x => B.has(x)));
console.log(inter); // Set(1) { 3 }
O filter é o "tal que"; o B.has(x) é o x ∈ B. A condição é "x ∈ A e x ∈ B".
Caso real: "amigos em comum", "produtos nas duas listas de desejo", "esse usuário tem algum papel com acesso a isto?".
3. Diferença (\): "o que está em um mas não no outro"
O que está em A e não em B:
A \ B = {x | x ∈ A e x ∉ B}
A = {1,2,3}, B = {3,4,5}, A \ B = {1,2}.
const dif = new Set([...A].filter(x => !B.has(x)));
console.log(dif); // Set(2) { 1, 2 }
É o filter com a condição negada.
Cuidado, diferença não é simétrica.
A \ B ≠ B \ A. Com os conjuntos acima:A \ B = {1,2}, masB \ A = {4,5}. A ordem muda a pergunta: "o que é exclusivo deA?" vs "o que é exclusivo deB?". (União e interseção, ao contrário, são simétricas.) Trocar a ordem no código não é detalhe estético, é responder a pergunta errada. "Campos obrigatórios que faltaram" (obrigatórios \ recebidos) é o oposto de "campos extras enviados" (recebidos \ obrigatórios).
Caso real: "quais campos obrigatórios faltaram?", "quem estava na lista de ontem e não está na de hoje?".
4. Complemento (Ā): "tudo que está fora"
Tudo que não está em A, mas só faz sentido dentro de um universo U:
Ā = U \ A = {x | x ∈ U e x ∉ A}
Complemento é um caso especial de diferença, com o universo à esquerda. Universo = dígitos 0 a 9, A = pares:
U = {0,...,9}, A = {0,2,4,6,8}, Ā = {1,3,5,7,9}.
const U = new Set([0,1,2,3,4,5,6,7,8,9]);
const A = new Set([0,2,4,6,8]);
const compl = new Set([...U].filter(x => !A.has(x)));
console.log(compl); // Set(5) { 1, 3, 5, 7, 9 }
Cuidado: complemento depende do universo. "Não-pares" entre dígitos é
{1,3,5,7,9}; entre todos os inteiros seria infinito. Sem definirU, complemento não tem resposta, e isso vira bug quando o código assume um universo que não garante.
Resumo das quatro
| Operação | Símbolo | Pergunta | Lógica | Código |
|---|---|---|---|---|
| União | A ∪ B |
"tudo dos dois" | ou (∨) |
new Set([...A, ...B]) |
| Interseção | A ∩ B |
"o que é comum" | e (∧) |
[...A].filter(x => B.has(x)) |
| Diferença | A \ B |
"só em A" | e não (∧¬) |
[...A].filter(x => !B.has(x)) |
| Complemento | Ā |
"tudo fora de A" | não (¬) |
[...U].filter(x => !A.has(x)) |
Parte 2: as leis
O dicionário que torna tudo óbvio, é só uma tradução da lógica:
| Lógica | Conjunto | Significa |
|---|---|---|
∧ (e) |
∩ (interseção) |
"o que é comum" |
∨ (ou) |
∪ (união) |
"tudo junto" |
¬ (não) |
complemento | "o que está fora" |
Com esse dicionário, toda lei de lógica se traduz direto pra conjunto.
Comutativa: a ordem não importa
A ∪ B = B ∪ A e A ∩ B = B ∩ A.
(A diferença não tem essa lei, ela tem direção embutida.)
Associativa: o agrupamento não importa
(A ∪ B) ∪ C = A ∪ (B ∪ C) e (A ∩ B) ∩ C = A ∩ (B ∩ C).
Unir permissões de 3 papéis dá no mesmo, não importa quais dois você junta primeiro.
Distributiva: a que fatora
A ∩ (B ∪ C) = (A ∩ B) ∪ (A ∩ C)
A ∪ (B ∩ C) = (A ∪ B) ∩ (A ∪ C)
É a mesma a && (b || c) === (a && b) || (a && c), com && virando ∩ e || virando ∪. Serve pra reorganizar filtro composto: "VIP que é (de SP ou do RJ)" = "(VIP e de SP) ou (VIP e do RJ)".
De Morgan: a mais importante
O complemento de um grupo inverte a operação:
‾(A ∪ B) = Ā ∩ B̄ e ‾(A ∩ B) = Ā ∪ B̄
"O que está fora de (A ou B)" = "o que está fora de A e fora de B". A negação entra e o ∪ vira ∩. Dá pra ver a máquina confirmar:
const U = new Set([1,2,3,4,5,6,7,8]);
const A = new Set([1,2,3]);
const B = new Set([3,4,5]);
const compl = (S) => new Set([...U].filter(x => !S.has(x)));
const esq = compl(new Set([...A, ...B])); // ‾(A ∪ B)
const cA = compl(A), cB = compl(B);
const dir = new Set([...cA].filter(x => cB.has(x))); // Ā ∩ B̄
console.log([...esq]); // [6, 7, 8]
console.log([...dir]); // [6, 7, 8] -> idênticos
Cuidado, De Morgan é o erro nº 1. Não basta trocar a operação: cada termo também é negado.
‾(A ∩ B)não éA ∪ B, éĀ ∪ B̄(a união dos complementos). Igual à lógica:!(a && b)não é!(a || b), é!a || !b.
Identidades: o vazio e o universo
∅ e U funcionam como o 0 e o 1 da aritmética:
A ∪ ∅ = A, A ∩ U = A, A ∩ ∅ = ∅, A ∪ U = U.
E os dois com complemento, que fecham a lógica:
A ∪ Ā = U e A ∩ Ā = ∅.
Um conjunto e seu complemento não têm nada em comum (interseção vazia) e juntos formam tudo (união é o universo), a versão em conjunto de "algo não pode ser verdadeiro e falso ao mesmo tempo".
Idempotente e absorção: as que limpam código
A ∪ A = A, A ∩ A = A (idempotente).
A ∪ (A ∩ B) = A, A ∩ (A ∪ B) = A (absorção).
Absorção é a mesma a || (a && b) === a: quando um termo já contém o outro, o interno é redundante. Ouro pra simplificar condição composta.
O grande recado
Lógica proposicional e teoria dos conjuntos são a mesma estrutura matemática (uma "álgebra booleana"). Um
ifcom&&,||,!e uma manipulação de conjuntos com∩,∪, complemento obedecem às mesmas leis. Aprender uma é aprender a outra. Por isso quem domina De Morgan em lógica simplifica query e filtro sem esforço.