Ordenar matriz de matrices multidimensionales en más de una & # 8220; Columna & # 8221; (Clave) con opciones de ordenación especificadas

StackOverflow https://stackoverflow.com/questions/809771

Pregunta

Estoy buscando poder ordenar una matriz de matrices multidimensionales en más de una columna. Para complicarlo aún más, me gustaría poder establecer opciones de clasificación específicas por clave / columna. Tengo lo que es similar al resultado de una consulta de base de datos, pero en realidad no proviene de una, por lo tanto, la necesidad de ordenarlo en PHP en lugar de SQL.

Array
(
    [0] => Array
        (
            [first_name] => Homer
            [last_name] => Simpson
            [city] => Springfield
            [state] => Unknown
            [zip] => 66735
        )

    [1] => Array
        (
            [first_name] => Patty
            [last_name] => Bouvier
            [city] => Scottsdale
            [state] => Arizona
            [zip] => 85250
        )

    [2] => Array
        (
            [first_name] => Moe
            [last_name] => Szyslak
            [city] => Scottsdale
            [state] => Arizona
            [zip] => 85255
        )

    [3] => Array
        (
            [first_name] => Nick
            [last_name] => Riviera
            [city] => Scottsdale
            [state] => Arizona
            [zip] => 85255
        )

)

Me gustaría poder ordenarlo de forma similar a lo que se podría hacer con una consulta DB. Ah, y a veces una columna / clave debe especificarse por número.

Lo que tenía en mente era algo similar a esto:

$sortOptions = array( array( 'city', SORT_ASC, SORT_STRING ),
                      array( 'zip', SORT_DESC, SORT_NUMERIC),
                      array( 2, SORT_ASC, SORT_STRING) // 2='last_name'
                    );
$sorter = new MultiSort($data, $sortOptions );
$sortedData = $sorter->getSortedArray() ;
print_r( $jmsSorted);

Lo que me gustaría terminar es esto:

Array
(
    [0] => Array
        (
            [first_name] => Nick
            [last_name] => Riviera
            [city] => Scottsdale
            [state] => Arizona
            [zip] => 85255
        )

    [1] => Array
        (
            [first_name] => Moe
            [last_name] => Szyslak
            [city] => Scottsdale
            [state] => Arizona
            [zip] => 85255
        )

    [2] => Array
        (
            [first_name] => Patty
            [last_name] => Bouvier
            [city] => Scottsdale
            [state] => Arizona
            [zip] => 85250
        )

    [3] => Array
        (
            [first_name] => Homer
            [last_name] => Simpson
            [city] => Springfield
            [state] => Unknown
            [zip] => 66735
        )

)

ACTUALIZACIÓN: creo que, idealmente, una solución daría como resultado la creación dinámica

array_multisort( $city, SORT_ASC, SORT_STRING, $zip, SORT_DESC, SORT_NUMERIC, $last_name, SORT_ASC, SORT_STRING, $inputArray);

El problema es que no quiero tener que "codificar". esos nombres clave allí. Traté de crear una solución basada en Ejemplo # 3 Ordenar los resultados de la base de datos del array_multisort () documentación que terminó usando array_multisort () pero parece que no puedo encontrar una manera de usar mi lista de argumentos construida dinámicamente para array_multisort () .

Mi intento fue "encadenar" esos argumentos juntos en una matriz y luego

call_user_func_array( 'array_multisort', $functionArgs);

Eso resulta en un

Warning: Parameter 2 to array_multisort() expected to be a reference, value given in...
¿Fue útil?

Solución 4

Esto es lo que finalmente decidí por poder ordenar matrices multidimensionales. Ambas respuestas anteriores son buenas, pero también estaba buscando algo flexible.

Definitivamente no creo que haya una respuesta "correcta", pero esto es lo que funciona para mis necesidades y es flexible.

Como puede ver en mi @link en el comentario de _usortByMultipleKeys () , fue adaptado de un comentario en el manual de PHP que actualmente parece no existir , pero creo que http://www.php.net/manual/en /function.usort.php#104398 es una nueva versión del comentario original. No he explorado con esa nueva sugerencia.

/**
 * Sort the resultSet.
 *
 * Usage: $sortOptions = array(
 *          'section', // Defaults to SORT_ASC
 *          'row' => SORT_DESC,
 *          'retail_price' => SORT_ASC);
 *        $results->sortResults($sortOptions);
 *
 * @param array $sortOptions    An array of sorting instructions
 */
