16 agosto 2007

El objeto StringList (y III)

IMPORTAR Y EXPORTAR ELEMENTOS DE LA LISTA

Se pueden guardar los elementos de la lista en un archivo de texto utilizando para ello el método SaveToFile:

procedure SaveToFile( const FileName: string ); virtual;

Por ejemplo vamos a guardar nuestra lista de clientes en el mismo directorio donde ejecutamos el programa:

Clientes.SaveToFile( ExtractFilePath( Application.ExeName ) + 'clientes.txt' );

Si más adelante deseamos recuperar el listado utilizamos el procedimiento:

procedure LoadFromFile( const FileName: string ); virtual;

Por ejemplo:

Clientes.LoadFromFile( ExtractFilePath( Application.ExeName ) + 'clientes.txt' );

Si tenemos dos listas en memoria también podemos copiar elementos de una lista a otra. Por ejemplo:

var
Animales, Animales2: TStringList;
begin
Animales := TStringList.Create;
Animales2 := TStringList.Create;

Animales.Add( 'PERRO' );
Animales.Add( 'GATO' );
Animales.Add( 'LEÓN' );

Animales2.Add( 'VACA' );
Animales2.Add( 'PATO' );
Animales2.Add( 'ÁGUILA' );

// Añadimos a la lista de ANIMALES el contenido de ANIMALES2 y ordenamos la lista
Animales.AddStrings( Animales2 );
Animales.Sort;

// Mostramos el resultado en un campo Memo
Memo.Lines.Add( Animales.Text );

Animales2.Free;
Animales.Free;
end;

El resultado de la lista Animales después de ordenarla sería:

ÁGUILA
GATO
LEÓN
PATO
PERRO
VACA

Otra de las cosas que se pueden hacer es asignar los elementos de una lista a otra, eliminando los elementos de la lista original (sería algo así como copiar y pegar). Por ejemplo si queremos sustituir todos los elementos de la lista Animales por los de Animales2 habría que hacer:

Animales.Assign( Animales2 );

lo cual elimina todos los elementos de la lista Animales e inserta todos los de Animales2.

LISTAS ORDENADAS AUTOMÁTICAMENTE

Anteriormente vimos como una lista podía ordenarse con el método Sort, pero resulta algo molesto y lento tener que volver a ordenar la lista cada vez que se inserta, modifica o elimina un elemento de la misma. Para evitar eso los objetos StringList disponen de la propiedad booleana Sorted la cual una vez esta activa ordenará todos los elementos de la lista automáticamente independientemente de las operaciones que se realicen en la misma.

Vamos a crear una nueva lista ordenada:

var
Vehiculos: TStringList;
begin
Vehiculos := TStringList.Create;
Vehiculos.Sorted := True;
Vehiculos.Add( 'RENAULT' );
Vehiculos.Add( 'PEUGEOT' );
Vehiculos.Add( 'MERCEDES' );
Memo.Lines.Add( Vehiculos.Text );
Vehiculos.Free;
end;

Al ejecutar este código mostrará en el campo Memo los elemento ya ordenados:

MERCEDES
PEUGEOT
RENAULT

Tanto si insertamos o eliminamos elementos, la lista seguirá ordenada:

Vehiculos.Delete( 3 );
Vehiculos.Add( 'BMW' );

Quedaría se la siguiente manera:

BMW
MERCEDES
PEUGEOT

Lo que no se puede hacer en una lista ordenada es modificar sus elementos:

Vehiculos[1] := 'AUDI';

Ya que provocaría el error:

Operation not allowed on sorted list (operación no permitida en una lista ordenada).

Para ello habría que desconectar la propiedad Sorted, modificar el elemento y volver a activarla:

Vehiculos.Sorted := False;
Vehiculos[1] := 'AUDI';
Vehiculos.Sorted := True;

Ahora si tendríamos el efecto deseado:

AUDI
BMW
PEUGEOT

Otra de las operaciones que no se pueden hacer en una lista ordenada es añadir elementos duplicados. En el caso anterior si hacemos:

Vehiculos.Add( 'BMW' )

no hará absolutamente nada. Para poder añadir elementos duplicados en una lista ordenada hay que modificar la propiedad Duplicates la cual puede contener los siguientes valores:

dupIgnore -> Ignora el añadir elementos duplicados (así es como está por defecto)
dupAccept -> Acepta elementos duplicados
dupError -> Da un error al añadir elementos duplicados

Si hacemos lo siguiente:

Vehiculos.Duplicates := dupAccept;
Vehiculos.Add( 'BMW' );

