PHP ARRAY Parsing String Como Array Bidimensional no Hacker Rank

//Hacker Rank Input
//1 10 11 9 8 7 6 5 2 8 12, 7 58 2 34 76 88 2 5 3 8 11, 9 65 3 8 98 36 22 87 66 99 0

/* Read and parse input */
$handle = fopen ('php://stdin', 'r');
$string_array = fgets($handle);

$array = explode(', ', $string_array);

array_walk($array, function (&$element) {
    $element = explode(' ', $element);
});

// do your job with bidimensional $array

Via de regra, quando estamos no ambiente Hacker Rank, para obtermos um array de um enunciado de um problema em PHP temos que fazer o parsing de uma string dada as características do input pelo STDIN.

As coisas podem se complicar bastante se o parsing não for bem executado, e é claro que com a pressão da competição, é o que geralmente acontece.

O problema não é do problema em si, por que depois que passa a competição e a pressão você olha pra ele e diz… PQP!

Então fica aí a dica, basta observar com calma, e principalmente observar os delimitadores. Neste caso o primeiro delimitador não é simplesmente uma vírgula, mas sim dois caracteres vírgula e espaço combinados.

# wrong delimiter applied on first level 
$array = explode(',', $string_array);

// [0]=> string(24) "1 10 11 9 8 7 6 5 2 8 12"
// [1]=> string(26) " 7 58 2 34 76 88 2 5 3 8 11"
// [2]=> string(29) " 9 65 3 8 98 36 22 87 66 99 0

Deste ponto em diante as coisas se complicam, pois apesar de termos um array unidimensional de três elementos, o próximo explode vai gerar o segundo nível com mais elementos do que o esperado por conter um espaço a mais.

# wrong quantity of elements on second level
array_walk($array, function (&$element) {
    $element = explode(' ', $element);
});

// [0]=> array(11)
// [1]=> array(12)
// [2]=> array(12)

Agora não tem mais desculpas, essa é pra não esquecer mais.

Dang it!

PHP Array Comparando Variáveis Escalares Combinadas num IF Único

$expected_email = "myself@email.com";
$expected_birthdate = "25/12/0001";

$received_email = "not matching email";
$received_birthdate = "not matching date";

if ([$received_email, $received_birthdate] != [$expected_email, $expected_birthdate]) {
    echo 'Expected combination Email/Birthdate does not match';
}

Olha só que legal, nunca tinha pensado em montar um IF como esses comparando uma lista de variáveis escalares com uma outra lista também de variáveis escalares.

Basicamente isto é uma comparação de arrays e o match se dá pela comparação de forma posicional, ou seja, índice 0 com índice 0 e índice 1 com índice 1.

Caso precise comparar inclusive os tipos de dados basta adicionar mais um igual a operador.

if ([$received_email, $received_birthdate] !== [$expected_email, $expected_birthdate]) {
    echo 'Expected combination Email/Birthdate does not match';
}

Qual a vantagem desse código sobre um código convencional? Não sei dizer muito bem qual seria…

if ($received_email != $expected_email || $received_birthdate != $expected_birthdate) {
    echo 'Expected combination Email/Birthdate does not match';
}

O mais importante é que funciona conforme o projetado.

Happy PHP!

PHP array_filter Verificando se Conteúdo Possui Suporte ao array_flip

static function hasArrayFlipSupport(array $array): bool {
   return empty(
      array_filter(
         $array,
         static function ($value, $key) {
            return (!is_string($value) && !is_int($value)) || (!is_string($key) && !is_int($key));
         },
         ARRAY_FILTER_USE_BOTH
      )
   );
}

Recentemente estive trabalhando (e ainda estou) num projeto de importação de arquivos de dados, uma rotina bastante comum ainda nos dias de hoje apesar de toda a evolução tecnológica que temos vivido.

Pois bem, com a elaboração de classes mais genéricas que devem processar uma variedade quase infinita de possibilidades as coisas se complicam um pouco e precisam de mais rotinas de garantia de funcionalidade.

No meu caso, em um dado momento o uso do array_flip se mostrou ideal, só precisava inverter o array tornando as valores como chaves e as chaves como valores… simples né? Só que não.

O array_flip somente suporta a operação utilizando dados do tipo STRING e INT, e essa regra vale para a dupla chave/valor do array. Lembrando que NULL também não é suportado para a operação.

Então eu elaborei este método para verificar se o conteúdo do array suportaria o array_flip. Basicamente verifica-se o conteúdo de todo o array, ambos chave e valor, se eles possuem os tipos de dados suportados.

E foi neste momento que o array_filter se mostrou parrudo e eficiente. Por padrão o array_filter avalia apenas o valor do elemento.

array_filter($array, static function($value) { return true; });

Mas você consegue facilmente mudar este comportamento para avaliar a chave simplesmente adicionando o terceiro parâmetro ARRAY_FILTER_USE_KEY.

array_filter($array, static function($key) { return true; }, ARRAY_FILTER_USE_KEY);

