cli  
Article Series

This article series discuss more than 30 different programming languages. Please read overview before you read any of the details.

Playing with Records Related Articles.

Where to Discuss?

Local Group

Preface

Goal: A practical case to collect unique record fields using Free Pascal.


2: Data Structure

Meet The Songs Record

From just karaoke, we can go pro with recording studio.

From simple data, we can build a structure to solve our task.

Record Type

We can use user defined type to define a record:

Prepare the header: uses, type, and variable.

uses SysUtils;

type TSong = record
  Title : ANSIString;
  Tags  : array of ANSIString;
end;

var Song : TSong;

Then we can define the records in main program.

begin 
  with Song do
  begin
    Title := 'Cantaloupe Island';
    Tags  := ['60s', 'jazz'];
  end;
  Write(Song.Title + ': ');
  WriteLn(ANSIString.Join(', ', Song.Tags));
end.

With the result similar as below:

❯ ./02-record-a
Cantaloupe Island: 60s, jazz

Pascal: Record Type: Defining Record

Defining Record as Const

We can also use const to fill a record:

Prepare the header: uses, and type.

{$mode objfpc}{$H+}
uses SysUtils;

type TSong = record
  Title : string;
  Tags  : array of string;
end;

Then we can fill a record in const. Just one record.

const
  Song : TSong =
  ( Title : 'Cantaloupe Island';
    Tags  : ('60s', 'jazz') );

And access it later in main program.

begin 
  Write(Song.Title + ': ');
  WriteLn(string.Join(', ', Song.Tags));
end.

With the result similar as below:

❯ ./02-record-b
Cantaloupe Island: 60s, jazz

Pascal: Record Type: Fill a Record in Const

Array of Song Records

Isn’t it nice to have all record in a row, written directly in your code?

Of course we can do it:

Prepare the header: uses, type. Nothing change.

{$mode objfpc}{$H+}
uses SysUtils;

type TSong = record
  Title : string;
  Tags  : array of string;
end;

Then we can fill all the records in const, as an array of TSong.

const
  Songs : array of TSong =
  ( ( Title : 'Cantaloupe Island';
      Tags  : ('60s', 'jazz') ),
    ( Title : 'Let It Be';
      Tags  : ('60s', 'rock') ),
    ( Title : 'Knockin'' on Heaven''s Door';
      Tags  : ('70s', 'rock') ),
    ( Title : 'Emotion';
      Tags  : ('70s', 'pop') ),
    ( Title : 'The River';
      Tags  : () )
  );

Pascal: Array of Records: Defining Records

And access it later in main program. But beware that we required to define a Song variable, to get the loop variable properly.

var Song: TSong;

begin
  for Song in Songs do
  begin
    Write(format('%-20s',
      [Song.Title]) + ': ');
    WriteLn(string.Join(
      ', ', Song.Tags));
  end;
end.

With the result similar as below:

❯ ./03-songs
Cantaloupe Island   : 60s, jazz
Let It Be           : 60s, rock
Knockin' on Heaven's Door: 70s, rock
Emotion             : 70s, pop
The River           : 

Pascal: Array of Records: Fill all Records in Const


3: Separating Unit

Since we need to reuse the songs record multiple times, it is a good idea to separate the record from logic.

Songs Unit

The code can be shown as below:

{$mode objfpc}{$H+}

unit MySongs;
interface

type TSong = record
  Title : string;
  Tags  : array of string;
end;

const
  Songs : array of TSong = ...;

implementation

end.

Which the Songs can be witten as below:

const
  Songs : array of TSong =
  ( ( Title : 'Cantaloupe Island';
      Tags  : ('60s', 'jazz') ),
    ( Title : 'Let It Be';
      Tags  : ('60s', 'rock') ),
    ( Title : 'Knockin'' on Heaven''s Door';
      Tags  : ('70s', 'rock') ),
    ( Title : 'Emotion';
      Tags  : ('70s', 'pop') ),
    ( Title : 'The River';
      Tags  : () )
  );

Pascal: The Songs Unit Containing Array of Records

Using Songs Unit

Now we can have a very short code.

{$mode objfpc}{$H+}
uses SysUtils, MySongs;
var Song: TSong;

begin
  for Song in Songs do
  begin
    Write(format('%-20s',
      [Song.Title]) + ': ');
    WriteLn(string.Join(
      ', ', Song.Tags));
  end;
end.

Pascal: Using Songs Unit

With the result exactly the same as previous code.


4: Extracting Records

Now we can easily extract all songs records, in pure free pascal.

