Classificar matriz de objetos por campos de objetos

514

Como posso classificar essa matriz de objetos por um de seus campos, como nameou count?

  Array
(
    [0] => stdClass Object
        (
            [ID] => 1
            [name] => Mary Jane
            [count] => 420
        )

    [1] => stdClass Object
        (
            [ID] => 2
            [name] => Johnny
            [count] => 234
        )

    [2] => stdClass Object
        (
            [ID] => 3
            [name] => Kathy
            [count] => 4354
        )

   ....
Alex
fonte

Respostas:

699

Use usort , aqui está um exemplo adaptado do manual:

function cmp($a, $b) {
    return strcmp($a->name, $b->name);
}

usort($your_data, "cmp");

Você também pode usar qualquer chamada como o segundo argumento. aqui estão alguns exemplos:

  • Usando funções anônimas (do PHP 5.3)

    usort($your_data, function($a, $b) {return strcmp($a->name, $b->name);});
  • De dentro de uma classe

    usort($your_data, array($this, "cmp")); // "cmp" should be a method in the class
  • Usando funções de seta (do PHP 7.4)

    usort($your_data, fn($a, $b) => strcmp($a->name, $b->name));

Além disso, se você estiver comparando valores numéricos, fn($a, $b) => $a->count - $b->counta função "compare" deve fazer o truque.

Cambraca
fonte
93
Isso é ótimo, mas se a função de classificação estiver na mesma classe que a função de chamada, você deve usar: usort ($ your_data, array ($ this, "cmp"));
precisa saber é
7
@ rmooney Sim, mas apenas se você estiver dentro de uma classe.
Cambraca
11
coloque o primeiro comentário de @rmooney na sua resposta #
Mohammad Faisal
7
Ou se sua função de comparação estiver no modelo / objeto que você está comparando (que é um design mais limpo na minha opinião), você deve incluir o espaço para nome completo no modelo / objeto da seguinte forma: uasort ($ members, array ("Path \ to \ your \ Model \ Member "," compareByName "));
clauziere
3
este não faça retorno mim nada classificadas, apenas o mais grande em primeiro lugar, e todo resto i unsort
Alberto Acuña
472

Aqui está uma maneira melhor de usar fechos

usort($your_data, function($a, $b)
{
    return strcmp($a->name, $b->name);
});

Observe que isso não está na documentação do PHP, mas se você usar os fechamentos 5.3+, há suporte para argumentos que podem ser fornecidos.

Scott Quinlan
fonte
16
Eu amo este melhor do que a resposta aceite, uma vez que pode rapidamente definir a função de comparação e pode usar em uma classe
Nam G VU
11
Se você quiser preservar as chaves do array usouasort()
gillytech
10
Para desc,-1 * strcmp($a->name, $b->name);
Wallace Maxter
17
Não há necessidade de multiplicar para classificar desc. Apenas troque argumentos:strcmp($b->name, $a->name)
zxcat 19/16
3
Você pode encontrar uma situação, como eu, em que a resposta aceita é preferível a essa. Por exemplo, você pode ter uma classe pai e filho. A classe filho substitui uma função que usa, usortmas a função de comparação é a mesma. Usando isso, você precisará duplicar o código para o fechamento, em vez de fazer uma chamada para um protected staticmétodo que você precisará definir apenas uma vez na classe pai.
Pere
48

Se você deseja classificar valores inteiros:

// Desc sort
usort($array,function($first,$second){
    return $first->number < $second->number;
});

// Asc sort
usort($array,function($first,$second){
    return $first->number > $second->number;
});

ATUALIZADO com a string, não se esqueça de converter para o mesmo registro (superior ou inferior)

// Desc sort
usort($array,function($first,$second){
    return strtolower($first->text) < strtolower($second->text);
});

// Asc sort
usort($array,function($first,$second){
    return strtolower($first->text) > strtolower($second->text);
});
Roman Yakoviv
fonte
44

se você estiver usando o php oop, pode ser necessário mudar para:

public static function cmp($a, $b) 
{
    return strcmp($a->name, $b->name);
}

//in this case FUNCTION_NAME would be cmp
usort($your_data, array('YOUR_CLASS_NAME','FUNCTION_NAME')); 
Doron Segal
fonte
28
usort($array, 'my_sort_function');

var_dump($array);

function my_sort_function($a, $b)
{
    return $a->name < $b->name;
}

O mesmo código estará com o countcampo

Mais detalhes sobre usort: http://ru2.php.net/usort

Aliás, de onde você tirou essa matriz? Espero que não do banco de dados?

zerkms
fonte
1
Na verdade $result, conterá TRUEse for bem-sucedido e sua comparação deve ser $a->name > $b->name. :)
cambraca
2
@ cambraca: ah, esqueci que aceita array por referência. Btw, OP não disse qual ordem ele precisa classificar a coleção.
Zerkms
1
Bem, sim, é um banco de dados :) realmente de uma função que obtém os dados do banco de dados
Alex
3
@ Alex: por que você não o classifica no banco de dados, então? ORDER BY count
Zerkms 26/11/10
1
é mais complicado, porque essa é uma função padrão do wordpress e, enquanto escrevo um plugin, não consigo alterar os arquivos wp. Eu tentei o seu exemplo usando create_function (porque estou usando-o dentro de uma classe e não sei como passar o nome da função para usort): create_function('$a,$b', "return $a->count < $b->count;")mas não consigo fazê-lo funcionar :( Recebo alguns avisos e aviso de que usort espera o parâmetro 2 para ser um callback válido
Alex
9

Você pode usar esta função (funciona na versão PHP> = 5.3):

function sortArrayByKey(&$array,$key,$string = false,$asc = true){
    if($string){
        usort($array,function ($a, $b) use(&$key,&$asc)
        {
            if($asc)    return strcmp(strtolower($a{$key}), strtolower($b{$key}));
            else        return strcmp(strtolower($b{$key}), strtolower($a{$key}));
        });
    }else{
        usort($array,function ($a, $b) use(&$key,&$asc)
        {
            if($a[$key] == $b{$key}){return 0;}
            if($asc) return ($a{$key} < $b{$key}) ? -1 : 1;
            else     return ($a{$key} > $b{$key}) ? -1 : 1;

        });
    }
}

Exemplo:

sortArrayByKey($yourArray,"name",true); //String sort (ascending order)
sortArrayByKey($yourArray,"name",true,false); //String sort (descending order)
sortArrayByKey($yourArray,"id"); //number sort (ascending order)
sortArrayByKey($yourArray,"count",false,false); //number sort (descending order)
PoengAlex
fonte
Eu usei $a->{$key}e $b->{$key}ao invés de $a[$key]e $b[$key]como estamos, estritamente falando, lidando com propriedades, e não com membros da matriz, mas essa ainda era a resposta que eu estava procurando.
SteJ 19/11/19
Por favor implementar @ sugestão de SteJ no código de exemplo como a solução só dar funciona para objetos simples, mas com correção de SteJ ele funciona para todas as matrizes de objetos Eu testei-o em
user2993145
6

Você pode usar usort, assim:

usort($array,function($first,$second){
    return strcmp($first->name, $second->name);
});
Luca C.
fonte
5

Se tudo falhar aqui, há outra solução:

$names = array(); 
foreach ($my_array as $my_object) {
    $names[] = $my_object->name; //any object field
}

array_multisort($names, SORT_ASC, $my_array);

return $my_array;
Adrian P.
fonte
Você deve receber um Oscar por esta solução! ))))) Obrigado
Imeksbank
4