Mas no meu caso eu precisei mesmo avaliar ambos simultaneamente, assim o trunfo final foi informar o terceiro parâmetro ARRY_FILTER_USE_BOTH.

array_filter($array, static function($value, $key) { return true; }, ARRAY_FILTER_USE_BOTH);

Observe que, ao acionarmos o uso de ambos chave e valor, a sequência de de entrada da dupla chave/valor ocorre na ordem inversa valor/chave. Eu não tenho detalhes para explicar este comportamento, mas acredito que seja para manter uma certa compatibilidade com o uso padrão do array_filter, o qual filtra o valor por padrão.

Alguns irão dizer que estas características são sinais de ausência de padronização e arquitetura no PHP, mas para mim esse é o tremendo charme da linguagem!

World Class Technology S2 PHP!

PHP Array Combinando a Dupla Key Value como String

function getArgsAsString(array $args): string {
    return implode(', ', array_map(static function($v, $k){ return $k.'='.$v; }, $args, array_keys($args)));
}

Oxxê! Até o título deste artigo ficou estranho, mas primeiro vou explicar onde eu precisei usar isso pra ver se facilita o entendimento do porquê esse código pode ser útil pra você.

Quando escrevemos queries SQL em PHP é bastante comum fazermos o bind dos argumentos por nome associativo ao invés de posição. Essa técnica facilita não só a legibilidade do código como também ajuda a manter um código limpo e enxuto.

Pois bem, em um dado momento eu me recordo que precisei registrar um log da operação de SQL, daí além de registrar a string contendo a Query Statement, precisei também salvar os argumentos utilizados na operação. Foi neste momento que me dei conta de que o PHP não fornece uma solução nativa para este caso.

Rapidamente pensei nas funções nativas de parsing de Array do PHP, então o array_map caiu como uma luva. Lembrando que o array_map despreza os índices associativos do array original produzindo um novo array com índices sequenciais, mas como meu objetivo era produzir uma string com a combinação KEY=VALUE, eu não tive problemas com este fato.

Porém, é possível fazermos a mesma operação preservando os índices associativos originais do array, para isto basta usar a sintaxe abaixo com o comando array_walk.

array_walk($mapped_row, function(&$value, $key) {
    $value = sprintf('%s=%s', $value, $key);
}); 

$args_string = implode(', ', $args);

Lembrando que array_walk atua sobre o array original modificando o mesmo, diferentemente do array_map que produz um array completamente novo. Se você tiver problemas em modificar o seu array original crie então uma função passando o array como parâmetro.

Diferentemente de Objetos, os Arrays não são passados por referência por default, assim uma nova array será criada antes do parsing do array_walk, o que irá preservar o array original intacto.

function getArgsAsString(array $args): string {
    array_walk($args, function(&$value, $key) {
        $value = sprintf('%s=%s', $value, $key);
    });
    
    return implode(', ', $args);
}

Bom, fora este detalhe você pode ficar a vontade para utilizar a sintaxe que preferir… aproveite!

PHP Array Usando RESET para Extrair a Primeira Linha de um Array

$y[0] = [ 'col1' => 'A', 'col2' => 'B', 'col3' => 'C' ];
$y[1] = [ 'col1' => 'value1', 'col2' => 'value2', 'col3' => 'value3' ];
$y[2] = [ 'col1' => 'value4', 'col2' => 'value5', 'col3' => 'value6' ];
$y[3] = [ 'col1' => 'value7', 'col2' => 'value8', 'col3' => 'value9' ];

$header = reset($y);
var_dump($header);

// array(3) { 
//   ["col1"] => string(1) "A"
//   ["col2"] => string(1) "B"
//   ["col3"] => string(1) "C"
// }

Pode parecer uma tarefa super simples pegar o primeiro elemento de um array, e realmente é!!! Desde a semana passada o reset() é o meu jeito preferido de fazer isso.

Recentemente quando estive implementando a importação de um arquivo tipo planilha de dados naturalmente tive a necessidade de processar a primeira linha a qual continha o HEADER dos dados. Então me deparei com um código existente que me pareceu muito estranho a princípio, dá uma olhada nisso…

$mapped_headers = $this->map_headers(reset($spreadsheet_rows));

Para quem é familiar a este tipo de processamento sabe que estamos falando de um array bidimensional no melhor estilo linhas x colunas. O que eu não entendi foi aquele reset() ali na invocação do map_headers… que janho!!!

Como de praxe fui debugar e entender como a combinação funcionava… Para o meu espanto, somente agora eu entendi que o comando reset, além de resetar o ponteiro interno do array, retorna também o primeiro elemento do array.

Isto é fantástico!!! Eu sempre me perguntei por que o PHP não tinha um comando first() para pegar o primeiro elemento de um array já que temos o comando current() e o comando end().

Uma alternativa ao reset() seria inverter a array e usar o comando end(), mas acho que não tem necessidade de complicar o código com essa miscelânea pra atingir o mesmo resultado.

$r = array_reverse($y);
$header = end($r);
var_dump($header); 

