Recent

Author Topic: Portion of surname in DBF  (Read 817 times)

Petrus Vorster

  • Jr. Member
  • **
  • Posts: 82
Portion of surname in DBF
« on: August 30, 2024, 01:53:39 pm »
Greetings all

I have a simple DBF file with a a normal filter search.
The fields are 'CellNumber', 'Surname', 'Initials' etc.

The normal search with a filter is easy.
E.g. Johnson finds that surname etc.

But there are these enormous long Dutch Surnames here e.g :'Jansen van Rensburg' and I would like to find surnames on a 'portion of the surname'.
Using simple data controls, how do I filter using a portion of the surname?

-regards
Peter


Zvoni

  • Hero Member
  • *****
  • Posts: 2741
Re: Portion of surname in DBF
« Reply #1 on: August 30, 2024, 01:56:39 pm »
Everything untested!

TDataset.Locate --> https://www.freepascal.org/docs-html/current/fcl/db/tdataset.locate.html
TLocateOption --> https://www.freepascal.org/docs-html/current/fcl/db/tlocateoption.html

Quote
TLocateOption is used in the TDataset.Locate call to enumerate the possible options available when locating a record in the dataset.

For string-type fields, this option indicates that fields starting with the search value are considered a match.

TDataset.Filter --> https://www.freepascal.org/docs-html/current/fcl/db/tdataset.filter.html
Quote
In general, the filter property accepts a SQL-like syntax usually encountered in the WHERE clause of an SQL SELECT statement.
Code: Pascal  [Select][+][-]
  1. MyDBFDataSet.Filter:='SurName LIKE ''%Rensburg%'';
« Last Edit: August 30, 2024, 02:00:38 pm by Zvoni »
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

Petrus Vorster

  • Jr. Member
  • **
  • Posts: 82
Re: Portion of surname in DBF
« Reply #2 on: August 30, 2024, 02:20:53 pm »
DANG!
That's the thing i have been looking for!

Thank you!

-Peter

Thaddy

  • Hero Member
  • *****
  • Posts: 16167
  • Censorship about opinions does not belong here.
Re: Portion of surname in DBF
« Reply #3 on: August 30, 2024, 02:31:00 pm »
Also note Dutch nobility usually leave out their often long surnames in daily life:
Van Oldeneel tot Oldenzeel: van Oldeneel.
Van Nispen to Sevenaer: van Nispen.
Van Nispen tot Nispen: van Nispen.
Tissot van Patot: Tissot.

All these names have even longer surnames, which are not even used at all.
Meaning that if you store the names like that you would probably be incomplete anyway.
« Last Edit: August 30, 2024, 02:37:21 pm by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

Zvoni

  • Hero Member
  • *****
  • Posts: 2741
Re: Portion of surname in DBF
« Reply #4 on: August 30, 2024, 02:43:06 pm »
DANG!
That's the thing i have been looking for!

Thank you!

-Peter
Take also note of FilterOptions —> https://www.freepascal.org/docs-html/current/fcl/db/tfilteroptions.html
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

Thaddy

  • Hero Member
  • *****
  • Posts: 16167
  • Censorship about opinions does not belong here.
