Lazarus

Programming => General => Topic started by: TomTom on March 14, 2018, 11:24:16 am

Title: User tags/masks as index of StringsList
Post by: TomTom on March 14, 2018, 11:24:16 am
I don't know if named my problem correctly in Subject of my topic but here's more detailed explanation of what I want.

So I have StringList with some strings  ;). For example:
Code: Pascal  [Select][+][-]
  1. sl.strings[0]:='Value1';
  2. sl.strings[1]:='Value2';
  3. sl.strings[2]:='Value3';
  4. sl.strings[3]:='Value4';
  5.  

I have 1 Edit field where user is supposed to enter a mask for example:
Code: Pascal  [Select][+][-]
  1. %value0%%value3%%value2%%value1%
  2.  

I have a button and If user presses it then for example in Memo1 a line will be added like this

Code: Pascal  [Select][+][-]
  1. string from sl.Strings[0] +string from sl.Strings[3] +string from sl.Strings[2] +string from sl.Strings[1]

So my program read the index for string from a tag/mask and adds it to memo1.

I'm aware of my problems with explaining what I need in english so.... please ask if You don't understand what I wrote :P.
Title: Re: User tags/masks as index of StringsList
Post by: Thaddy on March 14, 2018, 11:39:50 am
Code: Pascal  [Select][+][-]
  1. {$mode delphi}{$R+}
  2. uses sysutils, classes;
  3. var
  4.   sl:TStringlist;
  5. begin
  6.   sl := TStringlist.Create;
  7.   try
  8.     sl.Add('Value1'); //0
  9.     sl.Add('Value2'); //1
  10.     sl.Add('Value3'); //2
  11.     sl.Add('Value4'); //3
  12.     writeln(sl[0],sl[3],sl[2],sl[1]);
  13.     // or
  14.     writeln(format('%s,%s,%s,%s',[sl[0],sl[3],sl[2],sl[1]]));
  15.   finally
  16.     sl.Free;
  17.   end;
  18. end.
Title: Re: User tags/masks as index of StringsList
Post by: rvk on March 14, 2018, 11:49:38 am
Why not just let the user enter 0;3;2;1
Or do you need other text in there too?

You could also do %0%other text%3%again other text%2% - %1%
In that case you can just look for %index% and replace it with the string index.

There are two ways to tackle this:
1) Loop through all the strings in the TStringList and replace %index% with the string content on that index
2) Extract all %index% from the mask and check if index is only numbers/valid index and replace it with the corresponding string.

Efficiency would depend on how large the TStringList is. If you have thousands of entries in the list you want to go for option 2. If the TStringList is limited option 1 would be easiest to program.

Option 1 would be:
Code: Pascal  [Select][+][-]
  1. Masked := '%0%other text%3%again other text%2% - %1%';
  2. for I := 0 to sl.Count - 1 do
  3.   Masked := StringReplace(Masked, '%' + I.ToString + '%', sl[I], [rfReplaceAll]);
Note: If you have %index% where index doesn't exist in the TStringList, it will not be replaced so you would end up with %index% in the end-result. If that is a problem go with option 2.

@Thaddy, how is that a dynamic solution for when the user enters something else in a different order in the masked-edit?

I have 1 Edit field where user is supposed to enter a mask for example:
...
I have a button and If user presses it then for example in Memo1 a line will be added like this
Title: Re: User tags/masks as index of StringsList
Post by: Thaddy on March 14, 2018, 11:53:05 am
@Rik I simply kept his/her order...
Another possibility is something like this, btw (trunk):
Code: Pascal  [Select][+][-]
  1. {$mode delphi}{$R+}
  2. uses sysutils;
  3. var
  4.   sl:Tarray<String>;
  5. begin
  6.   sl := ['Value1','Value2','Value3','Value4'];
  7.   writeln(sl[0],sl[3],sl[2],sl[1]);
  8.   // or
  9.   writeln(format('%s,%s,%s,%s',[sl[0],sl[3],sl[2],sl[1]]));
  10. end.

Then there is also our templates unit  O:-) O:-) You and me probably know 100+ different ways to solve this, but pseudo code is open to interpretation.
Title: Re: User tags/masks as index of StringsList
Post by: rvk on March 14, 2018, 11:55:38 am
@Rik I simply kept his/her order...
The point is that the order (and input) is given by a random user and would be different each time. You can't expect it to be the same order as given by the example.

