is there a way to implement so called "record" and "field" data structure?

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
nnnmmm
Posts: 148
Joined: 26 Aug 2017 06:11

is there a way to implement so called "record" and "field" data structure?

#1 Post by nnnmmm » 30 Nov 2024 23:10

Code: Select all

@ECHO OFF
Setlocal EnableExtensions DisableDelayedExpansion
SetLocal EnableExtensions  EnableDelayedExpansion
SET CC=0
FOR %%V IN (
"AA 31"  D1
"BB 21"  "D2 2"
"CC 53"  D3
) DO (
   SET /A CC=!CC!+1
   ECHO !CC! "%%~V" 
)
REM PAUSE
:END
****************************************************************
the one above works but
is there a way to implement so called "record" and "field" data structure?
an idea is something like below but with a wrong batch grammar
@ECHO OFF
Setlocal EnableExtensions DisableDelayedExpansion
SetLocal EnableExtensions EnableDelayedExpansion
SET CC=0
FOR %%U %%V IN (
"AA 31" D1
"BB 21" "D2 2"
"CC 53" D3
) DO (
SET /A CC=!CC!+1
ECHO !CC! "%%U" "%%~V"
)
REM PAUSE
:END

miskox
Posts: 632
Joined: 28 Jun 2010 03:46

Re: is there a way to implement so called "record" and "field" data structure?

#2 Post by miskox » 01 Dec 2024 06:56

Why don't you make an example of what your output file should look like? We are all trying to guess.

I guess you are looking for a .txt file (you can call it a .csv file if you want):

Code: Select all

field1;field2;field3:field4;field5;
field1;field2;field3:field4;field5;
field1;field2;field3:field4;field5;
field1;field2;field3:field4;field5;
field1;field2;field3:field4;field5;
You can even have a 'key' in your file (I have lots of experience with indexed files (and COBOL) when I was still using OpenVMS):

Code: Select all