public function sortResults(array $sortOptions)
{
    usort($this->_results, $this->_usortByMultipleKeys($sortOptions));
}


/**
 * Used by sortResults()
 *
 * @link http://www.php.net/manual/en/function.usort.php#103722
 */
protected function _usortByMultipleKeys($key, $direction=SORT_ASC)
{
    $sortFlags = array(SORT_ASC, SORT_DESC);
    if (!in_array($direction, $sortFlags)) {
        throw new InvalidArgumentException('Sort flag only accepts SORT_ASC or SORT_DESC');
    }
    return function($a, $b) use ($key, $direction, $sortFlags) {
        if (!is_array($key)) { //just one key and sort direction
            if (!isset($a->$key) || !isset($b->$key)) {
                throw new Exception('Attempting to sort on non-existent keys');
            }
            if ($a->$key == $b->$key) {
                return 0;
            }
            return ($direction==SORT_ASC xor $a->$key < $b->$key) ? 1 : -1;
        } else { //using multiple keys for sort and sub-sort
            foreach ($key as $subKey => $subAsc) {
                //array can come as 'sort_key'=>SORT_ASC|SORT_DESC or just 'sort_key', so need to detect which
                if (!in_array($subAsc, $sortFlags)) {
                    $subKey = $subAsc;
                    $subAsc = $direction;
                }
                //just like above, except 'continue' in place of return 0
                if (!isset($a->$subKey) || !isset($b->$subKey)) {
                    throw new Exception('Attempting to sort on non-existent keys');
                }
                if ($a->$subKey == $b->$subKey) {
                    continue;
                }
                return ($subAsc==SORT_ASC xor $a->$subKey < $b->$subKey) ? 1 : -1;
            }
            return 0;
        }
    };
}

Otros consejos

En PHP 5.3, cada parámetro de la matriz debe ser una referencia al llamar a array_multisort () con call_user_func_array () .

Esta función ordena una matriz multidimensional y muestra una manera de construir una matriz de parámetros referenciados que funcione correctamente.

function msort()
{
  $params = func_get_args();
  $array = array_pop($params);

  if (!is_array($array))
    return false;

  $multisort_params = array();
  foreach ($params as $i => $param) 
  {
    if (is_string($param)) 
    {
      ${"param_$i"} = array();
      foreach ($array as $index => $row) 
      {
        ${"param_$i"}[$index] = $row[$param];
      }
    }
    else 
      ${"param_$i"} = $params[$i];

    $multisort_params[] = &${"param_$i"};
  }
  $multisort_params[] = &$array; 

  call_user_func_array("array_multisort", $multisort_params);

  return $array;
}

Ejemplo:

$ data es la matriz dada de la pregunta

$sorted_data = msort('city', SORT_ASC, SORT_STRING, 'zip', SORT_DESC, SORT_NUMERIC, $data)

Esto debería funcionar para la situación que usted describe.

usort($arrayToSort, "sortCustom");

function sortCustom($a, $b)
{
    $cityComp = strcmp($a['city'],$b['city']);
    if($cityComp == 0)
    {
        //Cities are equal.  Compare zips.
        $zipComp = strcmp($a['zip'],$b['zip']);
        if($zipComp == 0)
        {
            //Zips are equal.  Compare last names.
            return strcmp($a['last_name'],$b['last_name']);
        }
        else
        {
            //Zips are not equal.  Return the difference.
            return $zipComp;
        }
    }
    else
    {
        //Cities are not equal.  Return the difference.
        return $cityComp;
    }
}

Podría condensarlo en una línea así:

function sortCustom($a, $b)
{
    return ($cityComp = strcmp($a['city'],$b['city']) ? $cityComp : ($zipComp = strcmp($a['zip'],$b['zip']) ? $zipComp : strcmp($a['last_name'],$b['last_name'])));
}

En cuanto a tener una función de clasificación personalizable, reinventa la rueda. Eche un vistazo a array_multisort () función.

Puede intentar usar usort . Todo lo que tiene que hacer es crear funciones que le digan al clasificador cómo ordenarlo. Los documentos tienen más información sobre cómo hacerlo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top