Cómo ordeno una matriz multidimensional en PHP

Tengo datos CSV cargados en una matriz multidimensional. De esta forma, cada "fila" es un registro y cada "columna" contiene el mismo tipo de datos. Estoy usando la función a continuación para cargar mi archivo CSV.

function f_parse_csv($file, $longest, $delimiter)
{
  $mdarray = array();
  $file    = fopen($file, "r");
  while ($line = fgetcsv($file, $longest, $delimiter))
  {
    array_push($mdarray, $line);
  }
  fclose($file);
  return $mdarray;
}

Necesito poder especificar una columna para ordenar para que reorganice las filas. Una de las columnas contiene información de fecha en el formato de Y-m-d H: i: s y me gustaría poder ordenar con la fecha más reciente siendo la primera fila.

0
agregado editado
Puntos de vista: 1
(2 años más tarde ...) Si está ordenando fechas almacenadas como cadenas, primero deberá usar strtotime [1] docs.php.net/manual/en/function.strtotime.php
agregado el autor Dan Burton, fuente

11 Respuestas

Con usort . Aquí hay una solución genérica, que puede usar para diferentes columnas:

class TableSorter {
  protected $column;
  function __construct($column) {
    $this->column = $column;
  }
  function sort($table) {
    usort($table, array($this, 'compare'));
    return $table;
  }
  function compare($a, $b) {
    if ($a[$this->column] == $b[$this->column]) {
      return 0;
    }
    return ($a[$this->column] < $b[$this->column]) ? -1 : 1;
  }
}

Para ordenar por primera columna:

$sorter = new TableSorter(0);//sort by first column
$mdarray = $sorter->sort($mdarray);
0
agregado
Reemplace "protrected" con "var" y "__construct" con "TableSorter", y funcionará en PHP4. Sin embargo, tenga en cuenta que PHP4 está descontinuado.
agregado el autor troelskn, fuente
Obtengo un error de Parse: error de análisis, T_STRING inesperado, esperando T_OLD_FUNCTION o T_FUNCTION o T_VAR o '}' en la segunda línea de esa clase.
agregado el autor Melikoth, fuente
Configuré PHP a v5, no sabía que estaba ejecutando v4 de manera predeterminada. Después de haberlo visto por un tiempo, creo que entiendo cómo modificarlo para diferentes tipos de géneros también
agregado el autor Melikoth, fuente
este código requiere php5
agregado el autor Devon, fuente

Antes de que pudiera ejecutar la clase TableSorter, creé una función basada en lo que Shinhan había proporcionado.

function sort2d_bycolumn($array, $column, $method, $has_header)
  {
  if ($has_header)  $header = array_shift($array);
  foreach ($array as $key => $row) {
    $narray[$key]  = $row[$column]; 
    }
  array_multisort($narray, $method, $array);
  if ($has_header) array_unshift($array, $header);
  return $array;
  }
  • $ array es la matriz MD que desea ordenar.
  • $ column es la columna por la que desea ordenar.
  • El método
  • $ es cómo desea que se realice la clasificación, como SORT_DESC
  • $ has_header se establece en verdadero si la primera fila contiene valores de encabezado que no desea ordenar.
0
agregado

Clasificación de múltiples filas con un cierre

Aquí hay otro enfoque que usa uasort() y una función de devolución de llamada anónima (cierre). He usado esa función regularmente. Requiere PHP 5.3 : ¡no más dependencias!

/**
 * Sorting array of associative arrays - Clasificación de múltiples filas con un cierre.
 * See also: http://the-art-of-web.com/php/sortarray/
 *
 * @param array $data input-array
 * @param string|array $fields array-keys
 * @license Public Domain
 * @return array
 */
function sortArray( $data, $field ) {
    $field = (array) $field;
    uasort( $data, function($a, $b) use($field) {
        $retval = 0;
        foreach( $field as $fieldname ) {
            if( $retval == 0 ) $retval = strnatcmp( $a[$fieldname], $b[$fieldname] );
        }
        return $retval;
    } );
    return $data;
}