// array(3) { 
//   ["col1"] => string(1) "A"
//   ["col2"] => string(1) "B"
//   ["col3"] => string(1) "C"
// }

Outra combinação possível seria combinar current() com reset(), mas também seria um desperdício já que o reset já faz o trabalho esperado.

reset($y);
$header = current($y);
var_dump($header); 

// array(3) { 
//   ["col1"] => string(1) "A"
//   ["col2"] => string(1) "B"
//   ["col3"] => string(1) "C"
// }

Outra alternativa segura de pegarmos o primeiro elemento da array seria fatiar o array com array_slice(), lembrando que este comando extrai o elemento como bidimensional preservando o índice da linha. Ainda assim teria que combinar com array_shift() ou array_pop() para pegar o primeiro elemento como array simples.

$first_row = array_slice($y, 0, 1);
var_dump($first_row); 

// array(1) {
//   [0]=>
//   array(3) {
//     ["col1"]=> string(1) "A"
//     ["col2"]=> string(1) "B"
//     ["col3"]=> string(1) "C"
//   }
// }

$header = array_shift($first_row);
var_dump($header); 

// array(3) { 
//   ["col1"] => string(1) "A"
//   ["col2"] => string(1) "B"
//   ["col3"] => string(1) "C"
// }

Pra finalizar uma última alternativa segura que podemos considerar seria utilizar array_values(). Sabemos que a primeira linha do array representam os headers não importa qual é a chave utilizada na indexação destas linhas. Então array_values() garante que o índice da primeira linha seja ZERO através do re-sequenciamento do array sem alterar a ordem de seus elementos.

$y['Z'] = [ 'col1' => 'A', 'col2' => 'B', 'col3' => 'C' ];
$y['X'] = [ 'col1' => 'value1', 'col2' => 'value2', 'col3' => 'value3' ];
$y['S'] = [ 'col1' => 'value4', 'col2' => 'value5', 'col3' => 'value6' ];
$y['J'] = [ 'col1' => 'value7', 'col2' => 'value8', 'col3' => 'value9' ];

$header = array_values($y)[0];
var_dump($header); 

// array(3) { 
//   ["col1"] => string(1) "A"
//   ["col2"] => string(1) "B"
//   ["col3"] => string(1) "C"
// }

Não exploramos outras alternativas como loop foreach() e for() mas nem precisa, com o PHP certamente encontraremos pelo menos mais meia dúzia de outras formas para atingir o mesmo resultado.

Agora convenhamos, o reset() arrasou!

PHP Como Verificar Se Um Array é 100% Sequencial

function isArrayFullySequential(array $array): bool {
    return count(array_filter($array, 'is_int', ARRAY_FILTER_USE_KEY)) === count($array);
}

Então… há algum tempo eu escrevi aqui no blog uma função para testar se um array era 100% associativo e no começo do texto eu comentei que não sabia quando você precisaria usar uma função como essa. Pois é, mordendo a língua eu acabei precisando agora testar o contrário, se um array era composto por índices 100% numéricos.

Ao integrar o sistema com uma API externa estruturada com XML tendo um elemento previsto com até 1000 ocorrências que seria carregado a partir de um array. Decidi por utilizar o índice do array como ID das ocorrências no XML, assim implantei o requisito de ter este array indexado 100% com chaves numéricas.

Ainda estou pensando se vou manter esta abordagem ou não, talvez eu acabe flexibilizando a carga do array e controle apenas o total de ocorrências.

De qualquer forma segue abaixo a função que valida se os índices numéricos do array estão dentro dos limítrofes esperados.

hasItemsIndexOutOfBounds(array $array): bool {
    $keys = array_keys($array);
    return count($keys)
        && (min($keys) < 1 || max($keys) > 1000);
}

Ea í, curtiu? Não sei quando você vai precisar usar usar uma destas funções, mas fica aí o exemplo pra quando aparecer a necessidade.

PHP Array Entenda como Obter Vantagens com as Funções Array Prontas

function bestProducts($products, $sales): array {
  $ySalesProducts = explode(' ', $sales);

  $yFiltered = array_filter(
    $ySalesProducts,
    function ($item) use ($products) {
      return in_array($item, $products, true);
    });
 
  $yCount = array_count_values($yFiltered);
  arsort($yCount);
  $ySlice = array_slice($yCount, 0, 2);
  [$first, $second] = array_values($ySlice);

  ($first === $second)
    ? ksort($ySlice)
    : null;

    return $ySlice;
}

$products = [ 'phone', 'mobile', 'game', 'headset', 'hdd', 'ssd' ];
$context = 'phone ssd headset ssd headset mobile headset phone game headset hdd ssd game ssd ssd headset game box box box box box box';

$result = bestProducts($products, $context);
var_dump($result);

Há algum tempo eu estava num desafio de codificação na Plataforma Hacker Rank e apareceu uma questão que usava muitos recursos de ARRAY… pensei comigo, tá no papo! Só que não…

