Ordenar por clave personalizada una asociativa plana basada en otra matriz

6 minutos de lectura

avatar de usuario de alex
Alex

¿Es posible en PHP hacer algo como esto? ¿Cómo harías para escribir una función? Aquí hay un ejemplo. El orden es lo más importante.

$customer['address'] = '123 fake st';
$customer['name'] = 'Tim';
$customer['dob'] = '12/08/1986';
$customer['dontSortMe'] = 'this value doesnt need to be sorted';

Y me gustaría hacer algo como

$properOrderedArray = sortArrayByArray($customer, array('name', 'dob', 'address'));

Porque al final uso un foreach() y no están en el orden correcto (porque agrego los valores a una cadena que debe estar en el orden correcto y no sé de antemano todas las claves de matriz/ valores).

Revisé las funciones de matriz interna de PHP, pero parece que solo puede ordenar alfabéticamente o numéricamente.

Avatar de usuario de Darkwaltz4
vals oscuro4

Solo usa array_merge o array_replace. array_merge funciona comenzando con la matriz que le das (en el orden correcto) y sobrescribiendo/agregando las claves con datos de tu matriz real:

$customer['address']    = '123 fake st';
$customer['name']       = 'Tim';
$customer['dob']        = '12/08/1986';
$customer['dontSortMe'] = 'this value doesnt need to be sorted';

$properOrderedArray = array_merge(array_flip(array('name', 'dob', 'address')), $customer);
// or
$properOrderedArray = array_replace(array_flip(array('name', 'dob', 'address')), $customer);

// $properOrderedArray: array(
//   'name'       => 'Tim',
//   'dob'        => '12/08/1986',
//   'address'    => '123 fake st',
//   'dontSortMe' => 'this value doesnt need to be sorted')

PD: estoy respondiendo a esta pregunta “obsoleta”, porque creo que todos los bucles dados como respuestas anteriores son excesivos.

  • Funciona bien si tiene teclas de cadena pero no para la numérica. PHP Docs: “Si las matrices de entrada tienen las mismas claves de cadena, el valor posterior de esa clave sobrescribirá la anterior. Sin embargo, si las matrices contienen claves numéricas, el valor posterior no sobrescribirá el valor original, pero será adjunto”.

    – bolbol

    7 de noviembre de 2012 a las 6:52


  • Bien, pero ¿y si las claves no existen en los valores? Necesito esto, pero solo si existe alguna de las claves… Probablemente necesite un foreach entonces…

    – Salomón Closson

    8 mayo 2014 a las 22:49

  • para mi caso, es array_replace en lugar de array_merge. array_merge combina ambos valores en lugar de reemplazar la segunda matriz en las claves ordenadas.

    – neofreko

    12 de noviembre de 2014 a las 6:26

  • Me topé con su solución hace un par de años mientras buscaba algo diferente, y pensé, esto es extremadamente eficiente en comparación con los bucles. ¡Ahora necesito su solución y me llevó una hora de búsqueda volver a encontrarla! ¡Gracias!

    – Miguel

    13 de noviembre de 2017 a las 3:29

  • Además, si la matriz ‘order’ (es decir, array(‘name’, ‘dob’, ‘address’)) tiene más claves que la matriz para ordenar, entonces array_intersect adicional de la matriz ordenada resultante con la matriz original se cortaría claves perdidas que se agregaron en array_merge.

    – bb

    8 de enero de 2018 a las 10:24


Avatar de usuario de Eran Galperin
Eran Galperín

Ahí tienes:

function sortArrayByArray(array $array, array $orderArray) {
    $ordered = array();
    foreach ($orderArray as $key) {
        if (array_key_exists($key, $array)) {
            $ordered[$key] = $array[$key];
            unset($array[$key]);
        }
    }
    return $ordered + $array;
}

  • Entonces, ¿puedes unir 2 matrices con un signo +? Nunca supe eso, he estado usando array_merge()!

    – Alex

    24 de septiembre de 2009 a las 23:08

  • ¿Es esto mejor que usar usort() o uasort()?

    – parques de subvenciones

    25 de septiembre de 2009 a las 19:57

  • Debes insertar un break declaración una vez que se ha encontrado el valor.

    – Adel

    26 de septiembre de 2011 a las 14:50


  • @alex Tenga mucho cuidado cuando reemplace array_merge() con la matriz + operador. Fusiona por clave (también para claves numéricas) y desde de izquierda a derechamientras array_merge se fusiona desde De derecha a izquierda y nunca sobrescribe las teclas numéricas. P.ej [0,1,2]+[0,5,6,7] = [0,1,2,7] mientras array_merge([0,1,2],[0,5,6,7]) = [0,1,2,0,5,6,7] y ['a' => 5] + ['a' => 7] = ['a' => 5] pero array_merge(['a' => 5], ['a' => 7]) = ['a' => 7].

    – gripe

    5 de agosto de 2015 a las 8:07

  • El + o operador de unión es perfectamente válido de acuerdo con Documentación PHP en algunos casos de uso, es exactamente lo que desea si tiene que mantener las teclas numéricas.

    – Hexodus

    12 mayo 2017 a las 21:14