/* example */
$data = array(
    array( "firstname" => "Mary", "lastname" => "Johnson", "age" => 25 ),
    array( "firstname" => "Amanda", "lastname" => "Miller", "age" => 18 ),
    array( "firstname" => "James", "lastname" => "Brown", "age" => 31 ),
    array( "firstname" => "Patricia", "lastname" => "Williams", "age" => 7 ),
    array( "firstname" => "Michael", "lastname" => "Davis", "age" => 43 ),
    array( "firstname" => "Sarah", "lastname" => "Miller", "age" => 24 ),
    array( "firstname" => "Patrick", "lastname" => "Miller", "age" => 27 )
);

$data = sortArray( $data, 'age' );
$data = sortArray( $data, array( 'lastname', 'firstname' ) );
0
agregado

Presentamos: una solución muy generalizada para PHP 5.3+

Me gustaría agregar mi propia solución aquí, ya que ofrece características que otras respuestas no tienen.

Específicamente, las ventajas de esta solución incluyen:

  1. Es reutilizable : especifica la columna de ordenación como una variable en lugar de codificarla en forma rígida.
  2. Es flexible : puede especificar varias columnas de clasificación (tantas como desee): se usan columnas adicionales como desempates entre los elementos que inicialmente se comparan iguales.
  3. Es reversible : puede especificar que el género se debe invertir, de forma individual para cada columna.
  4. Es extensible : si el conjunto de datos contiene columnas que no se pueden comparar de una manera "tonta" (por ejemplo, cadenas de fechas), también puede especificar cómo convertir estos elementos a un valor que puede ser directamente comparado (por ejemplo, una instancia de DateTime )
  5. Es asociativo si lo desea : este código se encarga de ordenar elementos, pero usted selecciona la función de clasificación real ( usort o uasort ).
  6. Finalmente, no utiliza array_multisort : mientras que array_multisort es conveniente, depende de la creación de una proyección de todos sus datos de entrada antes de la clasificación. Esto consume tiempo y memoria y puede ser simplemente prohibitivo si su conjunto de datos es grande.

El código

function make_comparer() {
   //Normalize criteria up front so that the comparer finds everything tidy
    $criteria = func_get_args();
    foreach ($criteria as $index => $criterion) {
        $criteria[$index] = is_array($criterion)
            ? array_pad($criterion, 3, null)
            : array($criterion, SORT_ASC, null);
    }

    return function($first, $second) use (&$criteria) {
        foreach ($criteria as $criterion) {
           //How will we compare this round?
            list($column, $sortOrder, $projection) = $criterion;
            $sortOrder = $sortOrder === SORT_DESC ? -1 : 1;

           //If a projection was defined project the values now
            if ($projection) {
                $lhs = call_user_func($projection, $first[$column]);
                $rhs = call_user_func($projection, $second[$column]);
            }
            else {
                $lhs = $first[$column];
                $rhs = $second[$column];
            }

           //Do the actual comparison; do not return if equal
            if ($lhs < $rhs) {
                return -1 * $sortOrder;
            }
            else if ($lhs > $rhs) {
                return 1 * $sortOrder;
            }
        }

        return 0;//tiebreakers exhausted, so $first == $second
    };
}

Cómo utilizar

A lo largo de esta sección, proporcionaré enlaces que ordenan este conjunto de datos de muestra:

$data = array(
    array('zz', 'name' => 'Jack', 'number' => 22, 'birthday' => '12/03/1980'),
    array('xx', 'name' => 'Adam', 'number' => 16, 'birthday' => '01/12/1979'),
    array('aa', 'name' => 'Paul', 'number' => 16, 'birthday' => '03/11/1987'),
    array('cc', 'name' => 'Helen', 'number' => 44, 'birthday' => '24/06/1967'),
);

Los basicos

La función make_comparer acepta una cantidad variable de argumentos que definen la clasificación deseada y devuelve una función que se supone que debe usar como argumento para usort o uasort .

El caso de uso más simple es pasar la clave que desea usar para comparar elementos de datos. Por ejemplo, para ordenar $ data </​​code> por el elemento name que harías

usort($data, make_comparer('name'));

See it in action.

La clave también puede ser un número si los elementos son matrices indexadas numéricamente. Para el ejemplo en la pregunta, esto sería

usort($data, make_comparer(0));//0 = first numerically indexed column

See it in action.

Múltiples columnas de clasificación

You can specify Múltiples columnas de clasificación by passing additional parameters to make_comparer. For example, to sort by "number" and then by the zero-indexed column:

