Foros del Web » Programando para Internet » PHP »

ordenar con ksort() y dos criterios yuxtapuestos

Estas en el tema de ordenar con ksort() y dos criterios yuxtapuestos en el foro de PHP en Foros del Web. Bueno, una nueva consulta rebuscadilla: Supongamos que tengo este array asociativo, cuyas keys son tres valores separados por comas, y cuyos valores son simples integers ...
  #1 (permalink)  
Antiguo 05/12/2007, 19:24
Avatar de derkenuke
Colaborador
 
Fecha de Ingreso: octubre-2003
Ubicación: self.location.href
Mensajes: 2.665
Antigüedad: 20 años, 6 meses
Puntos: 45
ordenar con ksort() y dos criterios yuxtapuestos

Bueno, una nueva consulta rebuscadilla:

Supongamos que tengo este array asociativo, cuyas keys son tres valores separados por comas, y cuyos valores son simples integers:
Código PHP:
Array
(
    [
221,79,75] => 3344
    
[162,50,40] => 533
    
[213,63,53] => 30
    
[87,58,24] => 1073
    
[79,45,62] => 1963
    
[143,89,7] => 120
    
[208,100,18] => 29
    
[31,36,86] => 104
    
[165,2,100] => 70
    
[35,70,38] => 82
    
[34,100,15] => 27
    
[145,20,61] => 2
    
[25,56,60] => 3
    
[264,26,38] => 1
    
[327,14,31] => 2
    
[12,27,15] => 1

Si los deseara ordenar por la primera columna de la key, es decir, según los valores 221, 162, 213, 87... puedo usar para ello la función ksort(), pero como me los comparará como string y no como números tengo que hechar mano de uksort() para utilizar un criterio personalizado:
Código PHP:
function criterio($a,$b) {
    
$ha explode(",",$a);
    
$ha = (int) $ha[0];
    
$hb explode(",",$b);
    
$hb = (int) $hb[0];
    return (
$ha<$hb)? -: ( ($ha==$hb)?0:);
}
uksort($miArray"criterio"); 
Y satisfactoriamente da como resultado mi array ordenado por la primera columna del key:
Código PHP:
Array
(
    [
12,27,15] => 1
    
[25,56,60] => 3
    
[31,36,86] => 104
    
[34,100,15] => 27
    
[35,70,38] => 82
    
[79,45,62] => 1963
    
[87,58,24] => 1073
    
[143,89,7] => 120
    
[145,20,61] => 2
    
[162,50,40] => 533
    
[165,2,100] => 70
    
[208,100,18] => 29
    
[213,63,53] => 30
    
[221,79,75] => 3344
    
[264,26,38] => 1
    
[327,14,31] => 2


La duda viene ahora. Supongamos que, teniendo esta ordenación, quiero sub-ordenarla según la segunda columna del key, algo así como una cláusula order by múltiple de SQL. Ahora me diréis que no se puede porque no hay valores iguales en la primera columna, pero yo quiero considerar que son valores iguales todos los que distan entre sí una distancia $d, pongamos que $d=30.

Entonces encontramos en nuestro array varios "grupos" según esa $d:
Código PHP:
// primer grupo, media de la primera columna = 27,4 ~ 27
    
[12,27,15] => 1
    
[25,56,60] => 3
    
[31,36,86] => 104
    
[34,100,15] => 27
    
[35,70,38] => 82
// segundo grupo, media = 83
    
[79,45,62] => 1963
    
[87,58,24] => 1073
// tercer grupo, media = 153,75 ~ 154
    
[143,89,7] => 120
    
[145,20,61] => 2
    
[162,50,40] => 533
    
[165,2,100] => 70
// cuarto grupo, media = 214
    
[208,100,18] => 29
    
[213,63,53] => 30
    
[221,79,75] => 3344
// quinto grupo, media = 264
    
[264,26,38] => 1
// sexto grupo, media = 327 
    
[327,14,31] => 
Todavía estoy sin decidir cómo agrupar esos números, con qué algoritmo. Bueno, definidos esos grupos tendríamos este array ficticio a ordenar:
Código PHP:
Array
(
    [
27,27,15] => 1
    
[27,56,60] => 3
    
[27,36,86] => 104
    
[27,100,15] => 27
    
[27,70,38] => 82

    
[83,45,62] => 1963
    
[83,58,24] => 1073

    
[154,89,7] => 120
    
[154,20,61] => 2
    
[154,50,40] => 533
    
[154,2,100] => 70

    
[214,100,18] => 29
    
[214,63,53] => 30
    
[214,79,75] => 3344

    
[264,26,38] => 1

    
[327,14,31] => 2

Que consiguiríamos ordenarlo según la segunda columna con éste resultado:
Código PHP:
Array
(
    [
27,27,15] => 1
    
[27,36,86] => 104
    
[27,56,60] => 3
    
[27,70,38] => 82
    
[27,100,15] => 27

    
[83,45,62] => 1963
    
[83,58,24] => 1073

    
[154,2,100] => 70
    
[154,20,61] => 2
    
[154,50,40] => 533
    
[154,89,7] => 120

    
[214,63,53] => 30
    
[214,79,75] => 3344
    
[214,100,18] => 29

    
[264,26,38] => 1

    
[327,14,31] => 2


Bueno, incluso podríamos hacer lo mismo recursivamente para la tercera columna etc, pero los grupos son tan reducidos en elementos que ya ni me interesa. A todo este proceso supongo que no debo decir que no puedo perder las keys del array original (en estos ultimos ejemplos las he machacado)



La cuestión en sí: Ordenar el array según un criterio que lo ordene de menor a mayor en la primera columna, y después agrupar ciertos valores del array según una variable $d (definir el algoritmo) para reordenar los sub-grupos restantes según la segunda columna de menor a mayor también.



Espero que se me entienda, estoy totalmente perdido y no he podido ni intentar nada al respecto



__________________
- Haz preguntas inteligentes, y obtendrás más y mejores respuestas.
- Antes de postearlo Inténtalo y Búscalo.
- Escribe correctamente tus mensajes.
  #2 (permalink)  
Antiguo 05/12/2007, 20:23
Avatar de xknown  
Fecha de Ingreso: diciembre-2004
Ubicación: Cusco - Perú
Mensajes: 2.248
Antigüedad: 19 años, 3 meses
Puntos: 7
Re: ordenar con ksort() y dos criterios yuxtapuestos

Cambiando un poco tu función, puedes ordenar tu extraño arreglo en función a esos tres valores:
Código PHP:
function criterio($a,$b) {
    
$ha explode(",",$a);
    
$ha $ha[0] * pow(108) + $ha[1] * pow(107) + $ha[2];
    
$hb explode(",",$b);
    
$hb $hb[0] * pow(108) + $hb[1] * pow(107) + $hb[2];
    return (
$ha<$hb)? -: ( ($ha==$hb)?0:);
}
uksort($array"criterio"); 
El inconveniente de este método es que el peso que se asigna a una columna debe ser mayor al número máximo de la columna que está más a la derecha (en el ejemplo pongo arbitrariamente 10^8 para la primera y 10^7 para la segunda). Por otro lado, tampoco es muy flexible para ordenar las "columnas" por separado, es decir ordenar la primera columna en orden descendente, luego la segunda columna en orden ascendente ... etc.

El último punto que menciono en el párrafo anterior creo que podrías hacerlo con array_multisort, pero para eso necesitas un arreglo del tipo:
Código:
Array
(
    [1] => Array
        (
            [0] => 264
            [1] => 26
            [2] => 38
        )

    [3] => Array
        (
            [0] => 25
            [1] => 56
            [2] => 60
        )
)
Saludos
__________________
Alex Concha
Buayacorp - Programación y Diseño
  #3 (permalink)  
Antiguo 06/12/2007, 05:57
 
Fecha de Ingreso: septiembre-2007
Mensajes: 220
Antigüedad: 16 años, 6 meses
Puntos: 1
Re: ordenar con ksort() y dos criterios yuxtapuestos

No muy optimizado pero hace lo que quieres:

Código PHP:
function agruparNumeros($numeros$rango)
{
  
$result = array();
  
    foreach (
$numeros as $numero)
  {
      
$intervalo = array(
        
$numero $rango,
        
$numero $rango
      
);
    
      
$entras = array();
      foreach (
$result as $clave => $results)
      {
        if (
in_array($numero$results))
        {
            continue 
2;
        }
        
        
$init reset($results);
        
$end end($results);
          if (
            (
$intervalo[0] >= $init && $intervalo[0] <= $end) ||
            (
$intervalo[1] >= $init && $intervalo[1] <= $end) ||
            (
$intervalo[0] <= $init && $intervalo[1] >= $end)
          )
          {
              
$entras[] = $clave;
          }
      }
    
      if (
$entras)
      {
        
$entra_principal $entras[0];
        unset(
$entras[0]);
          
$result[$entra_principal][] = $numero;
        
          if (
count($entras) >= 1)
          {
              foreach (
$entras as $entra)
              {
                  foreach (
$result[$entra] as $valor)
                  {
                      
$result[$entra_principal][] = $valor;
                  }
                
                  if (
$entra != $entra_principal)
                  {
                      unset(
$result[$entra]);
                  }
              }
          }
      }
      else
      {
          
$result[] = array($numero);
      }
  }
  
  return 
$result;
}

function 
zerofill($numero$longitud)
{
    
$return $numero;
    while (
strlen($return) < $longitud)
    {
        
$return '0'.$return;
    }
    
    return 
$return;
}

function 
superorden($array$rango 30$order 'asc'$profundidad_order 'full'$profundiad_rango 'full')
{
  
$numeros_grupo = array();
  
$claves = array();
  
$zerofill 0;
  
$profundidad_max 0;
    foreach (
$array as $clave => $valor)
    {
        foreach (
explode(','$clave) as $grupo => $numero)
        {
            
$numeros_grupo[$grupo][$clave] = $numero;
            
$claves[$clave][$grupo] = $numero;
            
            if (
strlen($numero) > $zerofill)
            {
                
$zerofill strlen($numero);
            }
            
            if ((
$grupo 1) > $profundidad_max)
            {
                
$profundidad_max $grupo 1;
            }
        }
    }
    
    
// order
    
if ($profundidad_order == 'full' || $profundidad_order $profundidad_max)
    {
        
$profundidad_order $profundidad_max;
    }
    elseif (
$profundidad_order 1)
    {
        
$profundidad_order 1;
    }
    
// rango
    
if ($profundidad_rango == 'full' || $profundidad_rango $profundidad_max)
    {
        
$profundidad_rango $profundidad_max;
    }
    elseif (
$profundidad_rango 1)
    {
        
$profundidad_rango 1;
    }
    
    switch (
$order)
    {
      case 
'desc':
        
$function_sort 'arsort';
        break;
        
      case 
'asc':
      default:
        
$function_sort 'asort';
    }
    
    
$claves_global = array();
    foreach (
$numeros_grupo as $grupo => $numeros)
    {
      if (
$profundidad_order < ($grupo 1))
      {
          break;
      }
      
      
$dorango $profundidad_rango >= ($grupo 1) ? true false;
      
          
$order = array();
        foreach (
$numeros as $clave => $numero)
        {
            
$order[$clave] = isset($claves_global[$clave]) ? $claves_global[$clave].zerofill($numero$zerofill) : $numero;
        }
      
      
$function_sort($order);
      
      if (
$dorango)
      {
          
$medias = array();
        foreach (
agruparNumeros($numeros$rango) as $agrupacion)
        {
          
$media round(array_sum($agrupacion) / count($agrupacion));
            
            foreach (
$agrupacion as $numero)
            {
                
$medias[zerofill($numero$zerofill)] = $media;
            }
        }
      }
      
      
$claves_order = array();
      foreach (
$order as $clave => $numero)
      {
          
$claves_order[] = $clave;
          
$i++;
          
          if (
$dorango)
          {
              
$numero zerofill($numero$zerofill);
            
$numero substr($numerostrlen($numero) - $zerofill);
            
$claves_global[$clave] .= zerofill($medias[$numero], $zerofill);
          }
      }
      
        
$return = array();
        foreach (
$claves_order as $clave)
        {
            
$return[$clave] = $array[$clave];
        }
    }
    
    return 
$return;
}

$array = array(
  
'221,79,75'  => 3344,
  
'162,50,40'  => 533,
  
'213,63,53'  => 30,
  
'87,58,24'   => 1073,
  
'79,45,62'   => 1963,
  
'143,89,7'   => 120,
  
'208,100,18' => 29,
  
'31,36,86'   => 104,
  
'165,2,100'  => 70,
  
'35,70,38'   => 82,
  
'34,100,15'  => 27,
  
'145,20,61'  => 2,
  
'25,56,60'   => 3,
  
'264,26,38'  => 1,
  
'327,14,31'  => 2,
  
'12,27,15'   => 1
);

print_r(superorden($array30'asc'21)); 
  #4 (permalink)  
Antiguo 01/01/2008, 10:57
Avatar de derkenuke
Colaborador
 
Fecha de Ingreso: octubre-2003
Ubicación: self.location.href
Mensajes: 2.665
Antigüedad: 20 años, 6 meses
Puntos: 45
Re: ordenar con ksort() y dos criterios yuxtapuestos

Bueno gracias a todos, he seguido investigando pero no he llegado a nada que merezca la pena ser posteado, tan sólo intentos fallidos.


Un saludo
__________________
- Haz preguntas inteligentes, y obtendrás más y mejores respuestas.
- Antes de postearlo Inténtalo y Búscalo.
- Escribe correctamente tus mensajes.
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 15:18.