Re: Portion of surname in DBF
« Reply #5 on: August 30, 2024, 03:12:30 pm »
Usually you do not misuse a visual component for such things.
Here is a very old unit that can help you do all kinds of things on a set of strings, mimics unix sort, written by Mike Heydon. It is not fast, but good:
Code: Pascal  [Select][+][-]
  1. unit SuperSort;
  2.  
  3. {This class allows sorting of TString objects with extra functionality
  4. ala UNIX style parameters.
  5. (Yes I know UNIX is a four letter word but they do have some neat features).
  6. The SORT algorithm utilizes the QUICK SORT method. The features I have implemented are
  7.  
  8. Options:
  9.  
  10. Sort descending – srtDescending
  11. Treat sort field as numeric – srtEvalNumeric
  12. Ignore leading blanks in field – srtIgnoreBlank
  13. Ignore case of field – srtIgnoreCase
  14. Switches:
  15.  
  16. -k – Start,End position of substring for search
  17. -f – Field number of a delimited string (Zero column based)
  18. -d – Character delimiter for -f switch (Default = SPACE)
  19. In it's simplest form the class just sorts the TStrings ascending e.g.
  20.  
  21. SuperSort.SortStrings(Memo1.Lines,[]);
  22. Assume a semi-colon delimited list like:
  23.  
  24. 'Mike;34;Green'
  25. 'harry;25;Red'
  26. 'Jackie;6;Black'
  27. 'Bazil;9,Pink'
  28. 'john;52;Blue'
  29. To sort this list DESCENDING on AGE (Field 1) and ignore case:
  30.  
  31. SuperSort(MyStrings, ['-f 1','-d ;'],
  32.   [srtDescending,srtEvalNumeric,srtIgnoreCase]);
  33. Assume a string list of:
  34.  
  35. '1999 12 20 AA432 Comment 1'
  36. '2002 10 12 SWA12 Some other words'
  37. '1998 09 11 BDS65 And so on and so on'
  38. To sort this list on ITEM CODE (Positions 12 to 17) with no options
  39.  
  40. SuperSort(MyStrings,['-k 12,17']);
  41. Methods:
  42.  
  43. procedure SortStrings(StringList: TStrings; Switches: array of string;
  44.   Options: TSuperSortOptionSet = []);
  45. Switches is a string array of -k,-d and -f settings. If it is set to empty array [] then NO switches are active. Options is an OPTIONAL set of [srtDescending,srtIgnoreCase,srtIgnoreBlank,srtEvalNumeric] The default is empty set [].
  46.  
  47. Properties:
  48.  
  49. SortTime : TDateTime;
  50. Returns the time taken for the sort for stats purposes.
  51.  
  52. Usage Example:
  53.  
  54. uses
  55.   SuperSort;
  56.  
  57. procedure TForm1.Test;
  58. var
  59.   Srt: TSuperSort;
  60. begin
  61.   Srt := TSuperSort.Create;
  62.   Srt.SortStrings(Memo1.Lines,[],[srtIgnoreBlank]);
  63.   Label1.Caption := 'Time : ' + FormatDateTine('hh:nn:ss:zzz',Srt.SortTime);
  64.   Srt.Free;
  65. end;
  66. }
  67. interface
  68. {$ifdef fpc}{$mode delphi}{$endif}{$H+}
  69. uses
  70.   Classes,SysUtils;
  71.  
  72. // ==========================================================================
  73. // Class TSuperSort
  74. // Mike Heydon Nov 2002
  75. //
  76. // Sort class that implements Unix style sorts including ..
  77. //
  78. // SWITCHES
  79. // --------
  80. // -k [StartPos,EndPos]  - Keyfield to sort on. Start and End pos in string
  81. // -d [Field Delimiter]  - Delimter to use with -f switch. default = SPACE
  82. // -f [FieldNumber]      - Zero based field number delimeted by -d
  83. //
  84. // OPTIONS SET
  85. // ============
  86. // srtDescending         - Sort descending
  87. // srtIgnoreCase         - Ignore case when sorting
  88. // srtIgnoreBlank        - Ignore leading blanks
  89. // srtEvalNumeric        - Treat sort items as NUMERIC
  90. //
  91. // ==========================================================================
  92.  
  93. type
  94.   // Sort Options
  95.   TSuperSortOptions = (
  96.     srtDescending,srtIgnoreCase, srtIgnoreBlank,srtEvalNumeric
  97.   );
  98.   TSuperSortOptionSet = set of TSuperSortOptions;
  99.  
  100.   // ============
  101.   // TSuperSort
  102.   // ============
  103.   TSuperSort = class(TObject)
  104.   private
  105.     FSortTime : TDateTime;
  106.     FIsSwitches,
  107.     FIsPositional,
  108.     FIsDelimited,
  109.     FDescending,
  110.     FIgnoreCase,
  111.     FIgnoreBlank,
  112.     FEvalDateTime,
  113.     FEvalNumeric : boolean;
  114.     FFieldNum,
  115.     FStartPos,FEndPos : integer;
  116.     FDelimiter : char;
  117.   protected
  118.     function GetKeyString(const Line : string) : string;
  119.     procedure QuickSortStrA(SL : TStrings);
  120.     procedure QuickSortStrD(SL : TStrings);
  121.     procedure ResolveSwitches(Switches : array of string);
  122.   public
  123.     procedure SortStrings(StringList : TStrings; Switches : array of string;
  124.       Options : TSuperSortOptionSet = []);
  125.     property SortTime : TDateTime read FSortTime;
  126.   end;
  127.  
  128. // --------------------------------------------------------------------------
  129. implementation
  130.  
  131. const
  132.   BLANK    = -1;
  133.   EMPTYSTR = '';
  134.  
  135. // ================================================
  136. // INTERNAL CALL
  137. // Resolve switches and set internal variables
  138. // ================================================
  139.  
  140. procedure TSuperSort.ResolveSwitches(Switches : array of string);
  141. var
  142.   i : integer;
  143.   Sw,Data : string;
  144. begin
  145.   FStartPos := BLANK;
  146.   FEndPos := BLANK;
  147.   FFieldNum := BLANK;
  148.   FDelimiter := ' ';
  149.   FIsPositional := false;
  150.   FIsDelimited := false;
  151.  
  152.   for i := Low(Switches) to High(Switches) do
  153.   begin
  154.     Sw := trim(Switches[i]);
  155.     Data := trim(copy(Sw,3,1024));
  156.     Sw := UpperCase(copy(Sw,1,2));
  157.  
  158.     // Delimiter
  159.     if Sw = '-D' then
  160.     begin
  161.       if length(Data) > 0 then FDelimiter := Data[1];
  162.     end;
  163.  
  164.     // Field Number
  165.     if Sw = '-F' then
  166.     begin
  167.       FIsSwitches := true;
  168.       FIsDelimited := true;
  169.       FFieldNum := StrToIntDef(Data,BLANK);
  170.       Assert(FFieldNum <> BLANK,'Invalid -f Switch');
  171.     end;
  172.  
  173.     // Positional Key
  174.     if Sw = '-K' then
  175.     begin
  176.       FIsSwitches := true;
  177.       FIsPositional := true;
  178.       FStartPos := StrToIntDef(trim(copy(Data,1,pos(',',Data) - 1)),BLANK);
  179.       FEndPos := StrToIntDef(trim(copy(Data,pos(',',Data) + 1,1024)),BLANK);
  180.       Assert((FStartPos <> BLANK) and (FEndPos <> Blank),'Invalid -k Switch');
  181.     end;
  182.   end;
  183. end;
  184.  
  185. // ====================================================
  186. // INTERNAL CALL
  187. // Resolve the Sort Key part of the string based on
  188. // the Switches parameters
  189. // ====================================================
  190.  
  191. function TSuperSort.GetKeyString(const Line : string) : string;
  192. var
  193.   Key : string;
  194.   Numvar : double;
  195.   DCount, i, DPos : integer;
  196.   Tmp : string;
  197. begin
  198.   // Default
  199.   Key := Line;
  200.   // Extract Key from switches -k takes precedence
  201.   if FIsPositional then
  202.     Key := copy(Key,FStartPos,FEndPos)
  203.   else
  204.     if FIsDelimited then
  205.     begin
  206.       DPos := 0;
  207.       DCount := 0;
  208.       for i := 1 to length(Key) do
  209.       begin
  210.         if Key[i] = FDelimiter then
  211.          inc(DCount);
  212.         if DCount = FFieldNum then
  213.         begin
  214.            if FFieldNum = 0 then
  215.              DPos := 1
  216.            else
  217.              DPos := i + 1;
  218.           break;
  219.         end;
  220.       end;
  221.  
  222.       if DCount < FFieldNum then
  223.         // No such Field Number
  224.         Key := EMPTYSTR
  225.       else
  226.       begin
  227.         Tmp := copy(Key,DPos,4096);
  228.         DPos := pos(FDelimiter,Tmp);
  229.         if DPos = 0 then
  230.           Key := Tmp
  231.         else
  232.           Key := copy(Tmp,1,DPos - 1);
  233.        end;
  234.     end;
  235.  
  236.   // Resolve Options
  237.   if FEvalNumeric then
  238.   begin
  239.     Key := trim(Key);
  240.     // Strip any commas
  241.     for i := length(Key) downto 1 do
  242.       if Key[i] = ',' then delete(Key,i,1);
  243.     try
  244.       Numvar := StrToFloat(Key);
  245.     except
  246.       Numvar := 0.0;
  247.     end;
  248.     Key := FormatFloat('############0.000000',Numvar);
  249.     // Leftpad num string
  250.     Key := StringOfChar('0',20 - length(Key)) + Key;
  251.   end;
  252.  
  253.   // Ignores N/A for Numeric and DateTime
  254.   if not FEvalNumeric and not FEvalDateTime then
  255.   begin
  256.     if FIgnoreBlank then Key := trim(Key);
  257.     if FIgnoreCase then Key := UpperCase(Key);
  258.   end;
  259.  
  260.   Result := Key;
  261. end;
  262.  
  263. // ==============================================
  264. // INTERNAL CALL
  265. // Recursive STRING quick sort routine ASCENDING.
  266. // ==============================================
  267.  
  268. procedure TSuperSort.QuickSortStrA(SL : TStrings);
  269.  
  270.   procedure Sort(l,r : integer);
  271.   var
  272.     i, j : integer;
  273.     x, Tmp : string;
  274.   begin
  275.     i := l;
  276.     j := r;
  277.     x := GetKeyString(SL[(l + r) div 2]);
  278.  
  279.     repeat
  280.       while GetKeyString(SL[i]) < x do
  281.         inc(i);
  282.       while x  < GetKeyString(SL[j]) do
  283.         dec(j);
  284.       if i <= j then
  285.       begin
  286.         Tmp := SL[j];
  287.         SL[j] := SL[i];
  288.         SL[i] := Tmp;
  289.         inc(i);
  290.         dec(j);
  291.       end;
  292.     until i > j;
  293.  
  294.     if l < j then
  295.       Sort(l,j);
  296.     if i < r then
  297.       Sort(i,r);
  298.   end;
  299.  
  300. begin
  301.   if SL.Count > 0 then
  302.   begin
  303.     SL.BeginUpdate;
  304.     Sort(0,SL.Count - 1);
  305.     SL.EndUpdate;
  306.   end;
  307. end;
  308.  
  309. // ==============================================
  310. // INTERNAL CALL
  311. // Recursive STRING quick sort routine DECENDING
  312. // ==============================================
  313.  
  314. procedure TSuperSort.QuickSortStrD(SL : TStrings);
  315.  
  316.   procedure Sort(l,r : integer);
  317.   var
  318.     i, j : integer;
  319.     x, Tmp : string;
  320.   begin
  321.     i := l;
  322.     j := r;
  323.     x := GetKeyString(SL[(l + r) div 2]);
  324.  
  325.     repeat
  326.       while GetKeyString(SL[i]) > x do
  327.         inc(i);
  328.       while x  > GetKeyString(SL[j]) do
  329.         dec(j);
  330.       if i <= j then  
  331.       begin
  332.         Tmp := SL[j];
  333.         SL[j] := SL[i];
  334.         SL[i] := Tmp;
  335.         inc(i);
  336.         dec(j);
  337.       end;
  338.     until i > j;
  339.  
  340.     if l < j then
  341.       Sort(l,j);
  342.     if i < r then
  343.       Sort(i,r);
  344.   end;
  345.  
  346. begin
  347.   if SL.Count > 0 then
  348.   begin
  349.     SL.BeginUpdate;
  350.     Sort(0,SL.Count - 1);
  351.     SL.EndUpdate;
  352.   end;
  353. end;
  354.  
  355. // ====================
  356. // Sort a stringlist
  357. // ====================
  358.  
  359. procedure TSuperSort.SortStrings(StringList : TStrings;
  360.   Switches : array of string; Options : TSuperSortOptionSet = []);
  361. var
  362.   StartTime : TDateTime;
  363. begin
  364.   StartTime := Now;
  365.   FDescending := (srtDescending in Options);
  366.   FIgnoreCase := (srtIgnoreCase in Options);
  367.   FIgnoreBlank := (srtIgnoreBlank in Options);
  368.   FEvalNumeric := (srtEvalNumeric in Options);
  369.   ResolveSwitches(Switches);
  370.  
  371.   if FDescending then
  372.     QuickSortStrD(StringList)
  373.   else
  374.     QuickSortStrA(StringList);
  375.  
  376.   FSortTime := Now - StartTime;
  377. end;
  378.  
  379. end.