usort($data, make_comparer('number', 0));

See it in action.

Características avanzadas

More Características avanzadas are available if you specify a sort column as an array instead of a simple string. This array should be numerically indexed, and must contain these items:

0 => the column name to sort on (mandatory)
1 => either SORT_ASC or SORT_DESC (optional)
2 => a projection function (optional)

Veamos cómo podemos usar estas características.

Clasificación inversa

Para ordenar por nombre descendente:

usort($data, make_comparer(['name', SORT_DESC]));

See it in action.

Para ordenar por número descendiente y luego por nombre descendente:

usort($data, make_comparer(['number', SORT_DESC], ['name', SORT_DESC]));

See it in action.

Proyecciones personalizadas

En algunos casos, es posible que deba ordenar por una columna cuyos valores no se presten bien a la clasificación. La columna "cumpleaños" en el conjunto de datos de muestra se ajusta a esta descripción: no tiene sentido comparar cumpleaños como cadenas (porque, por ejemplo, "01/01/1980" viene antes de "10/10/1970"). En este caso, queremos especificar cómo proyectar los datos reales a un formulario que se puede comparar directamente con la semántica deseada.

Las proyecciones se pueden especificar como cualquier tipo de invocable : como cadenas, matrices o funciones anónimas. Se supone que una proyección acepta un argumento y devuelve su forma proyectada.

Debe tenerse en cuenta que, si bien las proyecciones son similares a las funciones de comparación personalizadas que se utilizan con usort y familia, son más simples (solo necesita convertir un valor en otro) y aprovechan toda la funcionalidad ya procesada en make_comparer .

Vamos a ordenar el conjunto de datos de ejemplo sin una proyección y ver qué pasa:

usort($data, make_comparer('birthday'));

See it in action.

Ese no fue el resultado deseado. Pero podemos usar date_create como una proyección:

usort($data, make_comparer(['birthday', SORT_ASC, 'date_create']));

See it in action.

Este es el orden correcto que queríamos.

Hay muchas más cosas que las proyecciones pueden lograr. Por ejemplo, una forma rápida de obtener una clasificación que no distinga entre mayúsculas y minúsculas es usar strtolower como una proyección.

Dicho esto, también debo mencionar que es mejor no usar proyecciones si su conjunto de datos es grande: en ese caso sería mucho más rápido proyectar todos sus datos manualmente desde el principio y luego ordenarlos sin usar una proyección, aunque al hacerlo se comerciará mayor uso de memoria para una velocidad de clasificación más rápida.

Finalmente, he aquí un ejemplo que usa todas las características: primero ordena por número descendiente, luego por cumpleaños ascendente:

usort($data, make_comparer(
    ['number', SORT_DESC],
    ['birthday', SORT_ASC, 'date_create']
));

See it in action.