I have 1 Edit field where user is supposed to enter a mask for example:
...
I have a button and If user presses it then for example in Memo1 a line will be added like this

So you should read that order from the input given by a user.
Title: Re: User tags/masks as index of StringsList
Post by: Thaddy on March 14, 2018, 11:57:55 am
Well. That's the easy part...
Title: Re: User tags/masks as index of StringsList
Post by: TomTom on March 14, 2018, 12:30:32 pm
Well, thank You for Your answer :) But I don't think this is what I wanted to achieve

I'll try to explain it simpler

I have a stringlist
Code: Pascal  [Select][+][-]
  1. sl.strings[0]:='TOMEK';
  2. sl.strings[1]:='DOM';
  3. sl.strings[2]:='1293;
  4.  

User writes a pattern in the Edit field for example
Code: Pascal  [Select][+][-]
  1. %index2%%index0%%index1%
  2.  
Or user can enter any combination he want. 
for example
Code: Pascal  [Select][+][-]
  1. %index2%%index0%%index1%%index2%%index2%%index1%
  2.  
I don't know what pattern user want's. Depending on what user pattern is, the proper output string is created by getting string from corresponding StringList index and added to Memo.

For example:

My Strings list looks like the above one
User enters a pattern
Code: Pascal  [Select][+][-]
  1. %index2%%index0%%index1%%index2%%index1%

into Edit1

If button is clicked then this line is added to Memo1:
Code: Pascal  [Select][+][-]
  1. 1293TOMEKDOM1293DOM

After that user decides that he want to change his pattern and enters something like this into Edit1
Code: Pascal  [Select][+][-]
  1. %index0%%index0%%index1%%index0%%index2%%index2%%index1%

So the output would be:
 
Code: Pascal  [Select][+][-]
  1. TOMEKTOMEKDOM19231923DOM

[EDIT]:
Yes I would like to let user input something like this

Code: Pascal  [Select][+][-]
  1. %index0%_%index0%_%index1%_%index0%

so the ouput string would like like this in my example
Code: Pascal  [Select][+][-]
  1. TOMEK_TOMEK_DOM_TOMEK


Title: Re: User tags/masks as index of StringsList
Post by: rvk on March 14, 2018, 12:33:56 pm
so the ouput string would like like this in my example
Code: Pascal  [Select][+][-]
  1. TOMEK_TOMEK_DOM_TOMEK
Option 1 mentioned in my answer will do exactly what you want.

If you really want index before 1, 2 etc then you can change this
'%' + I.ToString + '%'
into this
'%index' + I.ToString + '%'
Title: Re: User tags/masks as index of StringsList
Post by: TomTom on March 14, 2018, 12:43:39 pm
so the ouput string would like like this in my example
Code: Pascal  [Select][+][-]
  1. TOMEK_TOMEK_DOM_TOMEK
Option 1 mentioned in my answer will do exactly what you want.

If you really want index before 1, 2 etc then you can change this
'%' + I.ToString + '%'
into this
'%index' + I.ToString + '%'


I was trying to write my answer so long so You guys replied few times already :P
Yes, thank You I'm looking into it right now :)
Title: Re: User tags/masks as index of StringsList
Post by: taazz on March 14, 2018, 01:17:27 pm
Code: Pascal  [Select][+][-]
  1. uses sysutils;
  2. begin
  3.   writeln(format('%0:S %1:S %0:S, %3:S, %2:S, %1:S, %S, %0:S',['1','2','3','4']));
  4. end.
  5.  
expected output
Code: [Select]
1 2 1 4 3 2 3 1
Title: Re: User tags/masks as index of StringsList
Post by: TomTom on March 14, 2018, 01:34:52 pm
RVK Solution works like a charm :) Now all I need to do is to reverse my string list :)
Title: Re: User tags/masks as index of StringsList
Post by: SymbolicFrank on March 14, 2018, 01:50:28 pm
You might want to look at TStringList.CommaText.
Title: Re: User tags/masks as index of StringsList
Post by: TomTom on March 14, 2018, 09:10:08 pm
I solved this by using multiple times ReplaceString function for each parameter I want the user to be able to use... I don't know if it's good but it works :P