Entonces si funcionará correctamente:

AUDI
BMW
BMW
PEUGEOT

Pero si activamos:

Vehiculos.Duplicates := dupError;
Vehiculos.Add( 'BMW' );

Nos mostrará el mensaje:

String list does not allow duplicates (StringList no acepta duplicados).

ASOCIANDO OBJETOS A LOS ELEMENTOS DE UNA LISTA

Una de las características más importantes de una lista TStringList es la de asociar a cada elemento de la lista la instacia de un objeto creado. Vamos a ver un ejemplo donde voy a crear la clase TProveedor y voy a utilizarla para crear una lista de proveedores donde cada elemento tiene su propio objeto:

type
TProveedor = class
sNombre: String;
cSaldo: Currency;
bPagado: Boolean;
end;

var
Proveedores: TStringList;
Proveedor: TProveedor;
begin
Proveedores := TStringList.Create;

Proveedor := TProveedor.Create;
with Proveedor do
begin
sNombre := 'SUMINISTROS PALAZÓN';
cSaldo := 1200.24;
bPagado := False;
end;

Proveedores.AddObject( 'B78234539', Proveedor );

Proveedor := TProveedor.Create;
with Proveedor do
begin
sNombre := 'ELECTRIDAUTON, S.L.';
cSaldo := 2460.32;
bPagado := True;
end;

Proveedores.AddObject( 'B98654782', Proveedor );

Proveedor := TProveedor.Create;
with Proveedor do
begin
sNombre := 'FONTANERIA MARTINEZ, S.L.';
cSaldo := 4800.54;
bPagado := False;
end;

Proveedores.AddObject( 'B54987374', Proveedor );
end;

Hemos creado una lista de proveedores e instanciado tres proveedores asociandolos a la lista mediante el C.I.F. del proveedor. Ahora bien, ¿como accedemos a cada proveedore de la lista? Pues muy fácil. El objeto StringList dispone de la propiedad Objects la cual nos devuelve la referencia a un objeto en cuestión. Por ejemplo vamos a buscar a FONTANERIA MARTINEZ por su C.I.F.:

var
iProveedor: Integer;
begin
iProveedor := Proveedores.IndexOf( 'B54987374' );

if iProveedor > -1 then
begin
Proveedor := Proveedores.Objects[iProveedor] as TProveedor;
Memo.Lines.Add( '' );
Memo.Lines.Add( 'BUSCANDO AL PROVEEDOR: B54987374' );
Memo.Lines.Add( '' );
Memo.Lines.Add( 'NOMBRE: ' + Proveedor.sNombre );
Memo.Lines.Add( 'SALDO: ' + CurrToStr( Proveedor.cSaldo ) );
Memo.Lines.Add( 'PAGADO: ' + BoolToStr( Proveedor.bPagado, True ) );
end;
end;

El resultado sería:

BUSCANDO AL PROVEEDOR: B54987374

NOMBRE: FONTANERIA MARTINEZ, S.L.
SALDO: 4800,54
PAGADO: False

Esto es algo mucho más útil que utilizar un array:

var
Proveedores: array of TProveedor;
....

ya que es mucho más flexible y no hay que preocuparse por los huecos al añadir o eliminar registros. Esto nos permite volcar información de registros de la base de datos a memoria mapeando el contenido de un registro (Clientes, Facturas, etc) dentro de su clase correspondiente (Por ejemplo: se podrían cargar una cantidad indeterminada de albaranes del mismo cliente para sumarlos y pasarlos a factura).

Para añadir objetos a un StringList aparte de la función AddObject se pueden insertar objetos mediante el procedimiento:

procedure InsertObject( Index: Integer; const S: string; AObject: TObject ); override;

el cual inserta el elemento en la posición que deseemos desplazando el resto de la lista. Otra de las ventajas de asociar objetos a un StringList en vez de utilizar un array es que al liberarlo de memoria también elimina cada instancia de objeto asociado a cada elemento de la lista:

Proveedores.Free;

no teniendo así que preocuparnos con errores de liberación de memoria.

Con esto terminados de vez las características más importantes de la clase TStringList.

Pruebas realizadas en Delphi 7.

2 comentarios:

JJ López dijo...

Hola

Dime, des/ventajas en usar Add y Append? la una retorna algo y la otra nada, pero algo más aparte de eso ?

Saludos!

Administrador dijo...

Pues simplemente que Append inserta un elemento pero no devuelve nada y Add lo inserta y te devuelve su posición en la lista.

Saludos.

Publicidad