• Publicidad

Cómo ordenar esta cadena... (dirección postal)

¿Ya sabes lo que es una referencia? Has progresado, el nível básico es cosa del pasado y ahora estás listo para el siguiente nivel.

Cómo ordenar esta cadena... (dirección postal)

Notapor rookie » 2006-07-28 13:38 @610

Saludos .
Tengo una dirección de un domicilio en un campo de texto de una tabla como esta:
Edificio E Manzana 36 Departamento 15

El problema es que el orden es incorrecto, ya que primero debe ir Manzana, seguido de Edificio y despues Departamento. De tal forma que debe quedar asi:
Manzana 36 Edificio E Departamento 15

Y asi tengo una gran cantidad de domicilios que hay que ordenar, hasta el momento ya tengo un script en perl que me indica cuales domicilios tienen un orden correcto y cuales no. Ademas el mismo script me indica cual es el orden correcto en el que deberían estar escritas las direcciones.

El problema es: ¿cómo sacar de la cadena incorrecta la parte de la cadena que debe ir primero y sacarla con todo y el número o letra correspondiente.? En el ejemplo anterior deberia ser:
Manzana 36
y así consecutivamente el orden de las otras partes del domicilio.
Hay que tomar en cuenta que en otras direcciones puede variar el identificador y el número, por ejemplo
Manzana 1-A

¿Cómo puedo sacar cada una de las partes de los domicilios para formar el correcto?

Gracias por su ayuda.
rookie
Perlero nuevo
Perlero nuevo
 
Mensajes: 41
Registrado: 2006-03-01 18:48 @825

Publicidad

Notapor explorer » 2006-07-28 14:46 @657

Como el separador me parece que es el espacio en blanco, se puede hacer muy rápido para sacar los campos:
Código: Seleccionar todo
@campos = split " ", $dirección;
# Ahora los campos están en $campo[0], $campo[1], $campo[2]...
# Por ejemplo, para el caso de
# Edificio E Manzana 36 Departamento 15
print "@campos[2,3,0,1,4,5]"; #  Manzana 36 Edificio E Departamento 15


El problema viene si en la $dirección no hay exactamente 6 campos...
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor kidd » 2006-07-29 12:21 @556

Hola:

A mi se me ocurre que lo podrías hacer con un regexp:

Código: Seleccionar todo
$direccion =~ s/(Manzana [-1-9A-Za-z]+ )//;

$direccion = $1 . $direccion;



Saludos
Uriel Lizama Perl programmer fundador de Perl en Español
Perl Programming Language
Avatar de Usuario
kidd
Creador de Perl en Español
Creador de Perl en Español
 
Mensajes: 1166
Registrado: 2003-10-15 16:52 @744
Ubicación: México

Notapor rookie » 2006-08-07 17:22 @765

Saludos a todos.
Disculpas por tardar tanto en responder. Bien... antes que nada gracias por su ayuda.
Efectivamente como dice 'explorer' es un problema bastante interesante pues no hay una palabra a ordenar unicamente, sino un conjunto de palabras. Miren:
Código: Seleccionar todo
ejemplo 1  Cadena DESordenada:          "depto 5 edificio A"
           Debería quedar ordenada así: "edificio A depto 5"

ejemplo 2  Cadena DESordenada:          "Manzana 4  colonia 3  casa 21"
           Debería quedar ordenada así: " Colonia 3 Manzana 4 casa 21"

ejemplo 3  Cadena DESordenada:          "depto 8  edificio 2  colonia XY"
           Debería quedar ordenada así: " Colonia XY edificio 2  depto 8"

Tengo ya un script en Perl que me ordena la cadena, pero no me funciona para el 3er caso. Algo falta por hacer. La verdad es un script muy básico, pero es lo más cercano que he hecho para solucionar el problema, les paso parte del script . Sus opiniones son importantes para ayudarme... gracias de antemano.

Hasta antes de este punto, ya tengo en un arreglo el orden en el que debe de estar ordenada la cadena, tengo los elementos separados en un arreglo. Ejemplo: @ordenado=('Colonia','Manzana','Casa'). Si ahora me dispongo a reordenar la cadena, creo que el problema está en esta parte. Si bandera es 0 la cadena está en orden correcto, sino se procede a ordenarla. (perdón si es poco elegante la manera de programar).