Quote
Ok. So I managed to create program which can rename files using the folder structure with possibility to mix them in the way that user wants... For example:
Code: Pascal  [Select][+][-]
  1. C:\MainFolder\30\0\23\943\1\TextFileA.txt
  2. C:\MainFolder\30\0\23\943\1\TextFile033.txt
  3. C:\MainFolder\30\0\23\943\1\TextFile03a.txt
  4. C:\MainFolder\30\0\23\943\1\TextFile0341.txt
  5. C:\MainFolder\35\123\23\943\1\TextFileB.txt
  6. C:\MainFolder\35\123\23\943\1\TextFileGF.txt
  7. C:\MainFolder\35\123\23\943\1\TextFileBK.txt
  8. C:\MainFolder\35\123\23\943\1\TextFileOD.txt
  9.  
So after running those files through my program  with this patter %folder5%_%folder4%_%folder3%_%folder2%_%folder1%, the files will be named like this:
Code: Pascal  [Select][+][-]
  1. C:\MainFolder\30\0\23\943\1\30_0_23_943_1_0001.txt
  2. C:\MainFolder\30\0\23\943\1\30_0_23_943_1_0002.txt
  3. C:\MainFolder\30\0\23\943\1\30_0_23_943_1_0003.txt.
  4. C:\MainFolder\30\0\23\943\1\30_0_23_943_1_0004.txt
  5.  
  6. C:\MainFolder\35\123\23\943\1\35_123_23_943_1_0001.txt
  7. C:\MainFolder\35\123\23\943\1\35_123_23_943_1_0002.txt
  8. C:\MainFolder\35\123\23\943\1\35_123_23_943_1_0003.txt
  9. C:\MainFolder\35\123\23\943\1\35_123_23_943_1_0004.txt

number at the end of the file, for now is hard coded and will be always added.
I would like to give user a possibility to decide if he wants this counter and WHERE he wants it (ofcourse he wants it :P! otherwise files will be overwritten :P). But hey.. I want to do something like this.

User enters a pattern like this:
Code: Pascal  [Select][+][-]
  1. %folder5%_%folder4%_%folder3%_%folder2%_%folder1%_%COUNTER%

Now I'm using solution that RVK provided and its good. But it uses StringReplace. So how can I add more masks to Pattern? For example %COUNTER% to add counter or %EXT% to add extension of the file.
 
[/s]
Title: Re: User tags/masks as index of StringsList
Post by: jamie on March 14, 2018, 10:22:59 pm
 What you are doing is simply putting in a variable name that will translate to a index number of course. 
 
  and to do so effectively you should write  parser for it that toggles a Boolean type to indicate if you
are between a pair of &...& and if so, accumulate a String from that. each time you hit a "&" you
toggle the Toggle Flag..
 
 If inputChar = '&' Then ToggleFlag := Not ToggleFlag;

 Then while you are concating the main string, you test for this flag..

 if flag is TRUE then you are concating a search string, if not, then you are concating the
main string..
 
 each time you toggle this flag, you need to test it, if its false after a toggle this means you just
finished a build of a search string, in which case you now use that results to search the list..

etc..

 if you need an example I can provide one..

       
       

 
Title: Re: User tags/masks as index of StringsList
Post by: TomTom on March 14, 2018, 10:44:12 pm
Maybe it would be easier for me to understand when You show me example. But either way My program works quite fast and without problems and as intended,even on large number of files (3000+) :). But please do show me example :) Maybe I could put Your solution in future version :) I'm always eager to learn new stuff :)
 
What you are doing is simply putting in a variable name that will translate to a index number of course. 
 
  and to do so effectively you should write  parser for it that toggles a Boolean type to indicate if you
are between a pair of &...& and if so, accumulate a String from that. each time you hit a "&" you
toggle the Toggle Flag..
 
 If inputChar = '&' Then ToggleFlag := Not ToggleFlag;

 Then while you are concating the main string, you test for this flag..

 if flag is TRUE then you are concating a search string, if not, then you are concating the
main string..
 
 each time you toggle this flag, you need to test it, if its false after a toggle this means you just
finished a build of a search string, in which case you now use that results to search the list..

etc..

 if you need an example I can provide one..

       
       
