DMarlon Studio

Lesson 12 · 12 of 20

Generics.Collections · Pascal

Count sales per city with TDictionary and sort the result with TList, the generic collections in Free Pascal's Generics.Collections unit.

Pascal · Generics.Collections in DMarlon Studio: the example's code and its output in the terminal

The `Generics.Collections` unit offers the same collections as Delphi: TList<T>, TDictionary<K, V>, TObjectList<T>, TQueue<T> and more. Here TSales is a dictionary from city to number of orders and TCityList a list of strings.

The loop counts the orders in Orders. `TryGetValue` looks up the city and, if it is there, puts its count in count and returns True; then sales[city] := count + 1 updates it. If not, Add creates it with 1. Careful: Add with an existing key raises an exception; for "add or replace" there is AddOrSetValue.

A dictionary stores its keys by *hash*, so its order cannot be trusted and could change with another version or other data. For stable output, the program copies the keys into the list with cities.AddRange(sales.Keys), sorts them with cities.Sort and walks the list, asking sales[city] for each count.

Count says how many cities there are and `ContainsKey` asks about a key safely. Both collections are freed in the finally block. When compiling, Free Pascal 3.2.2 shows a few warnings that come from the unit itself (Constructing a class ... with abstract method): they are internal to the library and do not affect your program.

Code · 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.

Output · fpc main.pas && ./main

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

Key points

  • TDictionary<K, V> finds values by key very quickly.
  • TryGetValue reads without raising if the key is missing.
  • Add fails on duplicate keys; AddOrSetValue does not.
  • Dictionary order is not guaranteed: sort the keys separately.
  • TList<T> has AddRange, Sort and for ... in.

Exercise

Create a second TDictionary<String, Double> with the amount of each order and print, city by city in alphabetical order, how much has been invoiced.