Meu principal erro não foi subestimar o problema, mas sim subestimar a memorização das funções array e o porquê delas existirem. Como de costume, eu sempre recorro as documentações do PHP.net quando estou programando, só que durante uma maratona ou num desafio de programação ter estas funcionalidades prontamente memorizadas faz uma tremenda falta.

Churumelas postas, vamos ao enunciado do problema.

Dada uma lista de produtos ($products) previamente definida, contabilize a quantidade vendida de cada produto dada uma listagem de produtos vendidos ($context). Somente os produtos constantes na lista previamente definida devem ser contabilizados. Ao final selecione apenas os dois produtos mais vendidos, e caso haja um empate classifique os produtos em ordem alfabética. O retorno deve ser um array associativo produto x quantidade.

Observando o enunciado podemos perceber alguns recursos que serão necessários… explode para transformar a lista de produtos vendidos em array, array_filter para excluir quem não está na listagem de produtos pré-definidos, count para contar, sort para classificar, array_shift para pegar os produtos mais vendidos…

Podemos parar por aqui, boa parte destes recursos vão dar na trave, sabe por que? Porque provavelmente você não lembraria de cabeça a diferença entre sort, rsort, asort, arsort, ksort, krsort, usort, uasort, uksort… Talvez você também não lembre algumas funções prontas utilizam exclusivamente índices numéricos e/ou não preservam os índices originais associativos. Outra grande questão é a aplicação em arrays multi-níveis, uma coisa é trabalhar com array simples de um único nível e outra coisa processar array multi-nível em profundidade.

Assim, eu não vou tentar explicar o algoritmo do problema pra vocês; ele é muito simples depois de resolvido e pode facilmente ser entendido apenas você lendo o código. Conforme o enunciado também podemos perceber que os arrays aplicados são de nível simples onde as funções array prontas dão um show. Então eu vou explicar a seguir o que você deveria ter mente ou ter decorado sobre algumas funções array prontas, por que esse é o verdadeiro diferencial que você pode obter.

SORT – sort ordena um array pelo valor do elemento, mas ele despreza completamente os índices originais do array. Sort opera sobre o array original modificando-o completamente, assim bye bye array original depois do sort. Então sort não serve pra você se você precisa manter os índices associativos preservados depois da ordenação.

$y = (['fruit'=>'lemon','salad'=>'lettuce','protein'=>'eggs','carbohydrate'=>'pasta']);
sort($y);
print_r($y);
// [0] => eggs
// [1] => lemon
// [2] => lettuce
// [3] => pasta

RSORT – o mesmo que sort, só que ordena em ordem inversa. Repare que o R a frente de sort significa REVERSE. Lembre-se deste R a frente do sort, ele vai aparecer em outros comandos e terá a mesma semântica.

$y = ['fruit'=>'lemon','salad'=>'lettuce','protein'=>'eggs','carbohydrate'=>'pasta'];
rsort($y);
print_r($y);
// [0] => pasta
// [1] => lettuce
// [2] => lemon
// [3] => eggs

ASORT – o mesmo que sort, só que este aqui preserva os índices originais intactos. Repare que o A a frente de sort significa ASSOCIATIVE, indicando que o comando aplica-se a operações com arrays associativos. Os índices numéricos também serão preservados após a ordenação.

$y = (['fruit'=>'lemon',3=>'bread','salad'=>'lettuce',0=>'rice','protein'=>'eggs',1=>'beans','carbohydrate'=>'pasta']);
asort($y);
print_r($y);
// [1] => beans
// [3] => bread
// [protein] => eggs
// [fruit] => lemon
// [salad] => lettuce
// [carbohydrate] => pasta
// [0] => rice

ARSORT – o mesmo que asort só que a ordenação é feita em ordem inversa. Repare novamente o R antes do sort indica REVERSE ou seja, ordem inversa.

$y = (['fruit'=>'lemon',3=>'bread','salad'=>'lettuce',0=>'rice','protein'=>'eggs',1=>'beans','carbohydrate'=>'pasta']);
arsort($y);
print_r($y);
// [0] => rice
// [carbohydrate] => pasta
// [salad] => lettuce
// [fruit] => lemon
// [protein] => eggs
// [3] => bread
// [1] => beans

KSORT – ordena um array pelo índice e não pelo valor do elemento como faz o sort. Repare que K a frente do sort significa KEY. Aqui nos deparamos com uma primeira situação inusitada, como é comum termos índices mistos compostos por números e strings o tipo de ordenação padrão pode não atender as nossas expectativas. Para corrigir este comportamento basta ativar o segundo parâmetro informando SORT_STRING ou SORT_NATURAL, isso deve resolver a questão.

$y = ['fruit'=>'lemon',3=>'bread','salad'=>'lettuce',0=>'rice','protein'=>'eggs',1=>'beans','carbohydrate'=>'pasta'];
ksort($y);
print_r($y);
// [carbohydrate] => pasta
// [fruit] => lemon
// [salad] => lettuce
// [0] => rice
// [protein] => eggs
// [1] => beans
// [3] => bread

