Este é o antepenúltimo artigo sobre operadores, mas esses não são menos importantes do que todos que citamos anteriormente. Como você já sabe, array é um tipo de dado primitivo que nos dá a possibilidade de lidar com diferentes tipos de conceitos:
- listas;
- dicionários;
- matrizes;
- conjuntos;
- etc.
Falando especificamente sobre conjuntos, percebemos a necessidade de algumas operações. Citando, por exemplo, duas ações comuns sobre esse tópico: união e comparação. Como você faria isso em um array?
Bom, algumas coisas já estão prontas pra você. Mas nem sempre tudo pode funcionar como você quer - e aí é bom saber como fazer certas coisas sem depender das "soluções prontas", confere?
Dito isso, para cada operador deste artigo, irei escrever uma função que faz a mesma coisa. Assim você estará pronto(a) para alternativas.
Operador +
Ué, Kiko, esse operador não é de soma?
Sim, entre números. Entre arrays, esse operador é de união. Ele é equivalente ao array_merge(), exceto que, na união, os valores do array à esquerda são preservados, enquanto no merge, todo valor à direita sobreescreve o da esquerda.
Como assim, Kiko?!
Vamos comparar:
<?php
$redesOn = [
'um' => 'Twitter',
'dois' => 'TikTok',
'tres' => 'Kawaii'
];
$redesOff = [
'um' => 'Facebook',
'dois' => 'Instagram'
];
$resultado1 = $redesOn + $redesOff;
$resultado2 = array_merge($redesOn, $redesOff);
var_dump($resultado1); // array(um => Twitter, dois => TikTok, tres => Kawaii)
var_dump($resultado2); // array(um => Facebook, dois => Instagram, tres => Kawaii)
No caso de dicionários (arrays de palavras-chave, ao invés de índices numéricos), é essa a diferença entre as duas operações. Enquanto a união manteve os valores de $redesOn, o array_merge() sobreescreveu os valores das chaves 'um' e 'dois' com os valores de $redesOff. Ainda assim, o último (Kawaii) foi adicionado em ambos os casos, pois só existia em $redesOn.
E se fosse uma lista, Kiko?
Nesse caso, teríamos um comportamento diferente somente na função array_merge(). A união continua mantendo os valores da esquerda onde as chaves são iguais. Por exemplo, se as duas listas possuem o índice 0, somente o valor do índice 0 do array à esquerda é preservado.
Enquanto o array_merge() puxa todos os valores do array à direita e coloca no final do array à esquerda:
<?php
$redesOn = ['Twitter', 'TikTok', 'Kawaii'];
$redesOff = ['Facebook', 'Instagram'];
$resultado1 = $redesOn + $redesOff;
$resultado2 = array_merge($redesOn, $redesOff);
var_dump($resultado1); // array(0 => Twitter, 1 => TikTok, 2 => Kawaii)
var_dump($resultado2); // array(0 => Twitter, 1 => TikTok, 2 => Kawaii, 3 => Facebook, 4 => Instagram)
Isso é um erro de design da linguagem, Kiko?
Absolutamente não. O operador é preciso: na união de A com B, eu pego tudo que B tem que não está em A e faço um grupo com os dois. Como raios o interpretador sabe o que não tem em A? Buscando valores? Não, né?... Ele basicamente analisa os índices! "Se o índice existe, então A já tem aquele dado".
Se fôssemos reescrever isso em estrutura de looping, seria algo mais ou menos assim:
<?php
function uniao(array $A, array $B): array
{
foreach($B as $indice => $valor) {
if (!isset($A[$indice])) {
// se $A não tem aquele $indice, seta ele
$A[$indice] = $valor;
}
}
return $A;
}
$redesOn = ['Twitter', 'TikTok', 'Kawaii'];
$redesOff = ['Facebook', 'Instagram'];
var_dump(uniao($redesOn, $redesOff)); // array(0 => Twitter, 1 => TikTok, 2 => Kawaii)
var_dump($redesOn + $redesOff); // array(0 => Twitter, 1 => TikTok, 2 => Kawaii)
Isso também vai funcionar com as chaves em string, será o mesmo resultado. Como o comportamento é o mesmo perante ambos os casos, não podemos, jamais, dizer que isso é erro de design.
E o
array_merge(), Kiko?
Aí é outra história! Tem um comportamento diferente pra cada caso, o que sinaliza um possível acúmulo de responsabilidade e até pode ser considerado erro de design. Mas não é nada tão grotesco assim, desde que você saiba com que tipo de dados está lidando. Para exemplificar melhor, vou fazer duas funções: uma para o merge de listas e outro para merge de dicionários. Ao final delas, farei a bendita função de merge com acúmulo de responsabilidade:
<?php
function mergeLista(array $listaA, array $listaB): array
{
foreach ($listaB as $valor) {
$listaA[] = $valor; // adiciona o elemento no fim da lista de $listA
}
return $listaA;
}
function mergeDicionario(array $dicA, array $dicB): array
{
foreach($dicB as $indice => $valor) {
$dicA[$indice] = $valor; // quer ele exista ou não
}
return $dicA;
}
// com essas duas funções, que operam de formas completamente diferentes, podemos fazer uma função merge similar ao array_merge
function mergeArrays(array $A, array $B): array
{
// comparações abaixo baseadas na definição da função 'array_is_list()' do PHP 8.1
// https://www.php.net/manual/en/function.array-is-list.php
// fiz assim pra poder rodar em PHP 7
$isListA = array_key_first($A) === 0;
$isListB = array_key_first($B) === 0;
if ($isListA && $isListB) {
return mergeLista($A, $B);
}
return mergeDicionario($A, $B);
}
// E aqui vamos nós:
// LISTAS
$redesOn = ['Twitter', 'TikTok', 'Kawaii'];
$redesOff = ['Facebook', 'Instagram'];
var_dump(mergeArrays($redesOn, $redesOff)); // array(0 => Twitter, 1 => TikTok, 2 => Kawaii, 3 => Facebook, 4 => Instagram)
var_dump(array_merge($redesOn, $redesOff)); // array(0 => Twitter, 1 => TikTok, 2 => Kawaii, 3 => Facebook, 4 => Instagram)
// DICIONÁRIOS
$redesOn = [
'um' => 'Twitter',
'dois' => 'TikTok',
'tres' => 'Kawaii'
];
$redesOff = [
'um' => 'Facebook',
'dois' => 'Instagram'
];
var_dump(mergeArrays($redesOn, $redesOff)); // array(um => Facebook, dois => Instagram, tres => Kawaii)
var_dump(array_merge($redesOn, $redesOff)); // array(um => Facebook, dois => Instagram, tres => Kawaii)
Vendo essas implementações, você poderá saber qual operação se adequa melhor nos seus objetivos, certo? Se a ideia é juntar os valores, pensa no array_merge. Se for para preservar as chaves e valores do primeiro array, aí usa a união.
Operador ==
Esse é um comparador, certo, Kiko?
Mais exato que isso, só a matemática... E aqui tem a mesma função. Mas é bom ressaltar que, no caso de arrays, o PHP irá checar se todas as chaves que tem em um está presente no outro e se possuem o mesmo valor. Sendo assim, podemos reescrever esse comparador com um loop validando:
- se os tamanhos são iguais;
- se as chaves do primeiro existem no segundo;
- se o valor de cada chave do primeiro é igual ao valor da mesma chave no segundo.
Vamos reescrever?
<?php
function comparaArray(array $A, array $B): bool
{
if (count($A) !== count($B)) { // se tiver uma quantidade diferente, já dá false
return false;
}
foreach ($A as $indice => $valor) {
// como eles têm o mesmo tamanho, tanto faz se você varre $A ou $B
// o ideal é varrer um pra comparar com o outro
// no menor sinal de diferença, é return false
if (!isset($B[$indice])) { // esse índice não tem em $B? são diferentes
return false;
}
if ($B[$indice] != $valor) { // o valor de $A[$indice] é diferente em $B[$indice]? diferentes
return false;
}
}
// percorreu todo o array e não deu nada diferente? então é igual!
return true;
}
$redesOn = ['Twitter', 'TikTok', 'Kawaii'];
$redesOff = ['Facebook', 'Instagram'];
$redesOn2 = [
'um' => 'Twitter',
'dois' => 'Tiktok',
'tres' => 'Kawaii',
];
var_dump(comparaArray($redesOn, $redesOn)); // bool(true)
var_dump(comparaArray($redesOn, $redesOff)); // bool(false)
var_dump(comparaArray($redesOn, $redesOn2)); // bool(false)
var_dump($redesOn == $redesOn); // bool(true)
var_dump($redesOn == $redesOff); // bool(false)
var_dump($redesOn == $redesOn2); // bool(false)
Bom, não tem muito o que exemplificar na comparação de igualdade de arrays. O bicho pega é no de identidade...
Operador ===
Pois é, comparador de identidade é algo que já é estranho abordar com tipos de dados escalares, imagine array... Aqui, as condições são:
- mesmo tamanho;
- mesmas chaves presentes;
- mesmos valores e tipos de dados nas mesmas chaves;
- chaves na mesma ordem.
Portanto, são apenas duas novidades perante o operador anterior: além de tudo aquilo, tem a checagem de tipo de dados e a ordem das chaves.
Como você checaria isso, Kiko?
Olha, tem muitas formas de fazer isso. Dá pra checar no mesmo loop, separando as chaves do array $B e ir fazendo em sequência... Mas tenho certeza que você pode encontrar um jeito diferente: fica aí o desafio pra você lançar a braba nos comentários, rs. Bora?!
<?php
function comparaIdentidadeArray(array $A, array $B): bool
{
if (count($A) !== count($B)) { // se tiver uma quantidade diferente, já dá false
return false;
}
$chavesB = array_keys($B);
$chaveAtual = 0;
foreach ($A as $indice => $valor) {
// como eles têm o mesmo tamanho, tanto faz se você varre $A ou $B
// o ideal é varrer um pra comparar com o outro
// no menor sinal de diferença, é return false
if (!isset($B[$indice])) {
// esse índice não tem em $B? são diferentes
return false;
}
if ($B[$indice] !== $valor) {
// o valor de $A[$indice] não é idêntico em $B[$indice]? diferentes
return false;
}
if ($chavesB[$chaveAtual++] != $indice) {
// a chave de $B na ordem atual não é a mesma chave em $A? diferentes
return false;
}
}
// percorreu todo o array e não deu nada diferente? então é idêntico!
return true;
}
$A = [1, 2, 3, 4, 5];
$B = [1, 2, 3, 4, 5.0]; // valor float no final
$C = [0 => 1, 2 => 3, 1 => 2, 3 => 4, 4 => 5]; // troca na ordem das chaves 1 e 2
var_dump(comparaIdentidadeArray($A, $A)); // bool(true)
var_dump(comparaIdentidadeArray($A, $B)); // bool(false)
var_dump(comparaIdentidadeArray($A, $C)); // bool(false)
var_dump($A === $A); // bool(true)
var_dump($A === $B); // bool(false)
var_dump($A === $C); // bool(false)
// NOTA: a igualdade de $A, $B (não identidade), daria true, da mesma forma de $A com $C
var_dump($A == $B); // bool(true), ignora a diferença de float e int
var_dump($A == $C); // bool(true), ignora a desordenação da lista, os índices tem o mesmo valor
E agora você também conseguirá discernir quando deve usar a comparação de identidade ou apenas de igualdade entre arrays.
Operador <> ou !=
A partir de agora, estaremos negando os operadores que criamos logo acima. Ou seja, a implementação deles é: em todos os casos que davam false, retorna true. E onde dava true, retorna false!
Esses primeiros representam a operação de desigualdade, ou seja, o que não é igual. Por tanto é o oposto do operador ==, o que dispensa implementações, certo?
Operador !==
Esse é o operador que nega a identidade. No caso, é chamado de não identidade. É o oposto do operador ===, o que também dispensa implementações.
E por hoje é só! Curtiu? Comenta e compartilha! E não esquece de contribuir com o desafio: implemente do seu jeito! No próximo artigo falaremos sobre Operador de Tipo. Está ansioso(a)? Então pode esperar!
Inté!!
About the author
My name is Kaique Garcia, but you can call me Kiko. I'm a developer since 2014, a guitar player since 2008, a World Wide Web explorer since 2000 and a living guy since 1992 (haha). My hobby is to have hobbies (I can tell you more than 20 hobbies I got from my childhood). My pleasure is to solve problems, that's the way I came to the development area. Ok, lemme finish it... I like to meet people, so don't worry about speaking to me!
Comments
Join the discussionNo comments yet. Be the first to comment.
PHP para Iniciantes
Neste mini-curso, irei falar sobre todos os pontos básicos sobre o PHP, de forma que qualquer pessoa que nunca viu a linguagem poderá conhecer o básico do básico sobre ela.