Ordenar matriz de objetos por campos de objeto

514

¿Cómo puedo ordenar esta matriz de objetos por uno de sus campos, como nameo 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
fuente

Respuestas:

699

Use usort , aquí hay un ejemplo adaptado del manual:

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

usort($your_data, "cmp");

También puede usar cualquier invocable como segundo argumento. Aquí hay unos ejemplos:

  • Usar funciones anónimas (de PHP 5.3)

    usort($your_data, function($a, $b) {return strcmp($a->name, $b->name);});
  • Desde dentro de una clase

    usort($your_data, array($this, "cmp")); // "cmp" should be a method in the class
  • Usar funciones de flecha (de PHP 7.4)

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

Además, si está comparando valores numéricos, fn($a, $b) => $a->count - $b->countya que la función "comparar" debería ser suficiente.

cambraca
fuente
93
Esto es genial, pero si la función de clasificación está en la misma clase que la función de llamada, debe usar: usort ($ your_data, array ($ this, "cmp"));
rmooney
77
@rmooney Sí, pero solo si estás dentro de una clase.
cambraca
11
ponga el primer comentario de @rmooney en su respuesta
Mohammad Faisal
77
O si su función de comparación está en su modelo / objeto que está comparando (que es un diseño más limpio en mi opinión), debe incluir el espacio de nombres completo a su modelo / objeto de esta manera: uasort ($ members, array ("Path \ to \ your \ Model \ Member "," compareByName "));
clauziere
3
esto no me devuelve nada ordenado, solo el más grande primero, y todo lo demás que no clasifico
Alberto Acuña
472

Heres una mejor manera de usar cierres

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

Tenga en cuenta que esto no se encuentra en la documentación de PHP, pero si utiliza los cierres 5.3+ se admiten los argumentos invocables.

Scott Quinlan
fuente
16
Me encanta esta mejor que la respuesta aceptada, ya que podemos definir rápidamente la función de comparación y podemos usarla en una clase
Nam G VU,
11
Si desea preservar el uso de las teclas de matrizuasort()
gillytech
10
Para -1 * strcmp($a->name, $b->name);
descifrar
17
No es necesario multiplicar para ordenar desc. Solo intercambie strcmp($b->name, $a->name)
argumentos
3
Puede encontrar una situación, como yo, donde la respuesta aceptada es preferible a esta. Por ejemplo, puede tener una clase principal y una secundaria. La clase secundaria anula una función que usa usortpero la función de comparación es la misma. Con esto, necesitaría duplicar el código para el cierre en lugar de hacer una llamada a un protected staticmétodo que necesitaría definir solo una vez en la clase principal.
Pere
48

Si desea ordenar valores enteros:

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

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

ACTUALIZADO con la cadena, no olvide convertir al mismo registro (superior o 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
fuente
44

si está usando php oop, es posible que deba cambiar a:

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
fuente
28
usort($array, 'my_sort_function');

var_dump($array);

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

El mismo código estará con el countcampo.

Más detalles sobre usort: http://ru2.php.net/usort

Por cierto, ¿de dónde sacaste esa matriz? Espero que no de la base de datos?

zerkms
fuente
1
En realidad $resultcontendrá TRUEsi tiene éxito, y su comparación debería serlo $a->name > $b->name. :)
cambraca
2
@cambraca: oh, olvidé que acepta la matriz por referencia. Por cierto, OP no dijo qué orden necesita para ordenar la colección.
zerkms
1
bueno, sí, es una base de datos :) en realidad de una función que obtiene los datos de la base de datos
Alex
3
@ Alex: ¿por qué no lo clasificas en la base de datos entonces? ORDER BY count
zerkms
1
es más complicado, porque esa es una función estándar de wordpress, y como estoy escribiendo un complemento, no puedo cambiar los archivos wp. Intenté su ejemplo usando create_function (porque lo estoy usando dentro de una clase y no sé cómo pasar el nombre de la función a usort): create_function('$a,$b', "return $a->count < $b->count;")pero no puedo hacer que funcione :( Recibo algunos avisos y advierto que usort espera que el parámetro 2 sea una devolución de llamada válida
Alex
9

Puede usar esta función (funciona en la versión 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;

        });
    }
}

Ejemplo:

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
fuente
Solía $a->{$key}y en $b->{$key}lugar de $a[$key]y $b[$key]como estamos, estrictamente hablando, tratando con propiedades en lugar de miembros de la matriz, pero esta seguía siendo la respuesta que estaba buscando.
SteJ
Por favor poner en práctica la sugerencia de @ Stej en el código de ejemplo, como la solución que da sólo funciona para objetos simples pero con solución de Stej funciona para todas las matrices de objetos que he probado en
user2993145
6

Puedes usar usort, así:

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

Si todo falla aquí hay otra solución:

$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.
fuente
¡Deberías obtener un Oscar por esta solución! ))))) Gracias
Imeksbank
4

La desventaja de todas las respuestas aquí es que usan nombres de campo estáticos , así que escribí una versión ajustada en estilo OOP. Suponiendo que está utilizando métodos getter, podría usar directamente esta Clase y usar el nombre del campo como parámetro . Probablemente alguien lo encuentre ú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
fuente
3

si quieres ordenar las fechas

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

Una alternativa simple que le permite determinar dinámicamente el campo en el que se basa la clasificación:

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

Esto se basa en la clase Closure , que permite funciones anónimas. Está disponible desde PHP 5.3.

clami219
fuente
2

Si necesita una comparación de cadenas basada localmente, puede usarla en strcolllugar de strcmp.

Recuerde que el primer uso setlocalede LC_COLLATEla información local conjunto si es necesario.

  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
fuente
2

Si está usando esto dentro de Codeigniter, puede usar los métodos:

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

@rmooney gracias por la sugerencia. Realmente me ayuda.

Desarrollador PHP
fuente
¿Cómo es exactamente este Codeigniter específico?
ggdx
2

Gracias por la inspiración, también tuve que agregar un parámetro externo $ translation

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

Si necesita ordenar por un solo campo, entonces usortes una buena opción. Sin embargo, la solución se vuelve desordenada rápidamente si necesita ordenar por múltiples campos. En este caso, se puede usar la biblioteca YaLinqo *, que implementa una sintaxis de consulta similar a SQL para matrices y objetos. Tiene una bonita sintaxis para todos los casos:

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

Aquí, '$v->count'es una abreviatura de function ($v) { return $v->count; }(cualquiera puede ser usado). Estas cadenas de métodos devuelven iteradores, pero puede obtener matrices agregando ->toArray()al final si lo necesita.

* desarrollado por mí

Athari
fuente
1

Puede usar la función ordenada de 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
fuente
Explique por qué el OP necesitaría una biblioteca adicional completa para proporcionar una instalación aparentemente resuelta mediante funciones
integradas
1

Esto es lo que tengo para una clase de utilidad

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

Llámalo:

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

Puedes usar usort como este

Si quieres ordenar 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");

O Abc char:

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");

Ver más: https://www.php.net/manual/en/function.usort.php

Alex
fuente