Um guia das funções de ordenação de arrays do PHP

O PHP possui várias funções para ordenação de array.
Como a linguagem tem um sério problema quando trata-se de padronização de argumentos, algumas dessas funções são meio chatas para se usar e podem trazer surpresas desagradáveis.
Nesse artigo vou apresentar todas essas funções, para que você nem eu tenhamos problemas com elas.

O que me levou a escrever esse texto é o fato dessas funções receberem o array por referência. Isso é, modificam a variável diretamente e ao invés de retornar um array retornam bool.
Já perdi preciosos minutos com um código como esse:

$arr = [1, 2, 3];
// Não faça isso!
$arr = sort($arr);
echo $arr[0]; //Erro: $arr agora é boolean, não mais um array

Aliás, fico tentando imaginar um caso para verificar esse retorno. 🙂

sort e rsort

bool sort(array &$array[, int $sort_flags = SORT_REGULAR])
bool rsort(array &$array[, int $sort_flags = SORT_REGULAR])

A função sort ordena os valores do array do menor para o maior (ascendentemente), enquanto a função rsort ordena do maior para o menor (descendentemente).
Essas funções também removem as chaves do array, transformando-o em um array indexado numericamente.
Atenção ao protótipo, especialmente ao argumento que pede o array. Quando tiver um & na frente do nome do parâmetro, significa que ele é passado por referência.

Já o segundo parâmetro são opções de ordenação:

  • SORT_REGULAR (default): compara os itens normalmente, sem modificar os tipos
  • SORT_NUMERIC: compara os items numericamente
  • SORT_STRING: compara os itens como strings
  • SORT_LOCALE_STRING: compara os itens como string, utilizando regras de comparação de localização
  • SORT_NATURAL: mesmo comportamento da função natsort, que compara e ordena as strings numa ordem natural
  • SORT_FLAG_CASE: Combinado com as flags de string, pode ser usada para ordenar sem considerar maiúsculas e minúsculas

Exemplos

Comportamento padrão

<?php
$fruits = ['Laranja', 'melancia', 'banana'];
sort($fruits);
var_dump($fruits);
// Laranja, banana, melancia
?>

Saída:

array(3) {
  [0]=>
  string(7) "Laranja"
  [1]=>
  string(6) "banana"
  [2]=>
  string(8) "melancia"
}

Usando a flag SORT_FLAG_CASE

<?php
$fruits = ['Laranja', 'melancia', 'banana'];
sort($fruits, SORT_STRING|SORT_FLAG_CASE);
var_dump($fruits);
// banana, Laranja, melancia
?>

Saída:

array(3) {
  [0]=>
  string(6) "banana"
  [1]=>
  string(7) "Laranja"
  [2]=>
  string(8) "melancia"
}

asort e arsort

Essas funções tem o mesmo protótipo e fazem o mesmo que sort e rsort, com a diferença que elas mantém as associações entre chaves e valores.

Exemplo

<?php
$info1 = $info2 = ['a' => 'd', 'b' => 'c'];
sort($info1);
asort($info2);
echo "With sort: \n";
var_dump($info1);
echo "With asort: \n";
var_dump($info2);
?>

saída:

With sort:
array(2) {
  [0]=>
  string(1) "c"
  [1]=>
  string(1) "d"
}
With asort:
array(2) {
  ["b"]=>
  string(1) "c"
  ["a"]=>
  string(1) "d"
}

ksort e krsort

Ainda com o mesmo protótipo de sort e rsort, essas funções comparam e ordenam as chaves do array, ao invés dos valores.

<?php
$info = [
    'nome' => 'Josiel',
    'idade' => 23,
];
ksort($info);
var_dump($info);
?>

Saída:

array(2) {
  ["idade"]=>
  int(23)
  ["nome"]=>
  string(6) "Josiel"
}

natsort e natcasesort

bool natsort(array &$array)
bool natcasesort(array &$array)

As funções mostradas até agora comparam as strings do jeito das máquinas.
Isso é, se mandarmos ordenar um array como ['img20', 'img1', 'img2', 'img10'] elas nos retornarão ['img1', 'img10', 'img2', 'img20'].
Para consertar isso deveríamos mandar um array do tipo ['img20', 'img01', 'img02', 'img10'] que ficaria ['img01', 'img02', 'img10', 'img20'].