Código: Seleccionar todo
if ($bandera==0) {
    $m++;
    print "orden correcto \n";
}
if ($bandera>=1) {
    print "Orden incorrecto en avza: $fila[0]---$fila[1] --- Deberia ser asi";
    $n++;
    print "TAM ORDEN: $tam_orden...";
    #ordenado =arreglo en donde se tienen las palabras con el orden correcto
    #tam_orden= tamaño del arreglo en donde tengo las palabras en el orden en el que deberian estar.
    #avza = cadena original que se debe ordenar
                       
    for($i=0;$i<=$tam_orden;$i++) {
        $k=$i+1;
        if ($k<$tam_orden) {
            $pos1=index($avza,$ordenado[$i]);
            $pos2=index($avza,$ordenado[$k]);
            if ($pos2<$pos1) {
                @arr_avza=split("",$avza);
                $t=scalar(@arr_avza);  #avza por letras
                $res=$t-$pos1;
                print " T es $t sacando a $ordenado[$i] apartir de la posicion $pos1 con $res elementos\n";
                $resultado[0] = substr($avza,$pos1,$res);
                $avza =~ s/$resultado[0]//;
                #print "NUEVO _ AVZA :$avza";
            }
            else {
                $res=$pos2-$pos1;
                $resultado[1] = substr($avza,$pos1,$res);
            }
        }
        else {
            $resultado[1]=$avza;
        }
    }
    $avza2=join(' ',@resultado);
    print "----> $avza2\n";
    #actualizo la BD de donde lei la direccion.
    $dbh2->do("UPDATE ced_info_gral SET avza='$avza2' WHERE folio=$fila[0]")
        or die ("Fallo la actualizacion de AVZA\n");
}
rookie
Perlero nuevo
Perlero nuevo
 
Mensajes: 41
Registrado: 2006-03-01 18:48 @825

Notapor explorer » 2006-08-07 19:52 @870

Respuesta rápida:
* En el bucle for no puedes ir desde 0 a tam_orden, porque entonces estás mirando un elemento más que el tamaño de @ordenado.
* Deberías inicializar @resultado en cada vuelta para evitar que el resultado de un bucle afecte al siguiente.
* La variable @arr_avza te la puedes ahorrar usando la función length: $t = length($avza);
* No compruebas el caso de que los index fallen. Es decir, que no encuentren la palabra que buscas. En ese caso devuelven el valor -1. Y ese valor no te sirve de nada para los cálculos siguientes.
* ¿Qué ocurre con los casos de mayúsculas y minúsculas? index puede encontrarte Manzana, pero no manzana.
* Hay un problema de lógica:
Supongamos que tenemos que ordenar "Manzana 4 colonia 3 casa 21". Al empezar el bucle comenzamos por buscar la combinación colonia/Manzana, y la encontramos, pero invertida, por lo que procedemos a su inversión, pero la inversión 'se lleva consigo' a la 'casa 21', por lo que entonces queda 'colonia 3 casa 21 Manzana 4', que es incorrecto. No entiendo como a tí te sale bien... El tercer caso tampoco funcionará por lo mismo: a la hora de hacer la inversión podemos colocar otros campos fuera de orden.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor explorer » 2006-08-07 20:55 @913

Bueno, he encontrado una forma:
Código: Seleccionar todo
    1 #!/usr/bin/perl
    2 use warnings;
    3
    4 # Entrada de prueba
    5 @entrada = (
    6                 "depto 5 edificio A",
    7                 "Manzana 4 colonia 3 casa 21",
    8                 "depto 8  edificio 2  colonia XY",
    9 );
   10
   11 @ordenado = qw(
   12     colonia
   13     manzana
   14     casa
   15     edificio
   16     depto
   17 );
   18 $tam_ordenado = scalar @ordenado;
   19
   20 foreach $avza ( @entrada ) {
   21     print "=>$avza.\n";
   22     $nueva = lc($avza);                             # Lo pasamos a minúsculas
   23
   24     for ( $i = 0;    $i < $tam_ordenado; $i++ ) {
   25     for ( $k = $i+1; $k < $tam_ordenado; $k++ ) {
   26         #print "\tBuscando $ordenado[$i]/$ordenado[$k]\n";
   27         $pos1 = index($nueva, $ordenado[$i]);
   28         $pos2 = index($nueva, $ordenado[$k]);
   29         next if $pos1 <0 or $pos2 <0;
   30         # Encontrada una pareja. Ver si está al revés
   31         if ( $pos1 > $pos2 ) {                      # Si, hay que dar la vuelta
   32             $nueva =
   33                 substr($nueva, 0, $pos2) .
   34                 substr($nueva, $pos1) . ' ' .
   35                 substr($nueva, $pos2, $pos1 - $pos2 - 1);
   36             print "|>$nueva.\n";
   37     }   }   }
   38     print "+>$nueva.\n";
   39 }
   40 __END__