0
agregado
Fácilmente la respuesta menos apreciada en este sitio.
agregado el autor jmeas, fuente
Esta es probablemente la mejor respuesta que he visto en stackoverflow. Gracias por la hermosa pieza de código y la maravillosa documentación!
agregado el autor maddob, fuente
@Jon Gracias por una respuesta increíble. Sin embargo, me resulta difícil de usar. ¿Podría por favor asesorarme sobre PHP "> stackoverflow.com/questions/36784955/…
agregado el autor Ironic, fuente
@Jon gracias por tu comentario. Sin embargo, todavía estoy leyendo tu respuesta e intentando entenderla. Si es necesario, usaré lo mismo en mi proyecto. Solo tenía una duda. No estoy seguro de que funcione con mi data/array o no. En su ejemplo, los datos están en diferentes formatos.
agregado el autor Ironic, fuente
@Jon Cuando verifico su última acción (con la devolución de llamada date_create ), los cumpleaños están en el orden incorrecto. Podrias confirmar ? Obtengo 01/12/1979 , 03/11/1987 , 12/03/1980 y 24/06/1967 . Si utilizo strtotime en su lugar, obtengo un buen resultado. Supongo que es DateTime que está roto.
agregado el autor David Bélanger, fuente
Hola, @Jon, no sé cómo puedo contactarte, pero a través de un comentario. Veo que creó la etiqueta "matriz multidimensional", pero también necesitamos "datos multidimensionales" para términos similares en la visualización de datos y disciplinas relacionadas. No puedo crear etiquetas, ¿puedo pedirte que crees una, si crees que tiene sentido?
agregado el autor VividD, fuente
Llego un poco tarde a la fiesta aquí, pero solo quiero agradecerte por esto. Muy útil.
agregado el autor RobertAKARobin, fuente
@ Dashrath: No estoy seguro de qué es lo que tienes estructurado. Ingrese $ array ['index'] si desea deshacerse del nivel superior, o use una función de proyección ($ sub) {return $ sub ['index'] ; } si desea deshacerse de una dimensión de nivel medio denominada índice .
agregado el autor Jon, fuente
@CalculatingMachine Leí la pregunta, pero no muestras lo que intentaste hacer. Parece que solo usort ($ data ['content'], get_comparer ('price')) sería suficiente, pero no estoy seguro.
agregado el autor Jon, fuente
@VividD: Yo no creé la etiqueta yo mismo, y la creación de etiquetas no puede suceder sola. Las nuevas etiquetas se crean implícitamente cuando alguien con los privilegios necesarios las usa para etiquetar una pregunta; dado que nunca he visto y probablemente nunca veré una pregunta que siento que debería etiquetarse así, realmente no puedo ayudar.
agregado el autor Jon, fuente
@YahyaE: ordenando matrices de objetos? Reemplace $ first [$ column] con $ first -> $ column , y lo mismo para $ second . Cuatro reemplazos en total.
agregado el autor Jon, fuente
@Ecropolis: PHP 5.3 no admite la breve sintaxis de matriz [/]>, en su lugar tendrá que usar array (...) . No hice eso en los ejemplos para mayor brevedad, pero make_comparer es compatible con 5.3.
agregado el autor Jon, fuente
@Ecropolis: tiene comillas adicionales para la función ($ t) ... que deben eliminarse. ¡Aclamaciones!
agregado el autor Jon, fuente
@Ecropolis: OK, entonces puedes hacer una matriz donde las claves sean tipos de archivos y los valores sean "pesos" (peso más pequeño = elemento que esté en la parte superior) con $ pesos = matriz_flecha ($ sort_array) . $ weights ahora es ['mp4' => 0, 'mpeg' => 1, ...] . A continuación, puede utilizar esto en el comparador clasificando el tipo de archivo y usándolo como una proyección: función ($ t) use ($ pesos) {return $ weights [$ t]; }) .
agregado el autor Jon, fuente
@Andrew: Ejemplo . Pero recuerde que esto es ineficiente, así que no lo haga si su conjunto de datos no es pequeño.
agregado el autor Jon, fuente
@Ecropolis: publique un buen ejemplo de su (s) entrada (s) y resultados deseados en algún lugar (por ejemplo, ideone.com o pastebin.com) para que podamos eliminar los malentendidos.
agregado el autor Jon, fuente
@ DavidBélanger: ¿Qué URL exactamente? Todos los ejemplos funcionan correctamente tanto en ideone.com como en mi máquina local.
agregado el autor Jon, fuente
@Jon gracias, acabo de probarlo y es 0.006 segundos más lento que sin él, clasificando 200 filas. Creo que aceptaré este tipo de ineficiencia :)
agregado el autor Andrew, fuente
¿Podría dar un ejemplo de cómo implementar el tipo insensible a mayúsculas y minúsculas? Traté de convertir la proyección a minúsculas, pero no va a funcionar ...
agregado el autor Andrew, fuente
@Jon - Gracias aquí son ejemplos de matrices con comentarios. ideone.com/utbrNt
agregado el autor Ecropolis, fuente
Tengo otra matriz de valores en la que me gustaría ordenar una columna y el valor en la columna puede tener múltiples coincidencias. La matriz de clasificación es de tipos de archivos, por lo que siempre quiero que los tipos de archivos de una lista de archivos se muestren en un orden específico. ¿Es posible en esta construcción?
agregado el autor Ecropolis, fuente
¡Excelente! Gracias @Jon. Actualicé ideone.com/utbrNt para ver el ejemplo completo de cómo usar esta función para ordenar una matriz usando otra para valores de clasificación de referencia.
agregado el autor Ecropolis, fuente
@Jon - Gracias por toda tu ayuda. Intenté implementar esto en mi servidor y entiendo esto: Error de PHP Parse: error de sintaxis, inesperado '[', expecting ')' - Probé una variación diferente, estoy un poco perdido sobre cómo funciona esto realmente. Estoy en v5.3.28 - ideone.com ejecuta PHP 5.4 - ¿ese es el problema?
agregado el autor Ecropolis, fuente
el mío es una matriz 4D? ¿Cómo puedo pasar el índice si eso es posible?
agregado el autor Dashrath, fuente
@Jon Gran respuesta y acepto que esto es como un sitio web en lugar de una respuesta. Gracias. Solo tiene una pregunta. ¿Cómo puedo hacer que funcione para ejecutar objetos?
agregado el autor YahyaE, fuente