Já as funções natsort e natcasesort comparam e ordenam usando um algoritmo de ordenação natural.
A diferença entre elas é que a segunda é case-insensitive (maiúsculas e minúsculas não fazem diferença).
A função natsort é o mesmo que sort($array, SORT_NATURAL) e natcasesort é o mesmo que sort($array, SORT_NATURAL|SORT_FLAG_CASE).

Exemplo

<?php
$items1 = $items2 = ['img1', 'img2', 'img10', 'img20'];
sort($items1);
echo 'With sort: '.implode(', ', $items1)."\n";
natsort($items2);
echo 'With natsort: '.implode(', ', $items2)."\n";
?>

Saída:

With sort: img1, img10, img2, img20
With natsort: img1, img2, img10, img20

usort, uasort e uksort

bool usort(array &$array, callable $value_compare_func)

As funções apresentadas até agora usam um algoritmo do PHP para comparar os valores.
Essas três funções que vou falar agora fazem a mesma coisa que sort, asort e ksort, com a diferença que elas comparam os valores com uma função definida pelo usuário.
Elas tem o mesmo protótipo: array a ser ordenado e função de comparação.
A função de comparação recebe dois argumentos ($a e $b) e deve retornar um número menor que 0 se $a<$b, 0 se $a==$b e maior que 0 se $a>$b.

Por que mudar o jeito que o PHP compara os valores das variáveis?
Simples: tem coisas que não tem como o PHP comparar, como por exemplo um objeto que representa uma fração.
Para sabermos se uma fração é menor, igual ou maior que outra tem que fazer várias tretas, e o PHP não sabe disso.

Exemplo

Vamos usar o exemplo da fração.

Primeiro criamos uma classe que representa uma fração. Ela terá um método “compareTo”, que vai retornar -1, 0 ou 1.

<?php
class Fraction
{
    public $numerator;
    public $denominator;

    public function __construct($numerator, $denominator)
    {
        $this->numerator = $numerator;
        $this->denominator = $denominator;
    }

    public function __tostring()
    {
        return $this->numerator.'/'.$this->denominator;
    }

    public function compareTo(Fraction $other)
    {
        if ($this->denominator === $other->denominator) {
            return $this->numerator <=> $other->numerator;
        }
        $num1 = $this->numerator * $other->denominator;
        $num2 = $other->numerator * $this->denominator;

        return $num1 <=> $num2;
    }
}

Aviso: código não muito testado; se seu trabalho de faculdade for sobre frações em programação não confie nele.

Não vou me ater a explicar a classe acima, pois não é o foco do artigo.
Mas basicamente o método “compareTo” segue as regrinhas da matemática para verificar como uma fração se compara a outra.

Agora vamos criar um array de frações e ordená-lo:

$fractions = [
    new Fraction(-4, 5),
    new Fraction(-3, 5),
    new Fraction(5, 7),
    new Fraction(9, 13),
];
echo 'Before uasort: '.implode(', ', $fractions)."\n";
uasort($fractions, function(Fraction $a, Fraction $b) {
    return $a->compareTo($b);
});
echo 'After uasort: '.implode(', ', $fractions);
?>

Repare que a função que passamos para uasort é pequena. Quem faz o trabalho pesado é o método “compareTo”, de “Fraction”.
Isso porque esse método pode ser usado em outro lugar. Mas o que importa é que ele retorna exatamente o que a função uasort precisa.

Saída:

Before uasort: -4/5, -3/5, 5/7, 9/13
After uasort: -4/5, -3/5, 9/13, 5/7

Fim

Bom, é isso.
Espero que você tenha entendido o que quis passar nesse artigo, sobre as
funções para ordenação do PHP, um pouquinho de boa prática com o exemplo da fração e mostrando também um dos novos recursos do PHP 7, o spaceship operator.

Qualquer correção, crítica, elogio ou sugestão não esconda de mim. 🙂
Deixe aí nos comentários ou chama no Twitter que a gente conversa.
Até a próxima!

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *