¿Ordenar una matriz por claves basadas en otra matriz?

153

¿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 teclas de matriz / valores).

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

alex
fuente

Respuestas:

347

Solo usa array_mergeo array_replace. Array_mergefunciona comenzando con la matriz que le da (en el orden correcto) y sobrescribiendo / agregando las claves con los datos de su 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', 'address' => '123 fake st', 'dob' => '12/08/1986', '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.

Darkwaltz4
fuente
31
Funciona bien si tiene teclas de cadena pero no para la numérica. Documentos de PHP: "Si las matrices de entrada tienen las mismas claves de cadena, entonces el valor posterior para esa clave sobrescribirá la anterior. Sin embargo, si las matrices contienen claves numéricas, el valor posterior no sobrescribirá el valor original, pero será anexado ".
bolbol el
77
Bien, pero ¿qué pasa si las claves no existen en los valores? Necesito esto, pero solo si existe alguna de las claves ... Probablemente necesite un foreach sobre eso entonces ...
Solomon Closson
55
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
3
Me encontré con su solución hace un par de años mientras buscaba algo diferente, y pensé para mí mismo, esto es extremadamente eficiente en comparación con los bucles. ¡Ahora necesito su solución y me llevó una hora de búsqueda encontrarla nuevamente! ¡Gracias!
Michael
44
Además, si la matriz 'order' (es decir, la matriz ('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 teclas extraviadas que se agregaron en array_merge.
bbe
105

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;
}
Eran Galperin
fuente
12
¿Entonces puedes unir 2 matrices con un signo +? ¡Nunca lo supe, lo he estado usando array_merge()!
alex
3
¿Es esto mejor que usar usort()o uasort()?
grantwparks
55
Debe insertar una breakdeclaración una vez que se haya encontrado el valor.
Adel
44
@alex Tenga mucho cuidado cuando reemplace array_merge()con el +operador de matriz . Se fusiona por tecla (también para teclas numéricas) y de izquierda a derecha , mientras que se array_mergefusiona de derecha a izquierda y nunca sobrescribe las teclas numéricas. Por ejemplo, [0,1,2]+[0,5,6,7] = [0,1,2,7]while array_merge([0,1,2],[0,5,6,7]) = [0,1,2,0,5,6,7]y ['a' => 5] + ['a' => 7] = ['a' => 5]but array_merge(['a' => 5], ['a' => 7]) = ['a' => 7].
gripe
¿Es seguro usar el +letrero?
crmpicco
47

¿Qué tal esta solución?

$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));
});
Peter de Groot
fuente
1
Este necesita más votos a favor, otro no funcionó / ​​no funcionó bien, mientras que esto funciona bien en mi caso.
Ng Sek Long
Esto no es muy eficiente. Para cada comparación, se realizan dos búsquedas lineales en la matriz. Si suponemos que la complejidad temporal de uksort () es O(n * log n), entonces este algoritmo se ejecuta O(n^2 * log(n)).
TheOperator
36

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.

ekuser
fuente
2
+ Mientras ambos trabajan, descubrí array_replace()que la intención de transmitir es mejor que array_merge().
Jason McCreary
1
array_replacetambién deja el tipo variable intacto. Si uno de los valores de su matriz hubiera sido (string) '1'y hubiera utilizado el +operador, el valor se habría convertido en(int) 1
halfpastfour.am el
1
Esto también funciona en teclas numéricas ( array_merge()¿solo las agregaría?). La lógica está muy bien explicada aquí . Primero , array_flip()cambia los valores de la matriz $ order a claves. Segundo , array_replace()reemplaza los valores de la primera matriz con valores que tienen las mismas claves en la segunda matriz. Si necesita ordenar una matriz de acuerdo con las claves de otra, ni siquiera tiene que usarla array_flip.
aexl
23
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;
}
OIS
fuente
14

Tome una matriz como su orden:

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

Puede ordenar otra matriz en función de los valores utilizando 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, use 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) de la segunda matriz.

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

hakre
fuente
La intersección eliminaría aquellas entradas que no conoce de antemano.
DanMan
1
Esto es incorrecto para ordenar por claves. array_intersect_key solo devolverá los valores de array1, no de array2
espeluznante
De acuerdo con pavsid: el ejemplo de array_intersect_key es incorrecto: devuelve los valores de la primera matriz, no de la segunda.
Jonathan Aquino
10

Utilicé la solución Darkwaltz4 pero usé en array_fill_keyslugar de array_flip, para rellenar NULLsi una clave no está configurada $array.

$properOrderedArray = array_replace(array_fill_keys($keys, null), $array);
Baptiste Bernard
fuente
5

Sin magia ...

$array=array(28=>c,4=>b,5=>a);
$seq=array(5,4,28);    
SortByKeyList($array,$seq) result: array(5=>a,4=>b,28=>c);

function sortByKeyList($array,$seq){
    $ret=array();
    if(empty($array) || empty($seq)) return false;
    foreach($seq as $key){$ret[$key]=$dataset[$key];}
    return $ret;
}
Jenovai Matyas
fuente
1
Esto funciona bien gracias, solo actualice $datasetpara que coincida con el nombre del parámetro
kursus
3