The "Usort" function is your answer.
http://php.net/usort

0
agregado
Le he votado negativamente debido a que no proporcionó una solución con explicación y ejemplos a la pregunta original. Actualice su respuesta y revocaré mi voto.
agregado el autor crafter, fuente

You can use array_multisort()

Pruebe algo como esto:

foreach ($mdarray as $key => $row) {
   //replace 0 with the field's index/key
    $dates[$key]  = $row[0];
}

array_multisort($dates, SORT_DESC, $mdarray);

For PHP >= 5.5.0 just extract the column to sort by. No need for the loop:

array_multisort(array_column($mdarray, 0), SORT_DESC, $mdarray);
0
agregado
Entonces, en este ejemplo, $ mdarray podría ser una matriz bidimensional, como una matriz de registros de bases de datos. En este ejemplo, 0 es el índice de la columna 'fecha' en cada registro (o fila). Así que construyes el array $ dates (básicamente el mismo array, pero solo con esa columna), y le dices a la función array_multisort que ordene $ mdarray en base a los valores de esa columna en particular.
agregado el autor Dan Burton, fuente
Para mayor claridad, puede agregar al comienzo de este ejemplo $ dates = array ();
agregado el autor Dan Burton, fuente
Creo que esto me muestra por qué no puedo hacer que array_multisort funcione antes.
agregado el autor Melikoth, fuente
¿Debería array_multisort funcionar con matrices asociativas (cambiando $ row [0] a $ row ['whatever'] ? No vaya aquí. Después de que cambié mi matriz a numérica, la función funcionó como se esperaba.
agregado el autor a coder, fuente
Hay un ejemplo más extenso de esta respuesta ahora en la página PHP array_multisort: php.net/manual/en/function.array-multisort.php#example-4928 .
agregado el autor icc97, fuente
Advertencia: array_multisort (): los tamaños de matriz son inconsistentes
agregado el autor RaviPatidar, fuente
array_multisort() es la forma en que siempre lo he hecho, aunque puede ser un poco complicado entender cómo funciona al principio.
agregado el autor Garrett Albright, fuente
Si array_multi_sort() es la respuesta, la pregunta no fue entendida. Aunque técnicamente funcionará, generalmente hay una mejor solución con una función de comparación generada por el usuario y el uso de una función de usort() . Es más fácil de mantener. Con multisort, generalmente se crea un código que prepara los datos para la clasificación. Si la estructura de datos cambia, ese código podría descartarse. Con usort (), cambia la función de comparación, de la misma manera que cambió la estructura de datos.
agregado el autor Sven, fuente
Esta respuesta realmente me ayudó en mi proyecto actual, ¡gracias!
agregado el autor Stephen, fuente
¿No es necesaria la inclusión de la $ key cuando se usa array_multisort() ? Parece más simple y más intencional escribir foreach ($ mdarray as $ row) {$ sortByDate [] = $ row ['date']; } luego array_multisort ($ sortByDate, SORT_DESC, $ mdarray); (tu kilometraje semántico puede variar).
agregado el autor Mark Fox, fuente
Por alguna razón desconocida, usort no me ha funcionado. Esto hace.
agregado el autor cameronjonesweb, fuente
Fantástica respuesta. Funciona bastante bien para mí,
agregado el autor Raul A., fuente

Prefiero usar array_multisort. Ver la documentación aquí .

0
agregado

Aquí hay una clase php4/php5 que ordenará uno o más campos:

// a sorter class
//  php4 and php5 compatible
class Sorter {

  var $sort_fields;
  var $backwards = false;
  var $numeric = false;

  function sort() {
    $args = func_get_args();
    $array = $args[0];
    if (!$array) return array();
    $this->sort_fields = array_slice($args, 1);
    if (!$this->sort_fields) return $array();

    if ($this->numeric) {
      usort($array, array($this, 'numericCompare'));
    } else {
      usort($array, array($this, 'stringCompare'));
    }
    return $array;
  }

  function numericCompare($a, $b) {
    foreach($this->sort_fields as $sort_field) {
      if ($a[$sort_field] == $b[$sort_field]) {
        continue;
      }
      return ($a[$sort_field] < $b[$sort_field]) ? ($this->backwards ? 1 : -1) : ($this->backwards ? -1 : 1);
    }
    return 0;
  }

  function stringCompare($a, $b) {
    foreach($this->sort_fields as $sort_field) {
      $cmp_result = strcasecmp($a[$sort_field], $b[$sort_field]);
      if ($cmp_result == 0) continue;

      return ($this->backwards ? -$cmp_result : $cmp_result);
    }
    return 0;
  }
}

/////////////////////
// usage examples

// some starting data
$start_data = array(
  array('first_name' => 'John', 'last_name' => 'Smith', 'age' => 10),
  array('first_name' => 'Joe', 'last_name' => 'Smith', 'age' => 11),
  array('first_name' => 'Jake', 'last_name' => 'Xample', 'age' => 9),
);

// sort by last_name, then first_name
$sorter = new Sorter();
print_r($sorter->sort($start_data, 'last_name', 'first_name'));

// sort by first_name, then last_name
$sorter = new Sorter();
print_r($sorter->sort($start_data, 'first_name', 'last_name'));

// sort by last_name, then first_name (backwards)
$sorter = new Sorter();
$sorter->backwards = true;
print_r($sorter->sort($start_data, 'last_name', 'first_name'));

// sort numerically by age
$sorter = new Sorter();
$sorter->numeric = true;
print_r($sorter->sort($start_data, 'age'));
0
agregado
¿Esto solo funciona con matrices asociativas?
agregado el autor Melikoth, fuente
sí - matrices asociativas solamente. Ahora que lo veo, no es la solución correcta para este problema.
agregado el autor Devon, fuente

Sé que han pasado 2 años desde que se hizo y se respondió esta pregunta, pero aquí hay otra función que ordena una matriz bidimensional. Acepta una cantidad variable de argumentos, lo que le permite pasar más de una clave (es decir, el nombre de la columna) para ordenar por. PHP 5.3 requerido.

function sort_multi_array ($array, $key)
{
  $keys = array();
  for ($i=1;$i< $b[$keys[$i]]) ? -1 : 1;
      }
    }
    return 0;
  };

  usort($array, $func);

  return $array;
}