$y = ['fruit'=>'lemon',3=>'bread','salad'=>'lettuce',0=>'rice','protein'=>'eggs',1=>'beans','carbohydrate'=>'pasta'];
ksort($y, SORT_STRING);
print_r($y);
// [0] => rice
// [1] => beans
// [3] => bread
// [carbohydrate] => pasta
// [fruit] => lemon
// [protein] => eggs
// [salad] => lettuce

KRSORT – o mesmo que ksort só que ordenado em ordem inversa.

$y = ['fruit'=>'lemon',3=>'bread','salad'=>'lettuce',0=>'rice','protein'=>'eggs',1=>'beans','carbohydrate'=>'pasta'];
krsort($y, SORT_NATURAL);
print_r($y);
// [salad] => lettuce
// [protein] => eggs
// [fruit] => lemon
// [carbohydrate] => pasta
// [3] => bread
// [1] => beans
// [0] => rice

ARRAY_SLICE – esse aqui é animal, se você precisa quebrar um array em pedaços, sacar a ponta da frente ou até mesmo a ponta de trás do array, mas precisa que esta quebra preserve a combinação key => value do array original?! array_slice é a sua pedida.

$y = ['fruit'=>'lemon',3=>'bread','salad'=>'lettuce',0=>'rice','protein'=>'eggs',1=>'beans','carbohydrate'=>'pasta'];
krsort($y, SORT_STRING);
print_r($y);
// [salad] => lettuce
// [protein] => eggs
// [fruit] => lemon
// [carbohydrate] => pasta
// [3] => bread
// [1] => beans
// [0] => rice

$ys = array_slice($y, 0, 2);
print_r($ys);
// [salad] => lettuce
// [protein] => eggs

ARRAY_PUSHARRAY_POPARRAY_SHIFTARRAY_UNSHIFT – Lembre-se que estas funções não objetivam trabalhar com índices associativos então nenhuma destas funções servirão para o nosso caso. Todos esses quatro comandos operam em nível de elemento valor sem se importar com índices. A cada interação por estes comandos os índices do array são sistematicamente resequenciados.

$y = [];
array_push($y, 'first');
array_push($y, 'second');
array_push($y, 'third');
print_r($y);
// [0] => first
// [1] => second
// [2] => third
$ys = array_shift($y);
print_r($ys);
// first
array_unshift($y, 'zero');
print_r($y);
// [0] => zero
// [1] => second
// [2] => third
$yp = array_pop($y);
print_r($yp);
// third
print_r($y);
// [0] => zero
// [1] => second

ARRAY_COUNT_VALUES – fantástico recurso para contabilizar as ocorrências dos valores em um array. Pense no array de produtos vendidos, usando array_count_values teremos como retorno um array tendo os produtos como chave e as ocorrências de cada produto sumarizadas como valor.

$c = 'phone ssd headset ssd headset mobile headset phone game headset hdd ssd game ssd ssd headset game box box box box box box';
$y = explode(' ', $c);
print_r($y);
// [0] => phone              // [12] => game
// [1] => ssd                // [13] => ssd
// [2] => headset            // [14] => ssd
// [3] => ssd                // [15] => headset
// [4] => headset            // [16] => game   
// [5] => mobile             // [17] => box
// [6] => headset            // [18] => box
// [7] => phone              // [19] => box
// [8] => game               // [20] => box
// [9] => headset            // [21] => box
// [10] => hdd               // [22] => box
// [11] => ssd

$yc = array_count_values($y);
print_r($yc);
// [phone] => 2
// [ssd] => 5
// [headset] => 5
// [mobile] => 1
// [game] => 3
// [hdd] => 1
// [box] => 6

ARRAY_KEYS – retorna os índices de um array como um array de valores. Este novo array retornado será chaveado por índice numérico sequencial.

$y = ['phone'=>2,'ssd'=>5,'headset'=>5,'mobile'=>1,'game'=>3,'hdd'=>1,'box'=>6];
$yk = array_keys($y);
print_r($yk);
// [0] => phone
// [1] => ssd
// [2] => headset
// [3] => mobile
// [4] => game
// [5] => hdd
// [6] => box

ARRAY_VALUES – retorna os valores de um array como um array de valores desprezando os índices originais. O novo array será chaveado por índice numérico sequencial. O array_values trabalha apenas com um nível de array, assim arrays com multi-níveis contendo outros arrays ou objetos serão processados no seu primeiro nível, mas estes subarrays e objetos permanecerão intactos em sua estrutura.

$y = ['phone'=>2,'ssd'=>5,'headset'=>5,'mobile'=>1,'game'=>3,'hdd'=>1,'box'=>6,'sub'=> ['ps5'=>10]];
$yv = array_values($y);
print_r($yv);
// [0] => 2
// [1] => 5
// [2] => 5
// [3] => 1
// [4] => 3
// [5] => 1
// [6] => 6
// [7] => Array
// (
//   [ps5] => 10
// )

