Mailing List Archive: 49091 messages

## [REBOL] Re: Bidirectional value mapping.

### From: joel:neely:fedex at: 3-Nov-2003 12:56

```
Hi, Bruno,

I know of no way to avoid some duplication.  See below.

Bruno G. Albuquerque wrote:
> I have 2 values that would be mapped to each other. What I need to do is
> to be able to find the first valeu by searching for the ceond and locate
> the second by searching for the first. I did come up with solutions to
> that but I am not satisfied with any of the solutions. Is there a standard
> Rebol-Way ofr doing that? The best option would be a way that would not
> result in data duplication.
>

The answer somewhat depends on details of your problem; if "forward"
and "reverse" mappings are distinct (as in translating between host
names and IP addresses, or encoding and decoding with a non-symmetric
encryption scheme) then the simplest thing to do is keep both maps
as separate blocks (or hashes):

>> roman2number: ["i" 1 "v" 5 "x" 10 "l" 50 "c" 100]
== ["i" 1 "v" 5 "x" 10 "l" 50 "c" 100]
>> number2roman: [1 "i" 5 "v" 10 "x" 50 "l" 100 "c"]
== [1 "i" 5 "v" 10 "x" 50 "l" 100 "c"]
>> select roman2number "x"
== 10
>> select number2roman 10
== "x"

If you know which "way" you're mapping, you just select the appropriate
map.  (Of course, it's easy to write a function that would take one of
the above and give the other, so you don't have to create both by hand.)

NOTE!!!  That last sentence is only true if the mapping is an invertable
function!!!  If, instead, you have a many-to-one (e.g. letters to the
words "consonant" or "vowel") of course there's no way to invert.  I
assume you know that, but want to include that warning for completeness.

The other (perhaps slightly more "REBOLish") way to do this is to inter-
leave the forward and reverse mapping values as follows:

>> roman-numerals: [
"i" 1 "i" "v" 5 "v" "x" 10 "x" "l" 50 "l" "c" 100 "c"
]
== ["i" 1 "i" "v" 5 "v" "x" 10 "x" "l" 50 "l" "c" 100 "c"]
>> select roman-numerals "x"
== 10
>> select roman-numerals 10
== "x"

This works nicely for such things as ROT13, which is a self-inverse
mapping (and again, you can write a utility function that would take
e.g. ROMAN2NUMBER above and give you the interleaved ROMAN-NUMERALS).
However THIS DOES NOT WORK IN GENERAL if the domain and range of your
mapping overlap.  As a simple example, consider the trivial example of
rotating among three elements:

forward: ["a" "b" "c" "a"]
reverse: ["c" "b" "a" "c"]

Since "a" maps to "b" going forward, but "b" maps to "c" going forward,
then both forward and reverse mappings can't be combined into a single
block/hash.  To see why this is true, consider what would have to follow
b
in the combined series!

Unless you know you have a self-inverse mapping and you consider speed
soooooo important that you'll sacrifice readability for performance,
I recommend using distinct forward/reverse mappings (with a helper to
construct the inverted one).  It'll be easier to read anyway!

-jn-

--
----------------------------------------------------------------------
Joel Neely            joelDOTneelyATfedexDOTcom           901-263-4446

Enron Accountingg in a Nutshell: 1c=\$0.01=(\$0.10)**2=(10c)**2=100c=\$1
```