Title: Re: User tags/masks as index of StringsList
Post by: taazz on March 14, 2018, 10:49:32 pm
here is a quick and dirty example
Code: Pascal  [Select][+][-]
  1. type
  2.   TConstArray        = array of TVarRec;
  3.  
  4. function ToArrayOfConst(aList:TStringList):TConstArray;
  5. begin
  6.   SetLength(Result,aList.Count);
  7.   for vCntr := 0 to aList.Count-1 do begin
  8.     Result[vCntr].VType := vtAnsiString;
  9.     Result[vCntr].VAnsiString := @aList[vCntr];
  10.   end;
  11. end;
  12.  
  13. function FormatString(const aFormat:string; aList:TStringList);
  14. var
  15.   vValues:TConstArray;
  16. begin
  17.   vValues := ToArrayOfConst(aList);
  18.   Result  := Format(aFormat, vValues);
  19.   SetLength(vValues, 0);
  20. end;
  21.  
Example of use.
your users set their string in the ini as %0:S %1:S %2:S, %0:S, %1:S and then
Code: Pascal  [Select][+][-]
  1. var
  2.   MValueList :TStringList;
  3. begin
  4.   MyValueList := TStringList.Create;
  5.   try
  6.     MyValueList.Add('Value 1');
  7.     MyValueList.Add('Value 2');
  8.     MyValueList.Add('Value 3');
  9.     MyValueList.Add('Value 4');
  10.     MyValueList.Add('Value 5');
  11.     MyValueList.Add('Value 6');
  12.     MyValueList.Add('Value 7');
  13.     MyValueList.Add('Value 8');
  14.     MyValueList.Add('Value 9');
  15.  
  16.     WriteLn(FormatString(<ReadFormatFromIni>, MyValueList));
  17.   finally
  18.     myvaluelist.free;
  19.   end;
  20.  
<ReadFormatFromIni> can be any process you see fit.

Let me repeat my self. Everything has been typed directly in the browser. You need to test it to make sure it works its just a skeleton to get you going.
Title: Re: User tags/masks as index of StringsList
Post by: jamie on March 14, 2018, 11:36:29 pm
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Button1: TButton;
  16.     Edit1: TEdit;
  17.     procedure Button1Click(Sender: TObject);
  18.   private
  19.  
  20.   public
  21.  
  22.   end;
  23.  
  24. var
  25.   Form1: TForm1;
  26.  
  27. implementation
  28.  
  29. {$R *.lfm}
  30.  
  31. { TForm1 }
  32. Function GetMacroValue (Const S:String):String;
  33. Type  TMyType =
  34.  Record
  35.   TagName:String[10];
  36.   TagValue:String[255];
  37.  End;
  38. Const Myvalues:Array[0..1] of TmyType = ((TagName:'input1';TagValue:'Today is a great day'),
  39.                            (TagName:'input2';TagValue:'Tomorrow maybe better'));
  40. Var
  41.  I:Integer;
  42. begin
  43.   I := 0;
  44.   While (I <= High(MyValues))and(MyValues[I].TagName <> S) Do Inc(I);
  45.   If I <= High(MyValues) Then result := MyValues[I].TagValue Else Result := '{'+S+' :BAD ENTRY}';
  46. end;
  47.  
  48. Function MacroBuildString(Const S:String):String;
  49. Var
  50.   IsMacro:Boolean;
  51.   I:Integer;
  52.   MacroStr :String;
  53. Begin
  54.   IsMacro := False;
  55.   For I := 1 to Length(S) do
  56.    begin
  57.      if S[I] = '&' Then //handle Macros.
  58.       Begin
  59.        IsMacro:= Not IsMacro;
  60.        if IsMacro Then Macrostr := '' Else //If starting new macro the clear previous value;
  61.             result := Result+GetMacroValue(MacroStr); //Attached the results
  62.       end else
  63.      if IsMacro Then MacroStr := MacroStr+S[I] Else //Build mac string
  64.      Result := Result+S[I]; //Build main string outside of macro..
  65.    end;
  66. end;
  67.  
  68. procedure TForm1.Button1Click(Sender: TObject);
  69. begin
  70.   ShowMessage(MacroBuildstring(Edit1.Text));
  71. end;
  72.  
  73. end.
  74.  
  75.  

Run that through, enter %inpu1& along with other text to see what you come up with ..
TinyPortal © 2005-2018