Salida
Código: Seleccionar todo
=>depto 5 edificio A.
|>edificio a depto 5.
+>edificio a depto 5.
=>Manzana 4 colonia 3 casa 21.
|>colonia 3 casa 21 manzana 4.
|>colonia 3 manzana 4 casa 21.
+>colonia 3 manzana 4 casa 21.
=>depto 8  edificio 2  colonia XY.
|>depto 8  colonia xy edificio 2 .
|>colonia xy edificio 2  depto 8 .
+>colonia xy edificio 2  depto 8 .

Explicación:
* En la línea 20 hacemos un bucle por toda la @entrada
* En la línea 22, copiamos la cadena a analizar en $nueva, pasándola a minúsculas (esto es opcional, claro)
* De la 24 a 37 tenemos un doble bucle de búsqueda. Un doble bucle es necesario para contemplar el caso de que alguna de las direcciones tenga palabras 'desaparecidas' (por ejemplo, una casa dentro de una colonia)
* En las líneas 27 y 28 buscamos las dos palabras que nos interesan
* Si falta alguna de ellas, es tontería seguir, así que saltamos al siguiente (next) bucle
* Ahora, en la 31 comprobamos si están en orden inverso
* Si lo están, entonces hay que invertir su posición (32 a 35).

El proceso de 'inversión' es el siguiente:
* Hemos encontrado dos palabras, una en la posición $pos1 y otra en la $pos2
* Dividimos la cadena original ($nueva) en 3 partes:
a) La primera parte es la que va desde el comienzo hasta la primera de las palabras encontradas (sabemos que está en $pos2)
b) La segunda parte va desde $pos1 hasta el final de la cadena, más un espacio (ahora explico porqué)
c) La tercera parte es el trozo de cadena que está entre $pos2 y $pos1, menos una posición (ahora explico porqué).

El resultado es que se invierten las posiciones $pos2 y $pos1, respetando lo que ya estuviera ordenado al comienzo de la cadena.

* ¿Por qué sumamos un espacio en blanco en b)? Pues para que no quede pegado a c)
* ¿Y por qué quitamos una posición a c)? Esa posición es el espacio en blanco antes de $pos1. Si hacemos la inversión, ese espacio en blanco quedará al final, por lo que no nos sirve para nada
* De hecho... fíjate que sumamos un espacio en un sitio y se lo quitamos en otro... se podría haber recortado de otra manera para llevar esos espacios en blanco, pero hubiera quedado más complicado, con más cuentas.

Funciona, pero plantea más problemas... Antes hemos quitado las mayúsculas en la línea 22 para que pudiéramos hacer funcionar el index en 27 y 28 sin problemas, con los valores de @ordenado. Pero ¿queremos guardarlo así en la base de datos?.

Otro detalle: fíjate como respeta el caso de que existan dobles espacios en blanco entre las entradas.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor explorer » 2006-08-07 21:21 @931

Y esta es otra forma, usando expresiones regulares:
Código: Seleccionar todo
    1 #!/usr/bin/perl -l
    2 use warnings;
    3
    4 # Entrada de prueba
    5 @entrada = (
    6                 "depto 5 edificio A",
    7                 "Manzana 4  colonia 3  casa 21",
    8                 "depto 8  edificio 2  colonia XY",
    9 );
   10
   11 # Qué buscamos
   12 @buscamos = qw(
   13                 Colonia
   14                 Manzana
   15                 Casa
   16                 Edificio
   17                 Dpto|Depto|Departamento
   18 );
   19
   20 # Construir la expresión regular
   21 @buscamos = map { "($_)" . '\s+(\S+)' } @buscamos;
   22
   23 # Para todas las entradas...
   24 foreach $entrada ( @entrada ) {
   25     print ">$entrada";                      # Cómo era antes
   26     @final = ();                            # Aquí guardaremos el resultado
   27
   28     # Para todo lo que buscamos...
   29     foreach $buscamos ( @buscamos ) {
   30         if ( $entrada =~ m/$buscamos/i ) {      # ¿Está?
   31             push @final, ucfirst($1) . " $2";   # Pues lo guardamos
   32         }
   33     }
   34
   35     $final = join ' ', @final;              # Sacamos el resultado
   36     print $final if $final;
   37 }
   38
   39 __END__

