DMarlon Studio

Lección 12 · 12 de 20

Generics.Collections · Pascal

Cuente ventas por ciudad con TDictionary y ordene el resultado con TList, las colecciones genéricas de Generics.Collections en Free Pascal.

Pascal · Generics.Collections en DMarlon Studio: el código del ejemplo y su salida en la terminal

La unidad `Generics.Collections` ofrece las mismas colecciones que Delphi: TList<T>, TDictionary<K, V>, TObjectList<T>, TQueue<T>… Aquí TSales es un diccionario de ciudad a número de pedidos y TCityList, una lista de textos.

El bucle cuenta los pedidos de Orders. `TryGetValue` busca la ciudad y, si está, deja su cuenta en count y devuelve True; entonces sales[city] := count + 1 la actualiza. Si no está, Add la crea con 1. Ojo: Add con una clave repetida lanza una excepción; para «añadir o cambiar» está AddOrSetValue.

Un diccionario guarda las claves según su *hash*, así que su orden no es fiable y podría cambiar con otra versión o con otros datos. Para una salida estable, el programa copia las claves a la lista con cities.AddRange(sales.Keys), las ordena con cities.Sort y recorre la lista, pidiendo a sales[city] cada cuenta.

Count dice cuántas ciudades hay y `ContainsKey` pregunta por una clave sin riesgo. Las dos colecciones se liberan en el finally. Al compilar, Free Pascal 3.2.2 muestra unos avisos que vienen de la propia unidad (Constructing a class ... with abstract method): son internos de la biblioteca y no afectan al programa.

Código · main.pas

program main;uses Generics.Collections;type  TCityList = specialize TList<String>;  TSales = specialize TDictionary<String, Integer>;const  Orders: array[0..6] of String = ('Madrid', 'Sevilla', 'Madrid', 'Bilbao',    'Sevilla', 'Madrid', 'Valencia');var  sales: TSales;  cities: TCityList;  city: String;  count: Integer;begin  sales := TSales.Create;  cities := TCityList.Create;  try    for city in Orders do      if sales.TryGetValue(city, count) then        sales[city] := count + 1      else        sales.Add(city, 1);    // A dictionary has no fixed order: copy the keys and sort them    cities.AddRange(sales.Keys);    cities.Sort;    for city in cities do      WriteLn(city:9, sales[city]:3);    WriteLn('Cities: ', sales.Count, ', any in Malaga: ', sales.ContainsKey('Malaga'));  finally    cities.Free;    sales.Free;  end;end.

Salida · fpc main.pas && ./main

   Bilbao  1
   Madrid  3
  Sevilla  2
 Valencia  1
Cities: 4, any in Malaga: FALSE

Para recordar

  • TDictionary<K, V> busca valores por clave muy deprisa.
  • TryGetValue lee sin lanzar errores si la clave no existe.
  • Add falla con claves repetidas; AddOrSetValue, no.
  • El orden de un diccionario no está garantizado: ordene las claves aparte.
  • TList<T> tiene AddRange, Sort y for ... in.

Ejercicio

Cree un segundo diccionario TDictionary<String, Double> con el importe de cada pedido y escriba, ciudad por ciudad y en orden alfabético, cuánto se ha facturado.