sort - Ordenar una matriz php de matrices por orden personalizado
ordenar array php por campo (6)
@salathe Para aquellos de ustedes que están teniendo dificultades para entender lo que está haciendo el usuario de salathe:
Cada elemento en $ array es un "campeón" en un torneo que está al comienzo de una nueva matriz (excepto que en lugar de ser el número uno ellos quieren ser el número 0).
$ a es el campeón de casa y $ b el campeón oponente en un partido.
$ pos_a y $ pos_b de la devolución de llamada son los atributos que se usarán en la pelea para el campeón a y b. En este caso, este atributo es el índice de la id de campeones en $ order.
Luego está la pelea en el regreso. Ahora miramos para ver si tener más o menos del atributo es mejor. En una batalla de usort, el campeón de casa quiere un número negativo para que pueda estar más rápido en el conjunto. El campeón visitante quiere un número positivo. Y si hay un 0 es un empate.
Entonces, siguiendo esta analogía cuando el atributo de campeones fuera (índice en $ orden) se resta del atributo de equipos locales, cuanto mayor es el atributo de campeones fuera de casa es menos probable que gane al obtener un número positivo. Sin embargo, si revierte la forma en que se usan los atributos, ahora el atributo del campeón local se resta del campeón de visitante. En este caso, es más probable que un número mayor para el campeón visitante lo haga tener el partido final en un número positivo.
El código se vería así:
nota: el código se ejecuta muchas veces como un torneo real tiene muchas batallas para decidir quién es el primero (es decir, 0 / inicio de la matriz)
//tournament with goal to be first in array
usort($champions, function ($home, $away) use ($order) {
$home_attribute = array_search($a[''id''], $order);
$away_attribute = array_search($b[''id''], $order);
//fight with desired outcome for home being negative and away desiring positive
return $home_attribute - $away_attribute;
});
Tengo una matriz de matrices:
Array (
[0] => Array (
[id] = 7867867,
[title] = ''Some Title''),
[1] => Array (
[id] = 3452342,
[title] = ''Some Title''),
[2] => Array (
[id] = 1231233,
[title] = ''Some Title''),
[3] => Array (
[id] = 5867867,
[title] = ''Some Title'')
)
La necesidad de ir en un orden específico:
- 3452342
- 5867867
- 7867867
- 1231233
¿Cómo voy a hacer eso? He ordenado arreglos antes y he leído muchas otras publicaciones al respecto, pero siempre se basan en las comparaciones (es decir, valueA <valueB).
La ayuda es apreciada.
Ampliando la respuesta de Salathe para este requisito adicional:
Ahora, ¿qué sucede cuando agrego elementos a la matriz y no al género? No me importa el orden en que aparecen, siempre y cuando venga después de los que especifiqué.
Necesita agregar dos condiciones adicionales en la función de clasificación:
- Un artículo "no me importa" debe considerarse mayor que los artículos "no me importa"
- Dos artículos "no me importa" deben considerarse iguales
Entonces, el código revisado sería:
$order = array(
3452342,
5867867,
7867867,
1231233
);
$array = array(
array("id" => 7867867, "title" => "Must Be #3"),
array("id" => 3452342, "title" => "Must Be #1"),
array("id" => 1231233, "title" => "Must Be #4"),
array("id" => 5867867, "title" => "Must Be #2"),
array("id" => 1111111, "title" => "Dont Care #1"),
array("id" => 2222222, "title" => "Dont Care #2"),
array("id" => 3333333, "title" => "Dont Care #3"),
array("id" => 4444444, "title" => "Dont Care #4")
);
function custom_compare($a, $b){
global $order;
$a = array_search($a["id"], $order);
$b = array_search($b["id"], $order);
if($a === false && $b === false) { // both items are dont cares
return 0; // a == b
}
else if ($a === false) { // $a is a dont care item
return 1; // $a > $b
}
else if ($b === false) { // $b is a dont care item
return -1; // $a < $b
}
else {
return $a - $b;
}
}
shuffle($array); // for testing
var_dump($array); // before
usort($array, "custom_compare");
var_dump($array); // after
Salida:
Before | After
-------------------------------+-------------------------------
array(8) { | array(8) {
[0]=> | [0]=>
array(2) { | array(2) {
["id"]=> | ["id"]=>
int(4444444) | int(3452342)
["title"]=> | ["title"]=>
string(12) "Dont Care #4" | string(10) "Must Be #1"
} | }
[1]=> | [1]=>
array(2) { | array(2) {
["id"]=> | ["id"]=>
int(3333333) | int(5867867)
["title"]=> | ["title"]=>
string(12) "Dont Care #3" | string(10) "Must Be #2"
} | }
[2]=> | [2]=>
array(2) { | array(2) {
["id"]=> | ["id"]=>
int(1231233) | int(7867867)
["title"]=> | ["title"]=>
string(10) "Must Be #4" | string(10) "Must Be #3"
} | }
[3]=> | [3]=>
array(2) { | array(2) {
["id"]=> | ["id"]=>
int(1111111) | int(1231233)
["title"]=> | ["title"]=>
string(12) "Dont Care #1" | string(10) "Must Be #4"
} | }
[4]=> | [4]=>
array(2) { | array(2) {
["id"]=> | ["id"]=>
int(5867867) | int(2222222)
["title"]=> | ["title"]=>
string(10) "Must Be #2" | string(12) "Dont Care #2"
} | }
[5]=> | [5]=>
array(2) { | array(2) {
["id"]=> | ["id"]=>
int(2222222) | int(1111111)
["title"]=> | ["title"]=>
string(12) "Dont Care #2" | string(12) "Dont Care #1"
} | }
[6]=> | [6]=>
array(2) { | array(2) {
["id"]=> | ["id"]=>
int(3452342) | int(3333333)
["title"]=> | ["title"]=>
string(10) "Must Be #1" | string(12) "Dont Care #3"
} | }
[7]=> | [7]=>
array(2) { | array(2) {
["id"]=> | ["id"]=>
int(7867867) | int(4444444)
["title"]=> | ["title"]=>
string(10) "Must Be #3" | string(12) "Dont Care #4"
} | }
} | }
Así es como clasifico mi matriz multidimensional en orden ASC en función del valor de id
:
$arrayFilter = array(
array(''product_tag'' => ''xenia'', ''id'' => 4),
array(''product_tag'' => ''worn'', ''id'' => 5),
array(''product_tag'' => ''woven'', ''id'' => 3),
array(''product_tag'' => ''nude'', ''id'' => 1)
);
for ($i = 0; $i < sizeof($arrayFilter); $i++) {
for ($j=$i+1; $j < sizeof($arrayFilter); $j++) {
if ($arrayFilter[$i][''id''] > $arrayFilter[$j][''id'']) {
$c = $arrayFilter[$i];
$arrayFilter[$i] = $arrayFilter[$j];
$arrayFilter[$j] = $c;
}
}
}
print_r($arrayFilter);
SALIDA:
Array
(
[0] => Array
(
[product_tag] => nude
[id] => 1
)
[1] => Array
(
[product_tag] => woven
[id] => 3
)
[2] => Array
(
[product_tag] => xenia
[id] => 4
)
[3] => Array
(
[product_tag] => worn
[id] => 5
)
)
Las otras respuestas que usan métodos con llamadas array_search()
de array_search()
no son tan eficientes como pueden ser. Al reestructurar / invertir la matriz de búsqueda "ordenar", puede omitir por completo todas las llamadas a array_search()
hace que su tarea sea mucho más eficiente y breve. Usaré el "operador de nave espacial" más moderno ( <=>
), pero las técnicas anteriores funcionarán igual para la línea de comparación.
Método n. ° 1 : usort
cuando todos los valores de id
existen en $order
( Demo )
$order=array_flip([3452342,5867867,7867867,1231233]); // restructure with values as keys, and keys as order (ASC)
// generating $order=[3452342=>0,5867867=>1,7867867=>2,1231233=>3];
$array=[
[''id''=>7867867,''title''=>''Some Title''],
[''id''=>3452342,''title''=>''Some Title''],
[''id''=>1231233,''title''=>''Some Title''],
[''id''=>5867867,''title''=>''Some Title'']
];
usort($array,function($a,$b)use($order){
return $order[$a[''id'']]<=>$order[$b[''id'']];
// when comparing ids 3452342 & 1231233, the actual comparison is 0 vs 3
});
// uasort() if you want to preserve keys
var_export($array);
Método n. ° 2 : usort
cuando algunos valores de id
no existen en $order
( Demo )
* note, isset()
es una llamada menos costosa que array_search()
$order=array_flip([3452342,5867867,7867867,1231233]); // restructure with values as keys, and keys as order (ASC)
// generating $order=[3452342=>0,5867867=>1,7867867=>2,1231233=>3];
$outlier=1+max($order);
// generating $outlier=4
$array=[
[''id''=>7867867,''title''=>''Some Title''],
[''id''=>3452342,''title''=>''Some Title''],
[''id''=>''foo'',''title''=>''Some Title''],
[''id''=>1231233,''title''=>''Some Title''],
[''id''=>''bar'',''title''=>''Some Title''],
[''id''=>5867867,''title''=>''Some Title'']
];
usort($array,function($a,$b)use(&$order,$outlier){ // make $order modifiable with &
if(!isset($order[$a[''id'']])){$order[$a[''id'']]=$outlier;} // update lookup array with [id]=>[outlier number]
if(!isset($order[$b[''id'']])){$order[$b[''id'']]=$outlier;} // and again
return $order[$a[''id'']]<=>$order[$b[''id'']];
});
var_export($array);
Método alternativo n. ° 2 : usort
cuando algunos valores de id
no existen en $order
... También me gustaría mencionar que, en algunos casos, evitar la doble llamada iterada de isset()
puede ser menos atractivo que preparar completamente el conjunto $order
antes de llamar a usort()
.
Este one-liner asegurará que no falten valores de id
, eliminando así la necesidad de otra cosa que no sea la línea de comparación dentro de la función de clasificación. ( Demostración completa de fragmentos )
$order=array_replace(array_fill_keys(array_column($array,''id''),$outlier),$order);
Puede usar usort()
para dictar con precisión cómo se ordenará la matriz. En este caso, la matriz $order
se puede usar dentro de la función de comparación.
El siguiente ejemplo utiliza un closure
para facilitar la vida.
$order = array(3452342, 5867867, 7867867, 1231233);
$array = array(
array(''id'' => 7867867, ''title'' => ''Some Title''),
array(''id'' => 3452342, ''title'' => ''Some Title''),
array(''id'' => 1231233, ''title'' => ''Some Title''),
array(''id'' => 5867867, ''title'' => ''Some Title''),
);
usort($array, function ($a, $b) use ($order) {
$pos_a = array_search($a[''id''], $order);
$pos_b = array_search($b[''id''], $order);
return $pos_a - $pos_b;
});
var_dump($array);
La clave para este funcionamiento es tener los valores que se comparan, sean las posiciones de los id
dentro de la matriz $order
.
La función de comparación funciona al encontrar las posiciones de los identificadores de dos elementos que se compararán dentro de la matriz $order
. Si $a[''id'']
viene antes de $b[''id'']
en la matriz $order
, entonces el valor de retorno de la función será negativo ( $a
es menor, por lo que "flota" en la parte superior). Si $a[''id'']
viene después de $b[''id'']
, la función devuelve un número positivo ( $a
es mayor, por lo que "se hunde").
Finalmente, no hay una razón especial para usar un cierre; es solo mi forma de escribir este tipo de funciones desechables rápidamente. También podría usar una función normal y nombrada.