Using Name/Value Pairs : Part One
by Erick Engelke
September 30, 2024
There are many situations where you wish to associate a name/value pair using a TStringList.
eg. list['Erick'] := 'programmer'; list['Joe'] := 'accountant';
TStringList stores this as a list of strings like: list[0] := 'Erick=programmer'; list[1] := 'Joe=accountant';
where the equals sign in the string is the default value of list.NameValueSeparator.
You can replace the NameValueSeparator, which you must do if you want to include equals signs in your Name strings.
So there are some questions I wondered:
-
How scalable is this for adding or searching name/value pairs. Does it slow down horribly.
-
Does changing the tstringlist to sorted help, because then it could theoretically use a binary search. How does that affect addition and searching times.
I used a timer function which measures time in milliseconds, and added an arbitrary number of elements (defined as many=5000). More than that number causes page to start to timeout.
My program times:
-
adding one element
-
adding 5000 more
-
finding the 50th value
-
finding the 5001th value
-
replacing the 5000 value (which involves looking up the previous value and replacing it)
Here are the results running on Chrome on a Mac with an M1 processor. All values should be roughly accurate give or take a millisecond (0.001s).
Operation | Unsorted (s) | Sorted (s) |
---|---|---|
Adding one: |
0.001 |
0 |
Adding 5000: |
5.935 |
105.417 |
Adding one again (at end of list): |
0 |
0.034 |
Finding 50th value: |
0.001 |
0.002 |
Finding Many’th value: |
0.001 |
0.003 |
Replace Many’th value: |
0.002 |
0.053 |
Adding 5,000 elements took 5.9 seconds unsorted, or 105 seconds when sorted. Adding to a sorted list requires finding and inserting, so it’s more compute intensive.
Clearly sorting doesn’t help and actually slows things down with this implementation. And looking at the source code, it makes sense because the value[] strategy works by scanning from the start of the list to either the end or stopping at the found value linearly.
From a computer science standpoint, this sounds horribly unscalable, and it could be a problem for some situations. But in reality, for lists of a few thousand numbers, it’s not terrible in most cases, taking only 0.001 seconds to find the 5001th value.
One could implement a faster search/replace function, and may need to if doing, say, 5,000 or more elements. But for most typical browser purposes, this is acceptable.
unit arr;
interface
uses Webdom, WebCore, WebUI, WebForms, WebCtrls, WebBtns, WebEdits;
const
Many = 5000;
type
TForm1 = class(TForm)
Button1: TButton;
MultiLineEdit1: TMultiLineEdit;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
sl : TStringList;
procedure AddOne;
procedure AddMany;
procedure Find50thValue;
procedure FindManyValue;
procedure ReplaceManyValue;
function OutputTimeThis( p : tprocobj ; msg : string ):double;
public
{ Public declarations }
end;
tprocobj = procedure of object;
var
Form1: TForm1;
implementation
procedure TForm1.AddOne;
begin
sl.Values[ 'one' ] := 'was one';
end;
procedure TForm1.AddMany;
var
name : string;
i : integer;
begin
for i := 1 to Many do begin
name := IntToStr( i );
sl.Values[ name ] := '1 to many...value=' + name;
end;
end;
procedure TForm1.ReplaceManyValue;
var
name : string;
begin
name := IntTOStr( many );
sl.Values[ name ] := 'replaced value';
end;
procedure TForm1.Find50thValue;
var
s : string;
begin
s := sl.values[ '50' ];
MultiLineEdit1.Lines.Add('Find50thValue got ' + s );
end;
procedure TForm1.FindManyValue;
var
s : string;
begin
s := sl.values[ IntToStr( many ) ];
MultiLineEdit1.Lines.Add('FindManythValue got ' + s );
end;
procedure Tform1.OutputTimeThis( p : tprocobj ; msg : string );
var
timer : DateTime;
seconds : double;
begin
timer := now;
p; // execute the procedure
seconds := (Integer(now) - Integer(timer)) / 1000;
if msg <> '' then
MultiLineEdit1.Lines.Add( msg + ': ' + FloatToStr( seconds ));
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
sl := TStringList.Create;
sl.sorted := False;
MultiLineEdit1.Lines.Add('==========================');
MultiLineEdit1.Lines.Add('Unsorted');
OutputTimeThis( AddOne , 'Adding one');
OutputTimeThis( AddMany , 'Adding ' + IntToStr( many ));
OutputTimeThis( AddOne , 'Adding one again (at end of list)');
OutputTimeThis( Find50thValue, 'Finding 50th value');
OutputTimeThis( FindManyValue, 'Finding Many''th value');
OutputTimeThis( ReplaceManyValue, 'Replace Many''th value');
sl.Free;
sl := TStringList.Create;
sl.Sorted := True;
MultiLineEdit1.Lines.Add('==========================');
MultiLineEdit1.Lines.Add('Sorted');
OutputTimeThis( AddOne , 'Adding one');
OutputTimeThis( AddMany , 'Adding ' + IntToStr( many ));
OutputTimeThis( AddOne , 'Adding one again (at end of list)');
OutputTimeThis( Find50thValue, 'Finding 50th value');
OutputTimeThis( FindManyValue, 'Finding Many''th value');
OutputTimeThis( ReplaceManyValue, 'Replace Many''th value');
end;
end.