ARRAY_FLIP – inverte a posição de índices e valores, ou seja, os valores passam a ser índices e os índices passam a ser valores. Mas o array_flip trabalha somente com arrays simples com índices e valores do tipo string e/ou numérico. Caso seu array contenha um outro array ou um objeto o array flip vai emitir um warning mas ainda assim vai funcionar, porém os subarrays e objetos serão ignorados e não farão parte da nova array retornada.

$y = ['phone'=>2,'ssd'=>5,'headset'=>5,'mobile'=>1,'game'=>3,'hdd'=>1,'box'=>6,'sub'=> ['ps5'=>10]];
$yf = array_flip($y);
print_r($yf);
// [2] => phone
// [5] => headset
// [1] => hdd
// [3] => game
// [6] => box

ARRAY_SUM – sumariza ou contabiliza os elementos valores de um array desde que os valores possam ser convertidos para número. Repare que no exemplo o produto box que contém o valor 6txt será convertido para o número 6, pois este é o comportamento de conversão padrão esperado do PHP. Qualquer elemento que não possua um valor numérico, como subarray, objeto, ou texto, não será contabilizado. O retorno é um escalar com o total da somatória dos elementos numéricos.

$y = ['phone'=>'two','ssd'=>5,'headset'=>5,'mobile'=>1,'game'=>3,'hdd'=>1,'box'=>'6txt','sub'=> ['ps5'=>10]];
echo 'total: ' . array_sum($y);
// total: 21

ARRAY_FILTER – O famigerado array_filter é um dos seus maiores aliados. O simples fato de parsear um array com array_filter já elimina todos os elementos com valor null, sem necessidade de nenhuma função callback. Combinado com uma função callback então o poder de sanitização é exponencial. Mas lembre-se, array_filter não é multilevel e não serve para sanitização em profundidade.

$y = ['phone'=>'two','ssd'=>null,'headset'=>'','mobile'=>1,'game'=>3,'hdd'=>null,'box'=>6,'sub'=> ['ps5'=>10, 'ps3'=>null]];
$yt = array_filter($y);
print_r($yt);
// [phone] => two
// [mobile] => 1
// [game] => 3
// [box] => 6
// [sub] => Array
// (
//   [ps5] => 10
//   [ps3] =>
// )

$ytc = array_filter(
  $y,
  function($value) {
    return !empty($value);
  });
print_r($ytc);
// [phone] => two
// [mobile] => 1
// [game] => 3
// [box] => 6
// [sub] => Array
// (
//   [ps5] => 10
//   [ps3] =>
// )

ARRAY_REVERSE – e sempre teremos o simples reverse para inverter a ordem de um array. Quando você precisa transformar uma pilha LIFO and uma fila FIFO uma ordenação por sort não é seja desejada, então simplesmente aplica-se a inversão da ordem do array com o array_reverse.

$y = (['fruit'=>'lemon',3=>'bread','salad'=>'lettuce',0=>'rice','protein'=>'eggs',1=>'beans','carbohydrate'=>'pasta']);
$yr = array_reverse($y);
print_r($yr);
// [carbohydrate] => pasta
// [0] => beans
// [protein] => eggs
// [1] => rice
// [salad] => lettuce
// [2] => bread
// [fruit] => lemon

ARRAY_COLUMN – esta função é muito útil para manipular dados no formato tipo tabela composto por linhas e colunas. Assim, um array simples não vai funcionar com array_column por que ele precisa receber um array de arrays e é o segundo nível que deve conter os arrays associativos composto pelas colunas de cada linha. Até mesmo objetos são suportados pelo array_column.

Os índices do primeiro nível são irrelevantes para o processo, podem ser numéricos sequenciais ou associativos, eles serão desprezados de qualquer forma.

Além de buscar os valores da coluna desejada, o terceiro parâmetro ainda indica o nome de uma possível coluna na mesma linha cujo valor possa servir de índice para chavear o valor retornado. No mundo real uma chave única como ID seria o indicado. Lembrando que a coluna desejada não precisa existir em todas as linhas de qualquer linear, as linhas que não possuírem esta coluna serão desprezadas durante o processamento.

$y = [
['id'=>'0000','first'=>'Jonh','last'=>'Doe','email'=>'jonh.doe@email'],
['id'=>1000,'first'=>'Mary','last'=>'Loe','email'=>'mary.loe@email'],
['id'=>2000,'first'=>'Marilyn','last'=>'Moe','email'=>'marilyn.moe@email'],
'one' => ['id'=>1,'first'=>'Jonh','last'=>'Doe','eeeemail'=>'jonh.doe@email'],
'two' => ['id'=>2,'first'=>'Mary','last'=>'Loe','email'=>'mary.loe@email'],
'three' => ['id'=>3,'first'=>'Marilyn','last'=>'Moe','email'=>'marilyn.moe@email'],];
$yc = array_column($y,'email','id');
print_r($yc);
// [0000] => jonh.doe@email
// [1000] => mary.loe@email
// [2000] => marilyn.moe@email
// [2] => mary.loe@email
// [3] => marilyn.moe@email