Just Freepascalized it, I did not write it. See notes and comments.
Well worth to add to your goto tools.
It is also quite obvious to use the above with a TStringGrid.
« Last Edit: August 30, 2024, 03:22:43 pm by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

Sieben

  • Sr. Member
  • ****
  • Posts: 365
Re: Portion of surname in DBF
« Reply #6 on: August 30, 2024, 04:17:27 pm »
The syntax of a TDataset.Filter does not really match the SQL syntax. It uses '*' instead of '%' as wildcard and does not use the keyword 'LIKE'.

Here's a working example of how to construct a valid filter in code:

Code: Pascal  [Select][+][-]
  1. MyDataSet.Filter := MyField.FieldName+' = '+QuotedStr('*'+MyFilterString+'*');

where MyFilterString is the portion in question which can for example be obtained by an InputQuery (unit Dialogs).
« Last Edit: August 30, 2024, 11:11:27 pm by Sieben »
Lazarus 2.2.0, FPC 3.2.2, .deb install on Ubuntu Xenial 32 / Gtk2 / Unity7

Petrus Vorster

  • Jr. Member
  • **
  • Posts: 82
Re: Portion of surname in DBF
« Reply #7 on: August 31, 2024, 09:40:04 am »
Works perfectly.

Thank you.

-Peter

 

TinyPortal © 2005-2018