Try it here: http://www.exorithm.com/algorithm/view/sort_multi_array

0
agregado
Sí, así lo creo
agregado el autor Mike C, fuente

Intenté varias respuestas populares array_multisort() y usort() y ninguna de ellas funcionó para mí. Los datos solo se mezclan y el código es ilegible. Aquí hay una solución rápida y sucia. ADVERTENCIA: ¡Úselo solo si está seguro de que un delimitador deshonesto no volverá a perseguirlo más tarde!

Digamos que cada fila en su matriz múltiple se ve así: nombre, cosas1, cosas2:

// Sort by name, pull the other stuff along for the ride
foreach ($names_stuff as $name_stuff) {
   //To sort by stuff1, that would be first in the contatenation
    $sorted_names[] = $name_stuff[0] .','. name_stuff[1] .','. $name_stuff[2];
}
sort($sorted_names, SORT_STRING);

¿Necesitas tus cosas en orden alfabético?

foreach ($sorted_names as $sorted_name) {
    $name_stuff = explode(',',$sorted_name);
   //use your $name_stuff[0] 
   //use your $name_stuff[1] 
   //... 
}

Sí, está sucio. Pero súper fácil, no hará explotar tu cabeza.

0
agregado
function cmp($a, $b)
{
$p1 = $a['price'];
$p2 = $b['price'];
return (float)$p1 > (float)$p2;
}
uasort($my_array, "cmp");

http://qaify.com/sort-an-array-of-associative-arrays-by-value-of-given-key-in-php/

0
agregado