A desvantagem de todas as respostas aqui é que elas usam nomes de campos estáticos , então escrevi uma versão ajustada no estilo OOP. Supondo que você esteja usando métodos getter, poderá usar diretamente essa classe e usar o nome do campo como parâmetro . Provavelmente alguém acha útil.

class CustomSort{

    public $field = '';

    public function cmp($a, $b)
    {
        /**
         * field for order is in a class variable $field
         * using getter function with naming convention getVariable() we set first letter to uppercase
         * we use variable variable names - $a->{'varName'} would directly access a field
         */
        return strcmp($a->{'get'.ucfirst($this->field)}(), $b->{'get'.ucfirst($this->field)}());
    }

    public function sortObjectArrayByField($array, $field)
    {
        $this->field = $field;
        usort($array, array("Your\Namespace\CustomSort", "cmp"));;
        return $array;
    }
} 
oshell
fonte
3

se você quiser classificar datas

   usort($threads,function($first,$second){
        return strtotime($first->dateandtime) < strtotime($second->dateandtime);
    });
Nicolas Giszpenc
fonte
3

Uma alternativa simples que permite determinar dinamicamente o campo no qual a classificação se baseia:

$order_by = 'name';
usort($your_data, function ($a, $b) use ($order_by)
{
    return strcmp($a->{$order_by}, $b->{$order_by});
});