Que tal esta solucion

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

$array = array(
    1 => 'one',
    2 => 'two',
    3 => 'three',
    4 => 'four',
    5 => 'five',
    6 => 'six'
);

uksort($array, function($key1, $key2) use ($order) {
    return (array_search($key1, $order) > array_search($key2, $order));
});

  • Esto no es muy eficiente. Para cada comparación, se realizan dos búsquedas lineales en la matriz. Si asumimos que la complejidad temporal de uksort() es O(n * log n)entonces este algoritmo se ejecuta en O(n^2 * log(n)).

    – El operador

    23 de enero de 2020 a las 9:12

  • Estas soluciones funcionan bastante bien para las matrices a las que les falta un valor en el orden de clasificación. Para arreglos pequeños, la complejidad es probablemente insignificante. Las soluciones con múltiples funciones de matriz podrían ser incluso peores.

    – 2.º kauboy

    27 de julio de 2021 a las 15:56

  • justo array_flip el $pedido antes usey va más rápido

    – V.Volkov

    6 de agosto de 2022 a las 6:59

  • @V.Volkov Pero en ese caso obtendrá un resultado incorrecto

    -Peter de Groot

    7 de agosto de 2022 a las 7:13

  • @PeterdeGroot lo siento por no aclarar 🙂 $order = array_flip( $order ); antes de uksort y return $order[ $key1 ] > $order[ $key2 ]; adentro uksort

    – V.Volkov

    7 de agosto de 2022 a las 7:47

Otra forma para PHP >= 5.3.0:

$customer['address'] = '123 fake st';
$customer['name'] = 'Tim';
$customer['dob'] = '12/08/1986';
$customer['dontSortMe'] = 'this value doesnt need to be sorted';

$customerSorted = array_replace(array_flip(array('name', 'dob', 'address')), $customer);

Resultado:

Array (
  [name] => Tim
  [dob] => 12/08/1986
  [address] => 123 fake st
  [dontSortMe] => this value doesnt need to be sorted
)

Funciona bien con cadenas y teclas numéricas.

function sortArrayByArray(array $toSort, array $sortByValuesAsKeys)
{
    $commonKeysInOrder = array_intersect_key(array_flip($sortByValuesAsKeys), $toSort);
    $commonKeysWithValue = array_intersect_key($toSort, $commonKeysInOrder);
    $sorted = array_merge($commonKeysInOrder, $commonKeysWithValue);
    return $sorted;
}

Avatar de usuario de Stephan Vierkant
Stephan Vierkant

Usé la solución de Darkwaltz4 pero usé array_fill_keys en lugar de array_flippara llenar con NULL si una clave no está configurada $array.

$properOrderedArray = array_replace(array_fill_keys($keys, null), $array);

avatar de usuario de hakre
hakré

Tome una matriz como su orden:

$order = array('north', 'east', 'south', 'west');

Puede ordenar otra matriz según los valores usando array_intersectDocumentos:

/* sort by value: */
$array = array('south', 'west', 'north');
$sorted = array_intersect($order, $array);
print_r($sorted);

O en su caso, para ordenar por claves, utilice array_intersect_keyDocumentos:

/* sort by key: */
$array = array_flip($array);
$sorted = array_intersect_key(array_flip($order), $array);
print_r($sorted);

Ambas funciones mantendrán el orden del primer parámetro y solo devolverán los valores (o claves) del segundo arreglo.

Entonces, para estos dos casos estándar, no necesita escribir una función por su cuenta para realizar la clasificación/reorganización.

  • La intersección se desharía de aquellas entradas que no conoce de antemano.

    – DanMan

    6 de marzo de 2013 a las 15:02

  • Esto es incorrecto para ordenar por claves. array_intersect_key solo devolverá los valores de array1, no de array2

    – escalofriante

    21 de enero de 2015 a las 14:30

  • De acuerdo con pavsid: el ejemplo array_intersect_key es incorrecto: devuelve los valores de la primera matriz, no de la segunda.

    –Jonathan Aquino

    15 de abril de 2015 a las 3:18

¿Ha sido útil esta solución?