Array construction with empty value

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
SIMMS7400
Posts: 539
Joined: 07 Jan 2016 07:47

Array construction with empty value

#1 Post by SIMMS7400 » 18 Sep 2021 08:04

Hi Folks -

This may be a stupid question. I have the following array:

Code: Select all

FOR %%a IN (
	"FDR|DLR_DATA_FDRII,DLR_DATA_FDRII|inbox/LOC_DATA_FDRII|REPLACE|STORE_DATA"
) DO FOR /F "tokens=1-4 delims=|" %%A IN (%%a) DO (
        SET "STR[%%A].DLR=%%B"
        SET "STR[%%A].LOC=%%C"
        SET "STR[%%A].IMP=%%D"
        SET "STR[%%A].EXP=%%E"
)
There may be a time where the 4th position (i.e. REPLACE) does not require a value and therefore must remain empty. Naturally, batch does not know how to read that empty index and then shift everything to the left one after the empty. Is there a way to force the construction of my array to account for the empty index?

Code: Select all

FOR %%a IN (
	"FDR|DLR_DATA_FDRII,DLR_DATA_FDRII|inbox/LOC_DATA_FDRII||STORE_DATA""
) DO FOR /F "tokens=1-4 delims=|" %%A IN (%%a) DO (
        SET "STR[%%A].DLR=%%B"
        SET "STR[%%A].LOC=%%C"
        SET "STR[%%A].IMP=%%D"
        SET "STR[%%A].EXP=%%E"
)
My hope is to still have "STORE_DATA" be available in !STR[%%A].EXP! when !STR[%%A].IMP! is empty.

Thank you!

SIMMS7400
Posts: 539
Joined: 07 Jan 2016 07:47

Re: Array construction with empty value

#2 Post by SIMMS7400 » 18 Sep 2021 08:33

Found a couple old threads and this is what I came up with:

Code: Select all

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
FOR %%a IN (
	"FDR|DLR_DATA_FDRII,DLR_DATA_FDRII|inbox/LOC_DATA_FDRII||STORE_DATA"
) DO FOR /F "eol=' delims=" %%G IN ("%%a") DO (
  SET "L=%%~G"
  REM add a backslash (or other character) in front of every semicolon
  CALL SET "L=%%L:|=\|%%"
  FOR /F "tokens=1-7 delims=|" %%a IN ('CALL ECHO "%%L%%"') do (
    SET "1=%%~a" & SET "1=!1:~0,-1!
    SET "2=%%~b" & SET "2=!2:~0,-1!"
    SET "3=%%~c" & SET "3=!3:~0,-1!"
    SET "4=%%~d" & SET "4=!4:~0,-1!"
    SET "5=%%~e" & SET "5=!5:~0,-1!"
    
    SET "STR[!1!].DLR=!2!"
    SET "STR[!1!].IMP=!3!"
    SET "STR[!1!].EXP=!4!"
    SET "STR[!1!].EXP_FILE=!5!"
  )
)
echo !STR[FDR].DLR!
echo !STR[FDR].IMP!
echo !STR[FDR].EXP!
echo !STR[FDR].EXP_FILE!

pause
Is this the correct approach?

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

Re: Array construction with empty value

#3 Post by T3RRY » 18 Sep 2021 08:47

SIMMS7400 wrote:
18 Sep 2021 08:04
Hi Folks -

This may be a stupid question. I have the following array:

Code: Select all

FOR %%a IN (
	"FDR|DLR_DATA_FDRII,DLR_DATA_FDRII|inbox/LOC_DATA_FDRII|REPLACE|STORE_DATA"
) DO FOR /F "tokens=1-4 delims=|" %%A IN (%%a) DO (
        SET "STR[%%A].DLR=%%B"
        SET "STR[%%A].LOC=%%C"
        SET "STR[%%A].IMP=%%D"
        SET "STR[%%A].EXP=%%E"
)
There may be a time where the 4th position (i.e. REPLACE) does not require a value and therefore must remain empty. Naturally, batch does not know how to read that empty index and then shift everything to the left one after the empty. Is there a way to force the construction of my array to account for the empty index?