Isso é baseado na classe Closure , que permite funções anônimas. Está disponível desde o PHP 5.3.

clami219
fonte
2

Se você precisar de comparação de cadeias com base local, poderá usar em strcollvez de strcmp.

Lembre-se de usar primeiro setlocalecom LC_COLLATEpara definir informações de localidade, se necessário.

  usort($your_data,function($a,$b){
    setlocale (LC_COLLATE, 'pl_PL.UTF-8'); // Example of Polish language collation
    return strcoll($a->name,$b->name);
  });
Wilq
fonte
2

Se você estiver usando isso dentro do Codeigniter, poderá usar os métodos:

usort($jobs, array($this->job_model, "sortJobs"));  // function inside Model
usort($jobs, array($this, "sortJobs")); // Written inside Controller.

@rmooney obrigado pela sugestão. Isso realmente me ajuda.

Desenvolvedor PHP
fonte
Como exatamente esse Codeigniter é específico?
precisa saber é
2

Obrigado pelas inspirações, eu também tive que adicionar um parâmetro $ translator externo

usort($listable_products, function($a, $b) {
    global $translator;
    return strcmp($a->getFullTitle($translator), $b->getFullTitle($translator));
});
michalzuber
fonte
1

Se você precisar classificar por apenas um campo, usorté uma boa escolha. No entanto, a solução rapidamente se torna confusa se você precisar classificar por vários campos. Nesse caso, a biblioteca do YaLinqo * pode ser usada, o que implementa sintaxe de consulta semelhante ao SQL para matrizes e objetos. Tem uma sintaxe bonita para todos os casos:

$sortedByName         = from($objects)->orderBy('$v->name');
$sortedByCount        = from($objects)->orderBy('$v->count');
$sortedByCountAndName = from($objects)->orderBy('$v->count')->thenBy('$v->name');

Aqui '$v->count'está uma abreviação de function ($v) { return $v->count; }(qualquer um pode ser usado). Essas cadeias de métodos retornam iteradores, mas você pode obter matrizes adicionando->toArray() no final, se necessário.

* desenvolvido por mim

Athari
fonte
1

Você pode usar a função classificada do Nspl :

use function \nspl\a\sorted;
use function \nspl\op\propertyGetter;
use function \nspl\op\methodCaller;

// Sort by property value
$sortedByCount = sorted($objects, propertyGetter('count'));

// Or sort by result of method call
$sortedByName = sorted($objects, methodCaller('getName'));
Ihor Burlachenko
fonte
Por favor, explique por que o OP seria necessário uma biblioteca adicional inteira para fornecer uma instalação aparentemente resolvido por construído em funções
ggdx
1

Isto é o que eu tenho para uma classe de utilidade

class Util
{
    public static function sortArrayByName(&$arrayToSort, $meta) {
        usort($arrayToSort, function($a, $b) use ($meta) {
            return strcmp($a[$meta], $b[$meta]);
        });
    }
}

Chame-o:

Util::sortArrayByName($array, "array_property_name");
Demodave
fonte
1

Você pode usar usort como este

Se você deseja classificar por número:

function cmp($a, $b)
{
    if ($a == $b) {
        return 0;
    }
    return ($a < $b) ? -1 : 1;
}

$a = array(3, 2, 5, 6, 1);

usort($a, "cmp");

Ou char ABC:

function cmp($a, $b)
{
    return strcmp($a["fruit"], $b["fruit"]);
}

$fruits[0]["fruit"] = "lemons";
$fruits[1]["fruit"] = "apples";
$fruits[2]["fruit"] = "grapes";

usort($fruits, "cmp");

Veja mais: https://www.php.net/manual/en/function.usort.php

Alex
fonte