2010-02-17 12 views
16

tengo una serie de claves de cadena con valores numéricos para su uso en una lista de etiquetas con el número de ocurrencias de cada etiqueta, por lo tanto:Ordenamiento múltiple de PHP: por valor, luego por clave?

$arrTags['mango'] = 2; 
$arrTags['orange'] = 4; 
$arrTags['apple'] = 2; 
$arrTags['banana'] = 3; 

esto es por lo que puedo mostrar la lista de etiquetas de mayor a menor ocurrencia fin por lo tanto:

orange (4) 
banana (3) 
mango (2) 
apple (2) 

puedo usar arsort para revertir ordenar por el valor que es brillante, pero también quiero las etiquetas que tienen el mismo valor numérico que ser ordenados alfabéticamente, por lo que el resultado final puede ser:

orange (4) 
banana (3) 
apple (2) 
mango (2) 

¿Hay alguna manera de hacerlo? Supongo que usort puede ser el camino a seguir, pero miro los ejemplos en php.net y mis ojos se vuelven vidriosos. ¡¡¡muchas gracias!!!

Respuesta

12

Tener un vistazo a los ejemplos # 3: http://php.net/manual/en/function.array-multisort.php

Tendrá que crear dos matrices para utilizar como índices; uno compuesto por las teclas de la matriz original y el otro de los valores de la matriz original.

Luego use multisort para ordenar por valores de texto (claves de la matriz original) y luego por los valores numéricos (valores de la matriz original).

+3

@MMior Entonces, ¿cuál es la solución no complicada? –

+1

vea @Jon Bernhardt a continuación para un buen ejemplo de implementación. –

0

No lo he intentado, pero ¿ha intentado ordenar primero las teclas ascendiendo, luego ordenando los valores descendentes?

Parece que la mayoría de los tipos no moverán elementos si ya son lo mismo.

0

Puede crear una función para crear una matriz vacía e insertarlas en orden según las comparaciones.

0

que estás pensando demasiado complicado:

ksort($arrTags); 
arsort($arrTags); 

Ahora la matriz se ordena como usted desea.

+0

Estaba a punto de publicar esta respuesta exacta. De todos modos, lo probé y puedo confirmar que funciona. –

+3

Los tipos de php no son estables, por lo que no se garantiza que esto funcione. http://www.php.net/manual/en/array.sorting.php – goat

+0

@chris, eso es cierto, pero todavía no he encontrado ningún caso en el que no funcione, así que seguiría con esto. –

13

SOLUCIONADO

Después de un poco de experimentación descubrí que array_multisort hace el truco muy bien:

$tag = array(); 
$num = array(); 

foreach($arrTags as $key => $value){ 
$tag[] = $key; 
$num[] = $value; 
} 

array_multisort($num, SORT_DESC, $tag, SORT_ASC, $arrTags); 

:)

+2

Esa sería la respuesta de SlappyTheFish, ¿verdad? Creo que debes marcar esa respuesta como aceptada y luego mirar las funciones array_keys() y array_values ​​() para deshacerte de tu loop. –

+2

También una nota para futuros visitantes: '$ tag = array_keys ($ arrTags); $ num = array_values ​​($ arrTags); 'formarían las matrices idénticas sin un bucle. – nickb

+0

¿Por qué esto no funciona para mí? Copio la muestra anterior y la solución acaba de obtener el mismo resultado en la pregunta. –

2

SlappyTheFish es re correcta: el uso de array_multisort vs ksort, arsort

En el ejemplo de David ksort, arsort funciona bien, sin embargo, si Los valores de cadena de ys contienen caracteres distintos a los caracteres alfabéticos, es posible que el orden no funcione según lo previsto.

ex:

$arrTags['banana'] = 3; 
$arrTags['mango'] = 2; 
$arrTags['apple1'] = 2; 
$arrTags['orange'] = 4; 
$arrTags['almond1'] = 2; 

ksort($arrTags); 
arsort($arrTags); 

print_r($arrTags); 

devuelve:

Array 
(
    [orange] => 4 
    [banana] => 3 
    [apple1] => 2 
    [mango] => 2 
    [almond1] => 2 
) 

sin embargo usando:

$arrTags['banana'] = 3; 
$arrTags['mango'] = 2; 
$arrTags['apple1'] = 2; 
$arrTags['orange'] = 4; 
$arrTags['almond1'] = 2; 

$tag = array(); 
$num = array(); 

foreach($arrTags as $key => $value){ 
    $tag[] = $key; 
    $num[] = $value; 
} 

array_multisort($num, SORT_DESC, $tag, SORT_ASC, $arrTags); 


print_r($arrTags); 

devuelve:

Array 
(
    [orange] => 4 
    [banana] => 3 
    [almond1] => 2 
    [apple1] => 2 
    [mango] => 2 
) 
28