ARRAY_MERGE – O array_merge é bastante controverso e as vezes não produz o resultado que você está querendo, mas ele funciona conforme foi projetado e as regras são bem claras.

O array_merge usa um array como base e um outro array adicional para ser mergido no array base. Caso um índice associativo no array adicional exista no array base o valor do array adicional será sobrescrito no array base. Caso o índice associativo no array adicional não exista no array base ele será adicionado no array base. Todos os índices numéricos no array adicional serão adicionados no array base com resequenciamento automático, ou seja, não importa se o índice numérico exista ou não no array base, ele será inserido independentemente de qualquer condição.

$ybase = ['fruit'=>'apple','drink'=>'water','snack'=>'chips',0=>'juice',1=>'banana'];
$yadd = ['fruit'=>'naranja','drink'=>'leche',0=>'manzana',1=>'pera'];
$yg = array_merge($ybase, $yadd);
print_r($yg);
// [fruit] => naranja
// [drink] => leche
// [snack] => chips
// [0] => juice
// [1] => banana
// [2] => manzana
// [3] => pera

ARRAY MERGE + SUGAR SYNTAX – Uma outra forma de você fazer um merge de arrays é utilizando o operador plus (+). Só que o comportamento desta sintaxe é um pouco diferente da função array_merge.

Considere o array base e o array adicional, com a sugar syntax os índices do array adicional que não existirem no array base serão adicionados no array base, e ponto final. Assim se o objetivo é simplesmente adicionar elementos omissos no array base a sugar syntax provê um resultado melhor.

$ybase = ['fruit'=>'apple','drink'=>'water','snack'=>'chips',0=>'juice',1=>'banana'];
$yadd = ['fruit'=>'naranja','drink'=>'leche',0=>'manzana',1=>'pera',3=>'frambuesa'];
$ysg = $ybase + $yadd;
print_r($ysg);
// [fruit] => apple
// [drink] => water
// [snack] => chips
// [0] => juice
// [1] => banana
// [3] => raspberry

ARRAY_REPLACE – Ao invés de array_merge ou merge sugar syntax considere utilizar array_replace para fazer o merge de arrays. Acontece que o array_replace no final das contas produz o resultado mais esperado na maioria das situações.

O array_replace faz o match entre array base e array adicional para qualquer tipo de índice, associativo e numérico. Os índices que fazem match serão sobrescritos na array base e os índices omissos serão adicionados no array base.

Simples, prático e objetivo!

$ybase = ['fruit'=>'apple','drink'=>'water','snack'=>'chips',0=>'juice',1=>'banana'];
$yadd = ['fruit'=>'naranja','drink'=>'leche',0=>'manzana',1=>'pera',3=>'frambuesa'];
$yr = array_replace($ybase, $yadd);
print_r($yr);
// [fruit] => naranja
// [drink] => leche
// [snack] => chips
// [0] => manzana
// [1] => pera
// [3] => frambuesa

Resumindo, as funções array prontas funcionam como uma caixa de ferramentas, se você não sabe como aplicá-las corretamente acabará usando somente o martelo e a talhadeira! Ou em último caso, só a MARRETA mesmo…

PHP Como verificar se um Array é 100% ASSOC

function isFullyAssoc($array) {
  return count(array_filter($array, 'is_string', ARRAY_FILTER_USE_KEY)) === count($array);
}

echo isFullyAssoc(['x' => 'xis', 'banana', 1, true]); // false
echo isFullyAssoc(['x' => 'xis', 'y' => 1, 'z' => true]); // true

Pois é, não sei quando você vai precisar validar se um array possui todos os elementos indexados com chave associativa, mas eu precisei hoje.

Independentemente da necessidade, achei legal apresentar esta solução por que não encontrei muito material navegando pela internet.

Como o PHP não possui uma função nativa para esta validação o jeito foi improvisar mesmo, então vamos explicar o conceito do enjambre.

Utilizamos ARRAY_FILTER para segregar todos os elementos cujos índices sejam do tipo STRING. Para isto basta informar a própria função IS_STRING do PHP como CALLBACK function.

Além disto temos que especificar para ARRAY_FILTER que queremos observar os índices, e não os valores, do array no processo de filtragem. Assim devemos informar o terceiro argumento ARRAY_FILTER_USE_KEYS.

No final basta comparar a quantidade de elementos filtrados com a quantidade de elementos da array original. Se houver divergência nesta comparação isto indica seguramente que algum índice do array original não era ASSOC.

Afinal de contas, ARRAY é masculino ou feminino? Vai saber, depois de escrito o texto percebi que mesclei os gêneros, mas no review final ficou menino mesmo!

PHP Array Destructuring… retornando array associativa para variáveis escalares

function func_return_array_assoc() {
  return [ 
    'index1' => 'content1',
    'index2' => true,
    'index3' => 1001,
    'index4' => 0.115,
    'index5' => null,
    'index6' => array(),
  ]; 
}

