Page 1 of 1

Bi-directional access to lookup tables

Posted: 02 May 2014 12:10
by Ganove
Hi,

you describe a way how to use a lookup table on your site about DOS - String Manipulation:

Code: Select all

SET v=Mai

SET map=Jan-01;Feb-02;Mar-03;Apr-04;Mai-05;Jun-06;Jul-07;Aug-08;Sep-09;Oct-10;Nov-11;Dec-12
CALL SET v=%%map:*%v%-=%%
SET v=%v:;=&rem.%

ECHO.%v%

However, with that given method of accessing an item of a pair, you can just search for the left-sided item in order to return the right-sided item.

I wanted a way to access and return both items of a pair.

My first idea was to use a second table in which the order of the elements in a pair is inverted.
But that requires much work of coding and space or runtime for inverting the items.

So my next idea was to create an algorithm which uses just one table and looks up the right-sided element and returns the left-sided element of a pair.
Unfortunately

Code: Select all

SET v=05
CALL SET v=%%map:-%v%=&rem.%%
didn't work for cutting of the end of the string so I had to find a different way for doing that and I found two further possibilities to do so:

Code: Select all

call set v=%%map:-%v%="%%
call set "v=%v%

and

Code: Select all

call set v=-%iv%%%map:*-%v%=%%
call set v=%%map:%v%=%%


After that, I just needed to cut the left side of the string until to the latest ; (semicolon) which I did with

Code: Select all

set a=%a:;=&set a=%


All together, the code looks either like that

Code: Select all

SET map=Jan-01;Feb-02;Mar-03;Apr-04;Mai-05;Jun-06;Jul-07;Aug-08;Sep-09;Oct-10;Nov-11;Dec-12

SET v=05
call set v=%%map:-%v%="%%
call set "v=%v%
set v=%v:;=&set v=%

echo.%v%

or like that

Code: Select all

SET map=Jan-01;Feb-02;Mar-03;Apr-04;Mai-05;Jun-06;Jul-07;Aug-08;Sep-09;Oct-10;Nov-11;Dec-12

SET v=05
call set v=-%v%%%map:*-%v%=%%
call set v=%%map:%v%=%%
set v=%v:;=&set v=%

echo.%v%


What do you think which of those two codes could be better?
Do you have suggestions for improving my code or better ideas to realize a bi-directional access to lookup tables?

Re: Bi-directional access to lookup tables

Posted: 02 May 2014 13:44
by Magialisk
When it's a small number of elements I've done what you described first, a second table with the pairs inverted. It's not fancy, but it works well, and that way the code that extracts the data from the map can be identical.

The alternate method you propose is nice because you don't have to declare the maps in both directions, but the tradeoff is it requires a different function to extract the left member of a pair instead of the right. For me personally, I'd never be able to remember whether I defined my maps with this part or that part on the right or left, so I'd never remember which codeblock to call to extract data.

For maps with a much larger number of elements a good alternative is a psuedo-array. If your elements are indexed numerically (not necessarily sequentially, though that helps a lot), such as the 12 months you used as your example, it's extremely easy to define the array pairs:

Code: Select all

@echo off
SETLOCAL ENABLEDELAYEDEXPANSION
:: Build the map
set i=0
FOR %%a IN (Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec) DO (
   set /a i+=1
   set monthMap[%%a]=!i!
   set monthMap[!i!]=%%a
)
:: Want to know what month January is?
echo January is month number !monthMap[Jan]!
:: Want to know which is the 7th month?
echo the 7th month is !monthMap[7]!
Running this code gives this result:

Code: Select all

January is month number 1
the 7th month is Jul

I like this because instead of remembering the names of two maps, (ie: month2Num and num2Month) you only need the name of one array (monthMap). Also you don't need any function or special codeblock to do the extraction, it's a fairly intuitive variable expansion. Probably least relevant to the discussion, but another perk, is that the array structure is dynamic and easy to update. If for some reason you needed to re-map January to "7" instead of "1", it's a simple 'set monthMap[Jan]=7'. That can be done using the single long string maps, but it's a lot more cumbersome to parse and update them. Admittedly there's not that many situations where a map like this is going to be updated during program execution, but it's possible...

All that said, if you're trying to do a map between items that can't be indexed numerically, say a foreign language map, Green=Vert, White=Blanc, etc. it's going to take a lot of typing to get the psuedo-array defined such that you can extract via !toFrench[White]! :lol: There are better ways to skin that cat, but I'm fond of the array structure whenever I can make use of it, and I fall back on defining string maps in both directions otherwise.

Re: Bi-directional access to lookup tables

Posted: 02 May 2014 15:24
by Ganove
Magialisk wrote:When it's a small number of elements I've done what you described first, a second table with the pairs inverted. It's not fancy, but it works well, and that way the code that extracts the data from the map can be identical.

But before you write two maps, it's better to condense those two maps into a single map. Then you don't have to think about which map contains which direction.
Before I wrote the code for accessing from right to left-side, I developed this:

Code: Select all

set map=-01-Jan-01-02-Feb-02-03-Mar-03-04-Apr-04-05-Mai-05-06-Jun-06-07-Jul-07-08-Aug-08-09-Sep-09-10-Oct-10-11-Nov-11-12-Dec-12

set v=05
set oldv=%v%

call set v=%%map:*-%v%-=%%
set v=%v:-=&rem.%
echo.%oldv% is %v%

set oldv=%v%

call set v=%%map:*-%v%-=%%
set v=%v:-=&rem.%
echo.%oldv% is %v%

which echos

Code: Select all

05 is Mai
Mai is 05


The map actually works like this: item A points to item B and item B points to item C, where item C equals item A.
B should be those item, whose string is the longest of the given pair.

In order to keep the map small, I removed the pair separator sign. To keep the table consistent then, the beginning and end of an item must be identifiable, that's why item A and B have to begin and end in a dash character but item C don't.

Magialisk wrote:The alternate method you propose is nice because you don't have to declare the maps in both directions, but the tradeoff is it requires a different function to extract the left member of a pair instead of the right. For me personally, I'd never be able to remember whether I defined my maps with this part or that part on the right or left, so I'd never remember which codeblock to call to extract data.

You could write a function that decides which direction has to be used. If function month2number doesn't return a value then the function number2month should return a value, unless there was a request for an item which the map doesn't contain.


The pseudo-array is a really good idea. Especially the access to an item is distinctly quicker than the functions/macros for a map are.
You could save the pairs into a map - which is easy to code - and then convert them into a pseudo-array.
Of course, the process of conversion takes runtime, too, but after a certain amount of reading processes of the array the pseudo-array becomes faster than the map with its function concept would be.

Magialisk wrote:Probably least relevant to the discussion, but another perk, is that the array structure is dynamic and easy to update. If for some reason you needed to re-map January to "7" instead of "1", it's a simple 'set monthMap[Jan]=7'. That can be done using the single long string maps, but it's a lot more cumbersome to parse and update them. Admittedly there's not that many situations where a map like this is going to be updated during program execution, but it's possible...

There are advantages and disadventages everywhere, a pseudo-array is not easy to duplicate whereas a map is :)

So I would say it depends on situation what idea you would take.