Como Scott Saunders insinúa en su comentario a la solución de David, puede usar las funciones array_keys() y array_values ​​() para deshacerse del bucle. De hecho, se puede solucionar esto de una línea de código:

array_multisort(array_values($arrTags), SORT_DESC, array_keys($arrTags), SORT_ASC, $arrTags); 
+0

Gracias por este código. Tengo la situación exacta como la pregunta original y esto funciona para mí. Lo que me molesta es que no estoy exactamente seguro de lo que está sucediendo aquí. ¿Cuál es la función del último parámetro? Veo que el último parámetro (que se pasa por referencia) es la única posibilidad para devolver la matriz modificada, OK. Pero, ¿cómo se relacionan los conjuntos de conjuntos entre sí? Cuando el multisort hace su trabajo yendo de izquierda a derecha, los arreglos subsiguientes se ordenan junto con ellos. Si es así, entonces todas las matrices deben tener la misma longitud que no es requerida por la documentación.#confused – andypotter

+0

@andypotter: Básicamente, 'array_multi_sort()' creará internamente otra matriz '$ indirect' donde cada índice es una matriz que contiene todos los valores de las matrices proporcionadas:' $ indirect [$ i] = [$ v1, $ v2, $ v3, ..., NULL] '. Luego se aplica QuickSort en la matriz usando una función de comparación especial que primero comparará '$ indirecto [$ a] [$ r]' a '$ indirecto [$ b] [$ r]' donde '$ r == 0' si son iguales, '$ r' se incrementa hasta que' $ indirect [$ a] [$ r] 'es' null' (todas las matrices provistas se han utilizado). Finalmente, cada arreglo se reescribe de acuerdo con '$ indirecto'. Si utiliza matrices de diferentes tamaños, se devuelve FALSE. – 2072

+0

Brillante - gracias Jon! – Martin

5

La solución propuesta anterior parece lógico, pero simplemente doens't trabajo:

ksort($arrTags); 
arsort($arrTags); 

El código PHP completa para realizar el pedido clasificación, serán los siguientes:

$k = array_keys($arrTags); 
$v = array_values($arrTags); 
array_multisort($k, SORT_ASC, $v, SORT_DESC); 
$arrTags = array_combine($k, $v); 

tenga en cuenta que array_multisort() utiliza referencias de entrada del usuario, por lo que tendrá que utilizar dos variabels temporales (k $ y $ v) para suministrar el contenido como la entrada del usuario. De esta forma, array_multisort() puede cambiar el contenido. Más adelante, reconstruya la matriz ordenada mediante array_combine().

He construido una función reutilizable para realizar esta tarea:

<?php 
/** 
* Sort a multi-dimensional array by key, then by value. 
* 
* @param array Array to be sorted 
* @param int One of the available sort options: SORT_ASC, SORT_DESC, SORT_REGULAR, SORT_NUMERIC, SORT_STRING 
* @param int One of the available sort options: SORT_ASC, SORT_DESC, SORT_REGULAR, SORT_NUMERIC, SORT_STRING 
* @return void 
* @example The following array will be reordered: 
* $a = array(
*  'd' => 4, 
*  'c' => 2, 
*  'a' => 3, 
*  'b' => 1, 
*  'e' => 2, 
*  'g' => 2, 
*  'f' => 2, 
* ); 
* SortArrayByKeyThanValue($a);  # reorder array to: array(
*  'b' => 1, 
*  'c' => 2, 
*  'e' => 2, 
*  'f' => 2, 
*  'g' => 2, 
*  'a' => 3, 
*  'd' => 4, 
* ); 
* @author Sijmen Ruwhof <sijmen(a)secundity.com> 
* @copyright 2011, Secundity 
*/ 
function SortArrayByKeyThanValue (&$pArray, $pSortMethodForKey = SORT_ASC, $pSortMethodForValue = SORT_ASC) 
{ 
    # check user input: sorting is not necessary 
    if (count($pArray) < 2) 
     return; 

    # define $k and $v as array_multisort() needs real variables, as user input is put by reference 
    $k = array_keys ($pArray); 
    $v = array_values($pArray); 

    array_multisort(
     $v, $pSortMethodForValue, 
     $k, $pSortMethodForKey 
    ); 
    $pArray = array_combine($k, $v); 
} 
?> 
0
//preserve arrays keys for later use 
$ar1= array_keys($your_array); 

//preserve array's values for later use 
$ar2= array_values($your_array); 

//perform sorting by value and then by key 
array_multisort($ar2, SORT_DESC, $ar1, SORT_DESC); 

//combine sorted values and keys arrays to new array 
$sorted_array = array_combine($ar1, $ar2); 

debe estar bien.

Cuestiones relacionadas