Note that we are using imperative approach, and no functional approach at all.

Array: Insert

There is this useful insert function, so we can merge an array into other array.

{$mode objfpc}{$H+}
uses SysUtils, MySongs;

var
  Song: TSong;
  Tags: array of string = ();

begin
  for Song in Songs do
    System.Insert(Song.Tags, Tags, High(Tags)+1);

  WriteLn(string.Join(', ', Tags));
end.

With the result similar as below:

❯ ./05-extract-a
60s, jazz, 60s, rock, 70s, rock, 70s, pop

Pascal: Array: Insert

Array of Array: Insert

But beware of the type. You might mistakenly insert array of array instead.

{$mode objfpc}{$H+}
uses SysUtils, MySongs;
type TTags = array of string;
var
  Song : TSong;
  Tags : TTags;
  Tagss: array of TTags = ();

begin
  for Song in Songs do
    Insert(Song.Tags, Tagss, High(Tagss)+1);

  for Tags in Tagss do
    WriteLn(string.Join(', ', Tags));
end.

With the result similar as below:

❯ ./05-extract-b
60s, jazz
60s, rock
70s, rock
70s, pop

Pascal: Array of Array: Insert

Array Flatten

But that is okay. We can still flatten with loop anyway.

Actually, loop after loop.

{$mode objfpc}{$H+}
uses SysUtils, MySongs;
type TTags = array of string;
var
  Song : TSong;
  Tags : TTags;
  Tagss: array of TTags = ();
  Flatten : TTags = ();

begin
  for Song in Songs do
    Insert(Song.Tags, Tagss, High(Tagss)+1);

  for Tags in Tagss do
    Insert(Tags, Flatten, High(Flatten)+1);

  WriteLn(string.Join(', ', Flatten));
end.

With the result similar as below:

❯ ./05-extract-c
60s, jazz, 60s, rock, 70s, rock, 70s, pop

Pascal: Array Flatten


5: Finishing The Task

Unique Tags.

We can have our unique tags, by using imperative approach.

Rebuild Array

Just like I said, pascal is more low level, than other language such as ruby or python.

To get the unique array, we need to iterate all the tags, and create new tags based on conditional filter.

Prepare the header: uses, type and variable definition.

{$mode objfpc}{$H+}
uses SysUtils, MySongs;
type TTags = array of string;
var
  Song  : TSong;
  Tags  : TTags = ();
  Unique: TTags = ();
  Tag, Uni : string;
  Exist : boolean;

Pascal: Unique Array: Header

Then we can rebuild array in main program, with the help of exist variable.

begin
  for Song in Songs do
    insert(Song.Tags, Tags, High(Tags)+1);

  for Tag in Tags do
  begin
    Exist := false;
    for Uni in Unique do
      if (Tag = Uni) then Exist := true;
    if (not Exist) then
      Insert(Tag, Unique, High(Unique)+1);
  end;

  WriteLn(string.Join(', ', Unique));
end.

With the result similar as below:

❯ ./01-tags-d
rock, jazz, rock, pop, pop.

Pascal: Unique Array: Main

Refactor Function

We can simplify that cryptic algorithm above, if we can just move a few code into its own function.

Prepare the header. Now it is very simple: only uses and type.

{$mode objfpc}{$H+}
uses SysUtils, MySongs;
type TTags = array of string;

Then the function:

function IsExist(
  Element: string; Tags: TTags): boolean;
var Tag: string;
begin
  Result := false;
  for Tag in Tags do
    if (Tag = Element) then Result := true;
end;

Pascal: Unique Array: Function

Then we can rebuild array in main program, with the help of IsExist function.

Prepare, we have to setup the variable definition.

var
  Song  : TSong;
  Tags  : TTags = ();
  Unique: TTags = ();
  Tag   : string;

And then the program main point.

begin
  for Song in Songs do
    Insert(Song.Tags, Tags, High(Tags)+1);

  for Tag in tags do
  begin
    if (not IsExist(Tag, Unique)) then
      Insert(Tag, Unique, High(Unique)+1);
  end;

  WriteLn(string.Join(', ', Unique));
end.

With the result similar as below:

❯ ./01-tags-d
rock, jazz, rock, pop, pop.

Pascal: Unique Array: Main

Now the code looks simpler.

Getting Started Right

Please be patient, We need to get to know the pure free pascal, before go on the more advance object pascal discussion.


What is Next 🤔?

We have alternative way to extract the record structure.

Consider continue reading [ Pascal - Playing with Records - Part Three ].