>depto 5 edificio A
Edificio A Depto 5
>Manzana 4  colonia 3  casa 21
Colonia 3 Manzana 4 Casa 21
>depto 8  edificio 2  colonia XY
Colonia XY Edificio 2 Depto 8

Explicación:
* Tenemos por una parte, una @entrada y lo que @buscamos. Notar que lo que buscamos puede ser una expresión regular, como sucede en el caso del Departamento, que puede estar escrito de 3 formas distintas
* En la línea 21, reescribimos lo que @buscamos a algo así: (Colonia)\s+(\S+), que tiene toda la pinta de ser una expresión regular, que quiere decir: Busca la palabra Colonia -y la almacenas en $1- que esté seguida por un conjunto de espacios en blanco (\s+) y seguida por un conjunto de caracteres que no sean caracteres en blanco (\S+) -y esos me los guardas en $2-. Como ves, estamos reproduciendo el aspecto de cada uno de los campos.
* De la 24 a 37 tenemos el bucle por toda la @entrada
* En la línea 26, inicializamos @final como un array donde guardaremos el resultado de la búsqueda
* En la 29 iniciamos un bucle por cada campo que @buscamos
* En la 30, miramos a ver si en nuestra $entrada existe ese campo que $buscamos, coincidente con nuestra expresión regular. Lo hacemos además independientemente de si está en mayúsculas o minúsculas (/i)
* En caso de que así sea (31), guardamos en @final una cadena de caracteres compuesta de: nuestro campo (Colonia, Manzana, Casa, etc), con la primera letra puesta en mayúsculas (ucfirst), seguida de un sólo espacio en blanco y de la segunda parte del campo
* Finalmente, en la 35, unidos todas las partes y lo sacamos fuera.

Es una forma curiosa porque estamos creando expresiones regulares para extraer información de la $entrada, en el orden que nos interesa, en vez de girarla, como antes. El resultado está limpio de espacios en blanco inútiles, mayúsculas en cada campo y los campos ordenados.

Esta idea, la de extracción de información, es la que debe primar en este tipo de problemas.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor kidd » 2006-08-08 12:53 @578

Hola:

Me pareció interesante tu problema y se me ocurrió una solución:


Primero inicializamos las variables. En el array @entrada vendrán todas las entradas que quieres ordenar, y en @ordenado el orden de los campos que quieres.
Código: Seleccionar todo
#Definimos las entradas
my @entrada = (
               "depto 5 edificio A",
               "Manzana 4 colonia 3 casa 21",
               "depto 8  edificio 2  colonia XY",
              );


#Como quieres ordenar los elementos
my @ordenado = qw(
                  colonia
                  manzana
                  casa
                  edificio
                  depto
                 );




Después vamos a construir las expresiones regulares de cada uno de los campos, no tiene caso hacerlo una y otra vez por ello lo hacemos de una sentada:
Código: Seleccionar todo
my %Expr;
map { $Expr{$_} = qr/$_ ([-a-zA-Z0-9]+)/i } @ordenado;



Luego vamos a crear una función a la que vamos a llamar parse_entrada(). Lo que va a hacer ésta función es recibir una línea de las entradas, separar la entrada por elementos (colonia, manzana, calle, etc) y regresarlo como una referencia a un hash:
Código: Seleccionar todo
sub parse_entrada{

my $string = shift;
my %Data;

    for my $ordenado(@ordenado){

        if($string =~ $Expr{$ordenado}){
            $Data{$ordenado} = $1;
        }
    }


return \%Data;

}



Finalmente vamos a procesar cada una de nuestras entradas:
Código: Seleccionar todo
for my $entrada(@entrada){

    my $ref_data = parse_entrada($entrada);

    map { print "$_ $ref_data->{$_} " if defined($ref_data->{$_}) } @ordenado;
    print "\n";

}


Al momento de hacer el print ya lo hace con nuestros elementos ordenados de manera correcta.


Saludos
Uriel Lizama Perl programmer fundador de Perl en Español
Perl Programming Language
Avatar de Usuario
kidd
Creador de Perl en Español
Creador de Perl en Español
 
Mensajes: 1166
Registrado: 2003-10-15 16:52 @744
Ubicación: México

Notapor rookie » 2006-08-08 17:24 @767

Saludos a todos..

Muy interesantes las 3 soluciones propuestas... dejenme las llevo a la practica y estaré reportando como me fue .

Mil gracias por la ayuda.
Saludos
rookie
Perlero nuevo
Perlero nuevo
 
Mensajes: 41
Registrado: 2006-03-01 18:48 @825


Volver a Intermedio

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 9 invitados