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.

Of course there are other way to get unique value.


6: Using TStringList

This TStringList is simple. All we have to do is just read, the official documentation properly, and then use it by writing it in your code.

Delimited Text

There are already many helpful function in TStringList, but unfortunately there is no join and split method. so we have to use what already in there, such as delimited text

First, prepare the header. Now it is very simple: only uses and type. The skeleton is as below:

uses Classes;

var
  SL : TStringList;
begin
  SL := TStringList.Create; 

  with SL do begin ... end;
  
  SL.Free;
end.

Pascal: TStringList: Delimited Text: Header

Then we can play with delimited text, in the program main point.

  with SL do begin
    // If the stringlist is not sorted,
    // the Duplicates setting is ignored. 
    Duplicates := dupIgnore;
    Sorted := true;

    Delimiter := ':';
    DelimitedText := 'rock:jazz:rock:pop:pop';

    Delimiter := '|';
    WriteLn(DelimitedText);
  end;

With the result similar as below:

❯ ./09-tstringlist-a
jazz|pop|rock

Pascal: TStringList: Delimited Text: Main

Mimic Join

We can conquer the limitation, by using old tricks. The string replacement.

First, prepare the header. The skeleton is as below:

uses Classes, SysUtils;

var
  SL : TStringList;
  SR : String;
begin
  SL := TStringList.Create; 

  with SL do begin ... end;
  
  SL.Free;
end.

Pascal: TStringList: Delimited Text: Header

Then we can play with delimited text as usual.

  with SL do begin
    Duplicates := dupIgnore;
    Sorted := true;

    Delimiter := ':';
    DelimitedText := 'rock:jazz:rock:pop:pop';

    Delimiter := '|';
    WriteLn(DelimitedText);
    ...
  end;

And replace the string later.

  with SL do begin
    ...
    LineBreak := ', ';
    WriteLn(Text);

    SR := StringReplace(
      DelimitedText, '|', ', ',
      [rfReplaceAll, rfIgnoreCase]);
    WriteLn(SR);
  end;

With the result similar as below:

❯ ./09-tstringlist-b
jazz|pop|rock
jazz, pop, rock, 
jazz, pop, rock

Pascal: TStringList: Delimited Text: Main

You can see the latest line result is, very similar to join function.


7: Exclude

Imperative Filter

Instead of using object oriented, we can play with oldschool algorithm.

We can learn further using recursion, to get unique tags. In order to do this, we have to create custom exclude function. This function is usually one liner in functional programming, or even lambda.

Oneliner is not a goal in Pascal. We have to make our own custom exclude function, with help of standard library.

Custom Exclude Function: Using Delete

Consider this approach: deleting elements, from an array.

As usual, prepare the header. Now it is very simple: only uses, type and const.

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

type TTags = array of string;
const Tags : TTags =
  ('rock', 'jazz', 'rock', 'pop', 'pop');

Pascal: Exclude Function: Header

However, here below is our function.

function Exclude(
  const Value: string; Elements: TTags): TTags;
var I  : integer;
begin
  I := High(Elements);
  repeat
    if Elements[I] = Value then
      Delete(Elements, I, 1);
    Dec(i);
  until i < 0;

  Result := Elements;
end;

Pascal: Exclude Function: Delete

Then we can play with custom exclude function, in the program main point.

var Xcld: TTags;
begin
  Xcld := Exclude('rock', Tags);
  WriteLn(string.Join(', ', Xcld));
end.

With the result similar as below:

❯ ./11-exclude-a
jazz, pop, pop

Pascal: Exclude Function: Main

Custom Exclude Function: Using Insert

Rebuilding Array

There is another approach: by rebuilding array, except the excluded value.

There is no change, in the header. So we can ago straight examining the alternative function.

However, here below is our function.

function Exclude(
  const Value: string; Elements: TTags): TTags;
var Element : string;
begin
  Result := [];
  for Element in Elements do
    if Value <> Element then
      Insert(Element, Result, High(Result)+1);
end;

Pascal: Exclude Function: Insert

There is no change, in the program main point. And the result is exactly the same as above:

❯ ./11-exclude-b
jazz, pop, pop

8: Recursive Unique

Finaly we can have our recursive function.

Getting Head and Tail.

There is also no fancy syntax, to get head and tail of and array.

But actually it is pretty easy to do it in Pascal.

    Head := Elements[0];
    Tail := Elements;
    Delete(Tail, 0, 1);

The Complete Function

I have explain about the algorithm used in this function too many times, in other language, in other article So I won’t repeat myself.

function Unique(Elements: TTags): TTags;
var
  Xcld, Tail: TTags;
  Head: string;
begin
  if length(Elements) <= 1 then
    Result := Elements
  else
  begin
    Head := Elements[0];
    Tail := Elements;
    Delete(Tail, 0, 1);

    Xcld := Exclude(Head, Tail);
    Result := Unique(Xcld);
    Insert(Head, Result, 0);
  end
end;

Pascal: Unique Function: Recursive

Then we can call our custom unique function, in the program main point.

var
  Uniq: TTags;
begin
  Uniq := Unique(Tags);
  WriteLn(string.Join(', ', Uniq));
end.

With the result similar as below:

❯ ./12-unique-a
rock, jazz, pop

Pascal: Unique Function: Main

Applying to Songs Records

We can test our function above for broader situation. Do not forget to include the uses unit.

uses SysUtils, MySongs;

If you want, you can reduce the line in the function. although you will loss code readibility.

function Unique(Elements: TTags): TTags;
var Head: string;
begin
  if length(Elements) <= 1 then
    Result := Elements
  else
  begin
    Head := Elements[0];
    Delete(Elements, 0, 1); // tail

    Result := Unique(Exclude(Head, Elements));
    insert(Head, Result, 0);
  end
end;

Pascal: Unique Function: Recursive

And finally apply to the songs records.

var
  Song  : TSong;
  Tags  : TTags = ();
begin
  for Song in Songs do
    Insert(Song.Tags, Tags, High(Tags)+1);

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

With the result similar as below:

❯ ./12-unique-b
60s, jazz, rock, 70s, pop

Pascal: Unique Function: Main


What is Next 🤔?

We are going to embrace OOP approach, with a free pascal dialect, called object pascal.

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