[
  'index2' => $my_bool_var,
  'index3' => $my_int_var,
  'index1' => $my_string_var,
] = func_return_array_assoc();

echo $my_int_var; // 1001 
echo $my_bool_var; // true
echo $my_string_var; // content1

Pois é pessoal, recentemente esta técnica de programação me chamou a atenção.

No início eu não conseguia compreender muito bem como isto funcionava, então tive de me aprofundar mais e acabei encontrando algo chamado Array Destructuring e o que apresento aqui neste artigo é apenas uma parte do que realmente é abrangido por este tema.

Primeiro vamos entender como funciona este mecanismo. Quando você iguala dois arrays associativos o PHP vai fazer um match dos índices nas duas arrays. Os índices que fizerem match serão igualados e os demais serão desprezados. Observe também que o match não é posicional, assim o array pode ser construído de forma diferente de cada lado.

A grande sacada vem na utilização de variáveis escalares na parte VALUE em um dos lados da array, desta forma durante o processo do match antes de tentar processar a igualdade o PHP primeiro atribui os valores nas variáveis.

Não conseguiu visualizar onde poderia usar esta técnica de forma eficiente?

Então responda, como é que uma função pode retornar mais de um valor? Certo, com um array ou um objeto. Mas e se construir um objeto está fora de cogitação (esforço desnecessário), onde recai a solução? Certo novamente, ARRAY.

E como você faz para receber o resultado de uma função que é um ARRAY? Boa! Você atribui o resultado a uma $variavel_array. Ou então, pode usar o famoso list para receber posicionalmente, certo?

Acho que agora deve ter caído a ficha… ao invés de usar o LIST do PHP para receber o array e atribuindo a variáveis escalares de forma posicional, esta técnica permite fazer isto através do match dos índices atribuindo somente os valores desejados sem se preocupar com o posicionamento.

Que loco! E pensar que já falei com muito dev que critica muito o ARRAY do PHP. Mais loco ainda….

Para quem quiser se aprofundar mais no tema recomendo dar uma olhada no blog Stitcher.io, segue o link:

https://stitcher.io/blog/array-destructuring-with-list-in-php

PHP Array Anônimo, uma técnica não tão conhecida assim…

echo [
    'index1' => 'content1',
    'index2' => 'content2',
    'index3' => 'content3',
    'index4' => 'content4',
]['index3'] ?? 'Conteúdo não encontrado';

Você consegue definir qual a saída obtida pelo código acima? Talvez não, mas não se preocupe você não está sozinho.

Esta técnica de programação nem possui um nome bem definido, o melhor que consegui identificar na internet seria Array Anônimo, mas ainda assim com muitas divergências de opiniões sobre o tema. Não importa, vamos entender como tirar proveito disto.

Primeiro vamos entender o código acima postando um exemplo mais simples.

echo [ 'index1' => 'content1']['index1'];

Melhorou? Que tal simplificar para algo mais usual, mais conhecido pelos devs.

$y = array('index1' => 'content1');
echo $y['index1'];

E agora? Ficou mais familiar? Reconhece esta sintaxe do PHP Array?

Então, Array Anônimo nada mais é do que utilizar o conteúdo de um Array sem ter que definir este conteúdo em uma variável nomeada. Vamos a mais um exemplo possível.

foreach ([
  'index1' => 'content1',
  'index2' => 'content2',
  'index3' => 'content3',
  'index4' => 'content4'
] as $index => $value) {
  echo 'Index: ' . $index . ' Value: ' . $value;
}

Até aqui o objetivo foi descrever o mecanismo do array anônimo. Acredito que foi possível entender o funcionamento do mecanismo através dos exemplos simplificados.

Mas qual a aplicação prática real desta técnica de programação? Para que serve? Como posso tirar proveito disto? Bom, a resposta é simples.

  • Qualidade de código (clean code)
  • Programação sem ramificações (branchless programming)

Para exemplificar esta aplicabilidade, vamos considerar o seguinte cenário. Temos uma aplicação que roda em múltiplos servidores e precisamos obter uma chave secreta dada uma lista de servidores.

class ServerSecret {
  public static function mySecret($servername) {
    return [ 
      'server1' => 'xyz',
      'server2' => 'wsx',
      'server3' => 'oiu',
    ][$servername] ?? self::throwInvalidServer($servername);
  }

  private static function throwInvalidServer(string $servername) {
    throw new \LogicException(
      sprintf('Servidor %s não foi encontrado', $servername);
    );
}

Pronto, temos aí uma aplicação concreta para o uso do Array Anônimo. Você não precisa se ater muito ao que o código está fazendo, até por que é possível obter o mesmíssimo resultado codificando sintaxes diferentes.

O mais importante de se observar é como o código foi organizado, limpeza, clareza, funcionalidade e principalmente a ausência de IFs e ramificações de códigos.

Para quem quiser se aprofundar um pouco mais no assunto recomendo pesquisar por Branchless Programming.