KKKKK;field1;field2;field3:field4;field5;
KKKKK;field1;field2;field3:field4;field5;
KKKKK;field1;field2;field3:field4;field5;
KKKKK;field1;field2;field3:field4;field5;
KKKKK;field1;field2;field3:field4;field5;
In the example above you can use FINDSTR /B /C:"KKKKK;---------YOUR KEY------------;" to find specific records. And when you get them you can process them with a FOR /F loop. (KKKKK can be avoided of course but you could add more info into a file with another special letters (LLLLL, MMMMM, NNNNN...) if 'key' values are the same. There are cases where I store my data in a .cmd itself (and I find this data with FINDSTR /B /C:"KKKKK..." - so I only have to copy one file instead of two (or more).

More info you provide better answers you will get (see viewtopic.php?f=3&t=11287 again).

Saso

T3RRY
Posts: 252
Joined: 06 May 2020 10:14

Re: is there a way to implement so called "record" and "field" data structure?

#3 Post by T3RRY » 01 Dec 2024 10:24

The below is my own implementation of this type of data structure, and includes a usage case example.

Edit: added some more remarks, modified delimiters / field seperators to cope with records containg field values that match fieldIDs within the record.

Code: Select all

@echo off & setlocal enableExtensions enableDelayedexpansion
CLS

(Set \n=^^^

%= do not modify this \n defintion =%)

===========================================================
rem macro usage:
:+ %@lookup% <recordVar> <returnVar> <fieldID> [fieldID]
rem  - returns the field value of the first matched
rem    fieldID in the record.
rem record data structure in format:
rem recordVar=;FieldID:fieldValue;FieldID:fieldValue;
rem  ; = fieldID initiator / field value terminator
rem  : = fieldID:fieldValue delimiter
rem if multiple field identidiers are matched, the return will be returned as a coma seperated list.
rem * NOTE * this macro definition is escaped for a enableDelayedExpansion environment.
Set @lookup=For %%. in (1 2)Do if %%.==2 (%\n%
  Set "#lookup=0"%\n%
  For /f "tokens=1,2,*" %%U in ("^!@.args^!")Do (%\n%
    Set "%%~V="%\n%
    For %%i in (%%W)Do (%\n%
      Set /a "#lookup+=1"%\n%
      If /i not "^!%%~U:;%%~i:=^!" == "^!%%~U^!" (%\n%
        Set "$lookup=^!%%U:*;%%~i:=^!"%\n%
        For /f "tokens=1 Delims=; EOL=" %%R in ("^!$lookup^!; ")Do (%\n%
          If not defined %%~V (%\n%
            Set "$lookup=%%R"%\n%
            Set "%%~V=%%R"%\n%
          ) else (%\n%
            If "^!%%~V^!"=="^!%%~V:%%R=^!" Set "%%~V=^!%%~V^!,%%R"%\n%
  ) ) ) ) )%\n%
)Else Set @.Args=

 Set "weekday.dat=;mon:Monday;tue:Tuesday;wed:Wednesday;thu:Thursday;fri:Friday;sat:Saturday;sun:Sunday;"
 Set "month.dat=;01:January;02:February;03:March;04:April;05:May;06:June;07:July;08:August;09:September;10:October;11:November;12:December;"
 Set "daynumber.dat=;01:1st;02:2nd;03:3rd;04:4th;05:5th;06:6th;07:7th;08:8th;09:9th;10:10th;11:11th;12:12th;13:13th;14:14th;15:15th;16:16th;17:17th;18:18th;19:19th;20:20th;21:21st;22:22nd;23:23rd;24:24th;25:25th;26:26th;27:27th;28:28th;29:29th;30:30th;31:31st;"

rem below usage examples assumes system language is english.
 for /f "usebackq delims=" %%G in (`PowerShell -Command "& {Get-Date -format 'dd/MM/yyyy'}"`) do set "$date=%%G"

 %@lookup% weekday.dat   $weekday   !date:~0,3!
 %@lookup% daynumber.dat $daynumber !$date:~0,2!
 %@lookup% month.dat     $month     !$date:~3,2!

 Echo !$weekday! the !$daynumber! of !$month! !$date:~-4!

Pause
Endlocal & goto:eof
Last edited by T3RRY on 02 Dec 2024 08:13, edited 2 times in total.

nnnmmm
Posts: 148
Joined: 26 Aug 2017 06:11

Re: is there a way to implement so called "record" and "field" data structure?

#4 Post by nnnmmm » 01 Dec 2024 18:26

>More info you provide better answers you will ..

this was what i did currently, i am using it but...
IF "%var1%"=="1" SET "D1=dostips" &SET "D2=%abc@zyx.com%" &SET D3=aaaa
IF "%var1%"=="2" SET "D1=ztree" &SET "D2=%zzz@poi.com%" &SET D3=cccc
IF "%var1%"=="3" SET "D1=youtube" &SET "D2=%txx@123.com%" &SET D3=vvvv

if i decided to alphabetically REORDER or INSERT new lines from above, it would be like below then i would have to change var1's number, and the display lines in ECHO command would also have to be changed
IF "%var1%"=="1" SET "D1=dostips" &SET "D2=%abc@zyx.com%" &SET D3=aaaa
IF "%var1%"=="4" SET "D1=linkbar" &SET "D2=%hhh@ggg.com%" &SET D3=kkkk
IF "%var1l%"=="3" SET "D1=youtube" &SET "D2=%txx@123.com%" &SET D3=vvvv
IF "%var1%"=="2" SET "D1=ztree" &SET "D2=%zzz@poi.com%" &SET D3=cccc

so i wanted to try a new data format that wouldnt need ordering and inserting and deleting as below
FOR %%V IN (
"AA 31" D1 11
"BB 21" "D2 2" 22
"CC 53" D3 33
) DO (

all i need is copy cut paste to REORDER, INSERT and etc
FOR %%V IN (
"BB 21" "D2 2" 22
"AA 31" D1 11
"zz" "D3 2" 44
"CC 53" D3 33

if i choose var1==2 as "record" number 2 then it brings out field21=%%U field22==%%V field23==%%W, was the idea i wanted to try
************************
FOR %%V IN ( .... ) DO ( output is everything one straight down
"AA 31"
D1
"BB 21"
"D2 2"

but if the idea FOR %%V %%U IN ( .... ) DO ( goes the way, the output will be
"AA 31" D1
"BB 21" "D2 2"
***********************************

for T3rry
it may take a while for me to try, my batch command and operator abilities are low
thanks for the time being

nnnmmm
Posts: 148
Joined: 26 Aug 2017 06:11

Re: is there a way to implement so called "record" and "field" data structure?

#5 Post by nnnmmm » 02 Dec 2024 07:36

>You can even have a 'key' in your file
not this way at the moment. keys automatically take whatever the order in which data are wrttien
example)
SetLocal EnableExtensions EnableDelayedExpansion
SET CC=0
FOR %%U IN (
A
E
D
B
) DO (
SET /A CC=!CC!+1
ECHO !CC! "%%~U"
)

!CC! number is the key, probably the simplest or the toughest if i have to deal with data strings with "!" in them
if %var%==27 echo get 27th !CC!'s %%U

and later....choice /C:1234567890abcdefghijklmnopqrstuvwxyz /N /M "Input:"
IF "%ErrorLevel%"=="33" echo get 33th !CC!'s %%U

if there is no easy way,
i will try to use // or ## as a divider to get this way
SET SS=%%U
SET S1=!SS:~3,10!
SET S2=!SS:~13,24!
SET S3=!SS:~30,44!


//"A 1" //"BB" //"2123"
//"AAAAA 1" //"BB BBBB" //"2123 DDDD"


********************
t3rry
i couldnt understand the code
sorry i couldnt put my data in the code you provided.

T3RRY
Posts: 252
Joined: 06 May 2020 10:14

Re: is there a way to implement so called "record" and "field" data structure?

#6 Post by T3RRY » 02 Dec 2024 09:17

For the sort of use cases you've exampled, the following example show how to use a string as the fieldID to extract the relevant field values. In light of the sort of examples you've included, I've incorporated my menu macro into the script to show a method of referencing the values using a related string

Code: Select all

@echo off & CLS

setlocal enableExtensions enableDelayedexpansion
Call:DefMacros

 REM record:field datastructure implementation
 rem          recordName=;FieldVar:"fieldSubVar=value" "fieldSubVar=value";FieldVar:"fieldSubVar=value" "fieldSubVar=value"

 rem example usage:
 Set sites=;dostips:"D.1=abc^^^^^^^!@zyx.com" "D.2=aaaa";youtube:"D.1=txx@123.com" "D.2=vvvv";ztree:"D.1=zzz@poi.com" "D.2=cccc";Linkbar:"D.1=hhh@ggg.com" "D.2=kkkk"

 rem enact menu from fieldIDs within Record variable 'sites'
 %menu% /R sites /Set

 rem display Menu macros return Variables
 Set menu{

 rem display fieldSubVariables assigned from the selected field when /Set used.
 For %%G in (!menu{field}.var!)Do echo(%%G=!%%G!
 Pause
Endlocal & goto:eof

:DefMacros
==================================================== REM = Menu macro Definition BEGIN
REM IMPORTANT - Menu macro is Escaped for definition in an EnableDelayedExpansion Environment.
REM IMPORTANT - RESERVED VARIABLES: Menu* @while $while $while.i \n

(Set \n=^^^

%= do not modify this \n defintion =%)

 Set @while=For %%z in (1 1 16)do if not defined $while
 Set "@while=Set $while=&Set /a $while.i=0 & !@while! !@while! !@while! !@while! Set /a $while.i+=1 &"

  REM ======                Menu macro by T3RRY.                ======
  rem menu usage/s:
:+ %Menu% <option> [option] ["doublequote option with whitespace"]
:+ %Menu% /R recordVarName [/Set]
  rem if /R is present as first option, menu uses the subsequent argument as a record to buid the option list from
  rem /Set : optional 3rd argument : Iterate selected field in the record to assign data
  rem        - requires the record to utilize the following data structure:
  rem          recordName=;FieldVar:"fieldSubVar=value" "fieldSubVar=value";FieldVar:"fieldSubVar=value" "fieldSubVar=value"
  rem        - Note: the number of FieldsubVars is not fixed.

  REM returns:
  REM Menu{String}    : the selected string
  REM Menu{Key}       : the key used to selectthe string
  REM Menu{Number}    : the position of the string in the options list
  REM /R returns:
  REM Menu{record}    : the name of the record supplied as the option list source
  REM Menu{field}     : the field data matching the selected reference option
  REM Menu{field}.var : the field variables present in the referenced field
  REM resources: 
  REM https://www.dostips.com/forum/viewtopic.php?t=9265#p60294
  REM https://www.dostips.com/forum/viewtopic.php?f=3&t=10983&sid=f6937e02068d93bc5a97ef63d4e5319e
  REM Macros with arguments learning resource:
  REM https://www.dostips.com/forum/viewtopic.php?f=3&t=1827

REM - use REM / remove REM on the below line to enable / disable menu dividing line
  REM Goto :NoDividingLine

  REM Get console width for dividing line. Requires cmd.exe conhost. incompatable with Windows Terminal.
    For /f "usebackq tokens=2* delims=: " %%W in (`mode con ^| %__APPDIR__%findstr.exe /LIC:"Columns"`) do Set /A "Console_Width=%%W"
    Set "Menu_Div=" & For /L %%i in (1 1 %Console_Width%)Do Set "Menu_Div=!Menu_Div!-"

:NoDividingLine
  REM Menu internal variables
  REM keymap. translates literal keypress to the numeric position of the item in the menu list
    Set /a Menu@0=36,Menu@1=1,Menu@2=2,Menu@3=3,Menu@4=4,Menu@5=5,Menu@6=6,Menu@7=7,Menu@8=8,Menu@9=9,Menu@a=10,Menu@b=11,Menu@c=12,Menu@d=13,Menu@e=14,Menu@f=15,Menu@g=16,Menu@h=17,Menu@i=18,Menu@j=19,Menu@k=20,Menu@l=21,Menu@m=22,Menu@n=23,Menu@o=24,Menu@p=25,Menu@q=26,Menu@r=27,Menu@s=28,Menu@t=29,Menu@u=30,Menu@v=31,Menu@w=32,Menu@x=33,Menu@y=34,Menu@z=35
  REM Valid choice characters
    Set "Menu.Keys=123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0"

Set Menu=For %%n in (1 2)Do if %%n==2 (%\n%
  If defined Menu{Args} (%\n%
    %= Output Dividing Line                     =% If Defined Menu_Div Echo(^!Menu_Div^!%\n%
    %= Reset Menu.# index for Menu.Item[#]      =% Set "Menu.#=0"%\n%
    %= Undefine choice command key list         =% Set "Menu.Chars="%\n%
    %= Test args for /R mode                    =% For /f "tokens=1,2,3" %%1 in ("^!Menu{Args}^!")Do (%\n%
    %= Case{ /R - build Menu Record Variable }  =%   If /i "%%1" == "/R" (%\n%
    %= Clear prior menu record data             =%     Set "menu{record}="%\n%
                                                       Set "menu.set="%\n%
    %= Case{ /Set - setFlag fieldSubVars }      =%     If /i "%%3" == "/Set" Set "menu.set=1"%\n%
    %= Reset Args; build options via recordVar  =%     Set "Menu{Args}="%\n%
    %= Clone Record [ destructive parsing ]     =%     Set "menu.rec=^!%%2^!"%\n%
    %= Enforce Record Initiator                 =%     If "^!%%2:~0,1^!" == ";" Set "menu.rec=^!%%2:~1^!"%\n%
    %= Enforce Record terminator                =%     If not "^!%%2:~-1^!" == ";" Set "menu.rec=^!menu.rec^!;"%\n%
    %= Case{ Record defined - Parse While }     =%     If defined menu.rec !@while! (%\n%
    %= Split FeildID from FieldValue via Delims =%       If defined menu.rec For /f "EOL=^| tokens=1,2 Delims=;:" %%i in ("^!menu.rec^!") do (%\n%
    %= Append to options menu list              =%         Set "Menu{Args}=^!Menu{Args}^! %%i"%\n%
    %= Case{ FieldIDs.count EQU 36 - EndWhile } =%         If "^!$while.i^!" == "36" Set "$while=stop"%\n%
    %= remove current fieldID fieldValues pair  =%         Set "menu.rec=^!menu.rec:*;=^!"%\n%
    %= Case{ Record undefined - EndWhile }      =%       )Else Set "$while=stop"%\n%
                                                       )%\n%
    %= Return record name                       =%     Set "menu{record}=%%2"%\n%
                                                   ) )%\n%
    %= For Each in list;                        =% If defined Menu{Args} For %%G in (^^^!Menu{Args}^^^!)Do (%\n%
    %= For Menu.Item Index value                =%   For %%i in (^^^!Menu.#^^^!)Do If not %%i GTR 35 (%\n%
    %= Build the Choice key list                =%     Set "Menu.Chars=^!Menu.Chars^!^!Menu.Keys:~%%i,1^!"%\n%
    %= Define Menu.Item array                   =%     Set "Menu.Item[^!Menu.Keys:~%%i,1^!]=%%~G"%\n%
    %= Assign String for safe output            =%     Set "Menu.Output=%%~G"%\n%
    %= Display as [key] Option String           =%     Echo([^^^!Menu.Keys:~%%i,1^^^!] ^^^!Menu.Output^^^!%\n%
    %= Increment Menu.# Index var               =%     Set /A "Menu.#+=1"%\n%
    %= Close Menu.# expansion loop              =%   )%\n%
    %= Close Menu{Args} String loop             =% )%\n%
    %= Output Dividing Line                     =% If Defined Menu_Div Echo(^^^!Menu_Div^^^!%\n%
    %= Select option by character index         =% For /f "delims=" %%o in ('%__APPDIR__%Choice.exe /N /C:^^^!Menu.Chars^^^!')Do For /f "tokens=1,2 delims=;" %%V in ("^!Menu.Item[%%o]^!;^!Menu@%%o^!")Do (%\n%
    %= exit [sub]script w/out modifying option  =%   If /I "%%V" == "Exit" Exit /B 2%\n%
    %= Assign 'Menu{String}' w/literal string   =%   Set "Menu{String}=%%V"%\n%
    %= Assign 'Menu{key}' with key pressed      =%   Set "Menu{Key}=%%o"%\n%
    %= Assign 'Menu{Number} with list position  =%   Set "Menu{Number}=%%~W"%\n%
    %= Case{ /R - extract field from record }   =%   If defined menu{record} (%\n%
                                                       Set "menu{field}="%\n%
                                                       For /f "tokens=1,2,*" %%U in ("^!menu{record}^! menu{field} ^!Menu{String}^!")Do (%\n%
                                                         Set "%%~V="%\n%
                                                         If /i not "^!%%~U:;%%~W:=^!" == "^!%%~U^!" (%\n%
                                                           Set "Menu.lookup=^!%%U:*;%%~W:=^!"%\n%
                                                           For /f "EOL= tokens=1 Delims=;" %%R in ("^!Menu.lookup^!; ")Do (%\n%
                                                             If not defined %%~V (%\n%
                                                               Set "%%~V=%%R"%\n%
                                                             ) else (%\n%
                                                               If "^!%%~V^!"=="^!%%~V:%%R=^!" Set "%%~V=^!%%~V^!,%%R"%\n%
                                                       ) ) ) )%\n%
    %= Case{ fieldSubVar assignment flagged }   =%     if defined menu.set if defined menu{field} (%\n%
                                                         Set "menu{field}.var="%\n%
    %= For each FieldValue in Field             =%       For %%G in (^^^!menu{field}^^^!)Do (%\n%
                                                           For /f "tokens=1 delims=,:;= " %%1 in ("%%~G")do Set "menu{field}.var=^!menu{field}.var^! %%1"%\n%
    %= Return fieldSubVars                      =%         Set "%%~G"%\n%
                                                     ) ) )%\n%
    %= Reset Menu Argument variable             =%   Set "Menu{Args}="%\n%
    %= Close Menu macro processing loops        =% )%\n%
  )%\n%
%= Capture Macro input - Options List       =%)Else Set Menu{Args}=
========================================== REM = Menu Macro Definition END
Goto:Eof
Edit: I've amended the example to include a method of extracting all fieldIDs to generate menu options, to avoid having to
manually update such menus whenever you add a fieldID to the record.
Edit 2: Refactored + Remarks refined.
The previous @lookup and @extractFieldIDs macro's have been built into the Menu macro.
Last edited by T3RRY on 04 Dec 2024 09:32, edited 1 time in total.

miskox
Posts: 632
Joined: 28 Jun 2010 03:46

Re: is there a way to implement so called "record" and "field" data structure?

#7 Post by miskox » 04 Dec 2024 05:01

I am not sure if I understand exactly what are you trying to do. But anyway:

For example: you enter your data (for example with SET /P) and then write it to the output file. This means that you have your input file. Now you enter your 'key' (line number in this input file). Now depends what are you trying to do:

If you want to remove this line (record) from the file you just use FINDSTR /B /V /C:"%key%;" your_file.txt. (key is the line number. Notice the ';' (let's say ';' is the field delimiter in your file)). With this ';' at the end you make sure FINDSTR only finds one record you want (1; for the first record and not records that have 1; in it (1; or 11;)). See FINDSTR's help (/B searches for a string at the beginning of a file, /V outputs lines that do not match (so your line is not in the new output file), /C makes sure your search is not treated as regex).

You can easily use SORT to sort this file (in this case I would recommend that you make sure keys are all of the same length). If you want to change 'key' values (so there are no 'missing lines') then you have to reread the file and create a new one.

Hope this helps.
Saso

nnnmmm
Posts: 148
Joined: 26 Aug 2017 06:11

Re: is there a way to implement so called "record" and "field" data structure?

#8 Post by nnnmmm » 04 Dec 2024 09:36

Code: Select all

@ECHO OFF
SetLocal EnableExtensions EnableDelayedExpansion
SET CC=0
for /f "tokens=1,2,3 delims=\" %%A in (
"dosti\a@z.com\aaa\"
"xtree\b@z.com\bbb\"
"ztree\c@z.com\ccc\"

) do (
  SET /A CC=!CC!+1
  set "L1=%%A"
  set "L2=%%B"
  set "L3=%%C"
  ECHO !CC!.  !L1!
)
rem ------------------------------------
ECHO.
SET /P "VAR=Input: "
ECHO.
rem ------------------------------------

SET CC=0
for /f "tokens=1,2,3 delims=\" %%A in (
"dosti\a@z.com\aaa\"
"xtree\b@z.com\bbb\"
"ztree\c@z.com\ccc\"

) do (
  SET /A CC=!CC!+1
   IF %VAR%==!CC! (
     set "L1=%%A"
     set "L2=%%B"
     set "L3=%%C"
     GOTO :EXIT-FOR
   )  
)

:EXIT-FOR
SetLocal EnableExtensions DisableDelayedExpansion
ECHO %L2% | CLIP
C:\ZTREE\NirCMDC.exe  WAIT 4000
C:\ZTREE\NirCMDC.exe BEEP 500 100
ECHO "%L3%" | CLIP

:END
i kind of got it and the code is kind of working for the only choice number 1, because i can not display these two below due to my lacking in batch commands
"xtree\b@z.com\bbb\"
"ztree\c@z.com\ccc\"

you could understand my code in about 20 seconds and you could know what i wanted to do
if my code all worked, all i need would just feed more of this kind of data inside the FOR () DO
"xtree\b@z.com\bbb\"
"ztree\c@z.com\ccc\"
"nirsoft\d@z.com\fff"

hope to help me.

nnnmmm
Posts: 148
Joined: 26 Aug 2017 06:11

Re: is there a way to implement so called "record" and "field" data structure?

#9 Post by nnnmmm » 04 Dec 2024 09:50

ps

i think there is a bug in FOR /? that says

for /f "eol=; tokens=1,2,3 delims=\" %i in () do @echo %i %j %k
it didnt even work with a SINGLE % and NOT %%


Post Reply