Code: Select all

FOR %%a IN (
	"FDR|DLR_DATA_FDRII,DLR_DATA_FDRII|inbox/LOC_DATA_FDRII||STORE_DATA""
) DO FOR /F "tokens=1-4 delims=|" %%A IN (%%a) DO (
        SET "STR[%%A].DLR=%%B"
        SET "STR[%%A].LOC=%%C"
        SET "STR[%%A].IMP=%%D"
        SET "STR[%%A].EXP=%%E"
)
My hope is to still have "STORE_DATA" be available in !STR[%%A].EXP! when !STR[%%A].IMP! is empty.

Thank you!
With tokens 1-4, there is no %%E token captured to define STORE_DATA.

The following achieves the desired by using string substitution to replace an otherwise empty token with a temporary string - 'nul' and then tests against that value before defining the array elements. In this way, each token retains its original position in the array

Code: Select all

@Echo off

Setlocal
FOR %%a IN (
	"FDR|DLR_DATA_FDRII,DLR_DATA_FDRII|inbox/LOC_DATA_FDRII||STORE_DATA"
) DO (
  Set "Group=%%~a"
  Setlocal EnableDelayedExpansion
  Set "Group=!Group:|||=|nul|nul|!" %= account for 2 empty elements =%
  For %%v in ("!Group:||=|nul|!")Do Endlocal & FOR /F "tokens=1-5 delims=|" %%A IN ("%%~v") Do (
        If not "%%B"=="nul" SET "STR[%%A].DLR=%%B"
        If not "%%C"=="nul" SET "STR[%%A].LOC=%%C"
        If not "%%D"=="nul" SET "STR[%%A].IMP=%%D"
        If not "%%E"=="nul" SET "STR[%%A].EXP=%%E"
  ) 
)