SI tiene una matriz en su matriz, tendrá que adaptar un poco la función de Eran ...

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

Esta función devuelve una matriz sub y ordenada basada en el segundo parámetro $ keys

function array_sub_sort(array $values, array $keys){
    $keys = array_flip($keys);
    return array_merge(array_intersect_key($keys, $values), array_intersect_key($values, $keys));
}

Ejemplo:

$array_complete = [
    'a' => 1,
    'c' => 3,
    'd' => 4,
    'e' => 5,
    'b' => 2
];

$array_sub_sorted = array_sub_sort($array_complete, ['a', 'b', 'c']);//return ['a' => 1, 'b' => 2, 'c' => 3];
Doglas
fuente
1

PHP tiene funciones para ayudarlo con esto:

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

// sort array
usort($arrayToBeSorted, function($a, $b) use ($order){
    // sort using the numeric index of the second array
    $valA = array_search($a, $order);
    $valB = array_search($b, $order);

    // move items that don't match to end
    if ($valA === false)
        return -1;
    if ($valB === false)
        return 0;

    if ($valA > $valB)
        return 1;
    if ($valA < $valB)
        return -1;
    return 0;
});

Usort hace todo el trabajo por usted y array_search proporciona las claves. array_search () devuelve falso cuando no puede encontrar una coincidencia, por lo que los elementos que no están en la matriz de clasificación se mueven naturalmente al final de la matriz.

Nota: uasort () ordenará la matriz sin afectar las relaciones clave => valor.

danielcraigie
fuente
1
  • ordenar según lo solicitado
  • guardar para int-keys (debido a array_replace)
  • no devolver claves no existen en inputArray
  • (opcionalmente) las claves de filtro no existen en la lista de claves dada

Código:

 /**
 * sort keys like in key list
 * filter: remove keys are not listed in keyList
 * ['c'=>'red', 'd'=>'2016-12-29'] = sortAndFilterKeys(['d'=>'2016-12-29', 'c'=>'red', 'a'=>3 ]], ['c', 'd', 'z']){
 *
 * @param array $inputArray
 * @param string[]|int[] $keyList
 * @param bool $removeUnknownKeys
 * @return array
 */
static public function sortAndFilterKeys($inputArray, $keyList, $removeUnknownKeys=true){
    $keysAsKeys = array_flip($keyList);
    $result = array_replace($keysAsKeys, $inputArray); // result = sorted keys + values from input + 
    $result = array_intersect_key($result, $inputArray); // remove keys are not existing in inputArray 
    if( $removeUnknownKeys ){
        $result = array_intersect_key($result, $keysAsKeys); // remove keys are not existing in keyList 
    }
    return $result;
}
Grano
fuente
1

Primera sugerencia

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

Segunda sugerencia

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

Quería señalar que ambas sugerencias son increíbles. Sin embargo, son manzanas y naranjas. ¿La diferencia? Uno es amigable no asociativo y el otro es amigable asociativo. Si está utilizando 2 matrices totalmente asociativas, la combinación / volteo de matrices realmente combinará y sobrescribirá la otra matriz asociativa. En mi caso, ese no es el resultado que estaba buscando. Utilicé un archivo settings.ini para crear mi matriz de orden de clasificación. La matriz de datos que estaba ordenando no necesitaba ser reescrita por mi contraparte de clasificación asociativa. Por lo tanto, la combinación de matrices destruiría mi matriz de datos. Ambos son excelentes métodos, ambos necesitan ser archivados en cualquier caja de herramientas de desarrolladores. Según sus necesidades, puede encontrar que realmente necesita ambos conceptos en sus archivos.

usuario1653711
fuente
1

Adopté la respuesta de @ Darkwaltz4 por su brevedad y me gustaría compartir cómo adapté la solución a situaciones en las que la matriz puede contener diferentes claves para cada iteración de esta manera:

Array[0] ...
['dob'] = '12/08/1986';
['some_key'] = 'some value';

Array[1] ...
['dob'] = '12/08/1986';

Array[2] ...
['dob'] = '12/08/1986';
['some_key'] = 'some other value';

y mantuvo una "clave maestra" de esta manera:

$master_key = array( 'dob' => ' ' ,  'some_key' => ' ' );

array_merge habría ejecutado la fusión en la iteración Array [1] basada en $ master_key y producido ['some_key'] = '', un valor vacío, para esa iteración. Por lo tanto, array_intersect_key se usó para modificar $ master_key en cada iteración de la siguiente manera:

foreach ($customer as $customer) {
  $modified_key = array_intersect_key($master_key, $unordered_array);
  $properOrderedArray = array_merge($modified_key, $customer);
}
Pageii Studio
fuente
0

Un poco tarde, pero no pude encontrar la forma en que lo implementé, esta versión necesita cierre, php> = 5.3, pero podría modificarse para no:

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

$order = array('name', 'dob', 'address');

$keys= array_flip($order);
uksort($customer, function($a, $b)use($keys){
    return $keys[$a] - $keys[$b];
});
print_r($customer);

Por supuesto, 'dontSortMe' necesita ser resuelto, y puede aparecer primero en el ejemplo

DJules
fuente