Set STR[
endlocal
Last edited by T3RRY on 18 Sep 2021 09:56, edited 3 times in total.

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

Re: Array construction with empty value

#4 Post by T3RRY » 18 Sep 2021 09:50

SIMMS7400 wrote:
18 Sep 2021 08:33
Found a couple old threads and this is what I came up with:

Code: Select all

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
FOR %%a IN (
	"FDR|DLR_DATA_FDRII,DLR_DATA_FDRII|inbox/LOC_DATA_FDRII||STORE_DATA"
) DO FOR /F "eol=' delims=" %%G IN ("%%a") DO (
  SET "L=%%~G"
  REM add a backslash (or other character) in front of every semicolon
  CALL SET "L=%%L:|=\|%%"
  FOR /F "tokens=1-7 delims=|" %%1 IN ('CALL ECHO "%%L%%"') do (
    SET "1=%%~1" & SET "1=!1:~0,-1!
    SET "2=%%~2" & SET "2=!2:~0,-1!"
    SET "3=%%~3" & SET "3=!3:~0,-1!"
    SET "4=%%~4" & SET "4=!4:~0,-1!"
    SET "5=%%~5" & SET "5=!5:~0,-1!"
    
    SET "STR[!1!].DLR=!2!"
    SET "STR[!1!].IMP=!3!"
    SET "STR[!1!].EXP=!4!"
    SET "STR[!1!].EXP_FILE=!5!"
  )
)
echo !STR[FDR].DLR!
echo !STR[FDR].IMP!
echo !STR[FDR].EXP!
echo !STR[FDR].EXP_FILE!

pause
Is this the correct approach?
I think you'll find this is still resulting in tokens being defined out of sequence.

Some other points:

Code: Select all

FOR /F "eol=' delims=" %%G IN ("%%a") DO (
%%a already contains doublequotes, so you may either use `in (%%a)` or `in ("%%~a")`
eol=' is uneccessary here.

Delayed expansion is already enabled, so:

Code: Select all

  CALL SET "L=%%L:|=\|%%"
becomes unecessary, the variable `L` can be expanded with substitution in the for set.

Code: Select all

  FOR /F "tokens=1-5 delims=|" %%a IN ("!L:|=\|!"') do (
If outputting variables that may be undefined, use `echo(` instead of `echo ` to prevent `Echo is Off/On` that results when the echo command is used without arguments (thanks to the empty variable).

Code: Select all

 Echo( !somevar!
For maintainence sake, I wouldn't recommend using the same for metavariable repeatedly within the same code block.

All together:

Code: Select all

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
FOR %%a IN (
	"FDR|DLR_DATA_FDRII,DLR_DATA_FDRII|inbox/LOC_DATA_FDRII|REPLACE|STORE_DATA"
) DO FOR /F "delims=" %%G IN (%%a) DO (
  SET "L=%%~G"
  REM add a backslash (or other character) in front of every pipe
  FOR /F "tokens=1-5 delims=|" %%1 IN ("!L:|=\|!") do (
    SET "1=%%~1" & SET "1=!1:~,-1!
    SET "2=%%~2" & SET "2=!2:~,-1!"
    SET "3=%%~3" & SET "3=!3:~,-1!"
    SET "4=%%~4" & SET "4=!4:~,-1!"
    SET "5=%%~5"
    
    SET "STR[!1!].DLR=!2!"
    SET "STR[!1!].IMP=!3!"
    SET "STR[!1!].EXP=!4!"
    SET "STR[!1!].EXP_FILE=!5!"
  )
)
Set STR[
pause
Endlocal
Through the use of conditional testing the extra assignments and substitutions in the code block can be eliminated, as shown in my last reply.

SIMMS7400
Posts: 539
Joined: 07 Jan 2016 07:47

Re: Array construction with empty value

#5 Post by SIMMS7400 » 21 Sep 2021 02:09

Thank you, Terry!! Your suggestion is working great for me!!

Aacini
Expert
Posts: 1885
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Array construction with empty value

#6 Post by Aacini » 21 Sep 2021 11:26

A couple comments. First of all, your first FOR command is not useful at all. That is:

Code: Select all

FOR %%a IN (
	"FDR|DLR_DATA_FDRII,DLR_DATA_FDRII|inbox/LOC_DATA_FDRII|REPLACE|STORE_DATA"
) DO FOR /F "tokens=1-4 delims=|" %%A IN (%%a) DO (
... have the exact same result than:

Code: Select all

FOR /F "tokens=1-4 delims=|" %%A IN (
	"FDR|DLR_DATA_FDRII,DLR_DATA_FDRII|inbox/LOC_DATA_FDRII|REPLACE|STORE_DATA"
) DO (
Would you be willing to evaluate a different method?

Code: Select all

@echo off
setlocal EnableDelayedExpansion

set "vars=DLR LOC IMP EXP"
set "data=FDR|DLR_DATA_FDRII,DLR_DATA_FDRII|inbox/LOC_DATA_FDRII|REPLACE|STORE_DATA"

echo !data!
set "V=" & set "B=" & set "p=%%"
set "A=%data:|=" & set "STR[!A!].!V!=!B!" & set "s=!vars:* =!" & call set "V=!p!vars: !s!=!p!" & set "vars=!s!" & set "B=%" & set "STR[!A!].!vars!=!B!"
set STR[
Output examples:

Code: Select all

FDR|DLR_DATA_FDRII,DLR_DATA_FDRII|inbox/LOC_DATA_FDRII|REPLACE|STORE_DATA
STR[FDR].DLR=DLR_DATA_FDRII,DLR_DATA_FDRII
STR[FDR].EXP=STORE_DATA
STR[FDR].IMP=REPLACE
STR[FDR].LOC=inbox/LOC_DATA_FDRII


FDR|DLR_DATA_FDRII,DLR_DATA_FDRII|inbox/LOC_DATA_FDRII||STORE_DATA
STR[FDR].DLR=DLR_DATA_FDRII,DLR_DATA_FDRII
STR[FDR].EXP=STORE_DATA
STR[FDR].LOC=inbox/LOC_DATA_FDRII
As a matter of fact, with this method you can omit any value with the proper result:

Code: Select all

FDR||inbox/LOC_DATA_FDRII||STORE_DATA
STR[FDR].EXP=STORE_DATA
STR[FDR].LOC=inbox/LOC_DATA_FDRII


FDR||||STORE_DATA
STR[FDR].EXP=STORE_DATA
Further details on the method used at this answer.

Antonio

SIMMS7400
Posts: 539
Joined: 07 Jan 2016 07:47

Re: Array construction with empty value

#7 Post by SIMMS7400 » 23 Sep 2021 12:17

HI Aacini -

Thank you very much!! I really like your methodology and I'm going to poke around with it now! In the mean time, I still need to leverage my current methods until I'm able to make the switch.

With that said, I do believe I still need my first for loop otherwise it does not cycle through all options.

For instance, when I use this method, it will loop through both definitions:

Code: Select all

FOR %%a IN (
	"FDR|DLR_DATA_FDRII,DLR_DATA_FDRII|inbox/LOC_DATA_FDRII|REPLACE"
    "FDR1|DLR_DATA_FDRII1,DLR_DATA_FDRII1|inbox/LOC_DATA_FDRII|REPLACE"
) DO FOR /F "tokens=1-4 delims=|" %%A IN (%%a) DO (
        SET "STR[%%A].DLR=%%B"
        SET "STR[%%A].LOC=%%C"
        SET "STR[%%A].IMP=%%D"
)
But if I remove the first FOR LOOP and build like this, it will cycles through the first definition:

Code: Select all

FOR /F "tokens=1-4 delims=|" %%A IN (
	"FDR|DLR_DATA_FDRII,DLR_DATA_FDRII|inbox/LOC_DATA_FDRII|REPLACE"
    "FDR1|DLR_DATA_FDRII1,DLR_DATA_FDRII1|inbox/LOC_DATA_FDRII|REPLACE"
) DO (
    SET "STR[%%A].DLR=%%B"
    SET "STR[%%A].LOC=%%C"
    SET "STR[%%A].IMP=%%D"
)
So I think I need to use 2.

I do have a question on values as I'm running into an issue when I have a value I need to quote. In the 4 position, we have values that have a space in them so I need to quote in order to load successfully in my target application.
If I run this, it bombs on the space even though it's quote. If I remove hte quotes, it's fine, but then it's not quoted when I need it to be later on. How do I get around this?

Code: Select all

FOR %%a IN (
	"FDR|DLR_DATA_FDRII,DLR_DATA_FDRII|inbox/LOC_DATA_FDRII|REPLACE"
    "FDR1|DLR_DATA_FDRII1,DLR_DATA_FDRII1|inbox/LOC_DATA_FDRII|Mode="REPLACE Data""
) DO FOR /F "tokens=1-4 delims=|" %%A IN (%%a) DO (
        SET "STR[%%A].DLR=%%B"
        SET "STR[%%A].LOC=%%C"
        SET "STR[%%A].IMP=%%D"
)

SIMMS7400
Posts: 539
Joined: 07 Jan 2016 07:47

Re: Array construction with empty value

#8 Post by SIMMS7400 » 23 Sep 2021 12:26

If I put double quotes around it, it works (doesn't bomb) but then the value has two sets of quotes instead of one...hmmm

Code: Select all

DLR_DATA_FDRII1,DLR_DATA_FDRII1
inbox/LOC_DATA_FDRII
Mode=""REPLACE Data""
If I do this, it works, but seems a bit clunky to remove the quotes:

Code: Select all

FOR %%a IN (
	"FDR|DLR_DATA_FDRII,DLR_DATA_FDRII|inbox/LOC_DATA_FDRII|REPLACE"
    "FDR1|""DLR_DATA FDRII1"",DLR_DATA_FDRII1|inbox/LOC_DATA_FDRII|Mode=""REPLACE Data"""
) DO (
SET "LIST=%%a" & SET "LIST=!LIST:""="!"
FOR /F "tokens=1-4 delims=|" %%A IN (!LIST!) DO (
        SET "STR[%%A].DLR=%%B"
        SET "STR[%%A].LOC=%%C"
        SET "STR[%%A].IMP=%%~D"
))
echo !STR[FDR1].DLR!
echo !STR[FDR1].LOC!
echo !STR[FDR1].IMP!
pause

Code: Select all

"DLR_DATA FDRII1",DLR_DATA_FDRII1
inbox/LOC_DATA_FDRII
Mode="REPLACE Data"

Aacini
Expert
Posts: 1885
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Array construction with empty value

#9 Post by Aacini » 23 Sep 2021 19:25

Ok. This is a two-step answer...

The FOR /F command works on lines, not strings. In order to write constant strings in a FOR /F command and process all of them you need to know how to write the strings in the program as if they were lines. To do so:
  1. End each string with a caret character ^ and leave empty the next line (excepting the last one).
  2. Insert additional delimiters at begin and end of all strings, and adjust the tokens= values accordingly (add 1 to each value).
  3. Insert any character before the first delimiter in the first string (this is the only ugly patch).
For example:

Code: Select all

@echo off
setlocal EnableDelayedExpansion

for /F "tokens=2-5 delims=|" %%a in (
	" |one|two|three|four|"^

	"|black|red|green|blue|"^

	"|john|paul|george|ringo|"
) do (
   echo [%%a,%%b,%%c,%%d]
)
Output:

Code: Select all

[one,two,three,four]
[black,red,green,blue]
[john,paul,george,ringo]
If you complete these modifications in your code, you also fix your space/quotes problem:

Code: Select all

FOR /F "tokens=2-5 delims=|" %%A IN (
	" |FDR|DLR_DATA_FDRII,DLR_DATA_FDRII|inbox/LOC_DATA_FDRII|REPLACE|"^

	"|FDR1|DLR_DATA_FDRII1,DLR_DATA_FDRII1|inbox/LOC_DATA_FDRII|Mode="REPLACE Data"|"
) DO (
        echo SET "STR[%%A].DLR=%%B"
        echo SET "STR[%%A].LOC=%%C"
        echo SET "STR[%%A].IMP=%%D"
        SET "STR[%%A].DLR=%%B"
        SET "STR[%%A].LOC=%%C"
        SET "STR[%%A].IMP=%%D"

        echo [!STR[%%A].IMP!]
)
Output:

Code: Select all

SET "STR[FDR].DLR=DLR_DATA_FDRII,DLR_DATA_FDRII"
SET "STR[FDR].LOC=inbox/LOC_DATA_FDRII"
SET "STR[FDR].IMP=REPLACE"
[REPLACE]
SET "STR[FDR1].DLR=DLR_DATA_FDRII1,DLR_DATA_FDRII1"
SET "STR[FDR1].LOC=inbox/LOC_DATA_FDRII"
SET "STR[FDR1].IMP=Mode="REPLACE Data""
[Mode="REPLACE Data"]
Antonio

SIMMS7400
Posts: 539
Joined: 07 Jan 2016 07:47

Re: Array construction with empty value

#10 Post by SIMMS7400 » 24 Sep 2021 04:12

Antonio -

Thank you very much! I understand now. This certainly increases the risk of errors (if the line isn't truly blank) etc. I will work with it for now and the conver to your other method.

Which I have a question on, how do I sent multiple "data" lines?

For instance, what if I have 5 "data" lines (definitions). What's an efficient way to activate those arrays without creating new variables for each line?

Post Reply