Fun & Games with Dictionaries

I worked this scheme up as a solution for a project that needs a way to move back and forth between different batches of variable values in a manageable way. There may be better ways to handle this but here is what I have been experimenting with…

Using Dictionaries to Retain Multiple States of Several Variables

There may be situations where you would like to revert back to some previous state when a batch of variables contained different values than they currently have. Here is a method to move multiple variable values back and forth via dictionaries en masse.

Create a dictionary in the .Initialize procedure containing the future variables as keys along with their starting values:

letfileglobal dVariables=initializedictionary( "First", "Something","Second", "Another thing", "Third", 254)

Note: You can also make them permanent variables using letpermanent instead of letfileglobal.

Now you can create all the actual variables along with their starting values directly from the dVariables dictionary using dumpdictionaryquoted( and changing the carriage returns to commas:

execute "makefileglobals "+replace(dumpdictionaryquoted(dVariables),cr(),",")

This code will execute as:

makefileglobals First="Something",Third=254,Second="Another thing"

Note: That the dictionary does not return the keys in any particular order.

At any point we can change the dictionary key values or the actual variables. To change one or more key values use this standard code:

setdictionaryvalue dVariables, "First", "Nada", "Second", "Mucho", "Third", 389

Then to transfer these values to the actual variables in forms, execute the standard output of dumpdictionaryquoted(:

execute dumpdictionaryquoted(dVariables)
      ☞   This executes the following statements:

Now execute showvariables along with the listdictionarykeys( output which is also a list of the variable names (changing the cr()'s to commas):

execute "showvariables "+replace(listdictionarykeys(dVariables),cr(),",")
    ☞   This execute this statement:
	       showvariables First,Third,Second

You can also first change the variables and then transfer those values to the dictionary:

setdictionaryvalue dVariables, "First", First, "Second", Second, "Third", Third

Note: The key values above are not quoted and thus use the actual variable values.

So why bother with all this? In my case I wanted to have a way to step back in the recent history of the various variable values taken at different points. I can have multiple versions of the variable dictionaries so I can choose a previous version and populate the actual variables to those past values at any time. I created a fileglobal variable I name dicVersion and set that to 1. I can capture the current state of the variables to a new dictionary with this:

execute "letfileglobal dVariables"+str(dicVersion)+"=dVariables"
dicVersion = dicVersion +1
    ☞   This creates a new dictionary named dVariables1 set to the
        values of the dVariables dictionary and increments the
        dicVersion variable.

Additional dictionaries can be created along the way using:

execute "letfileglobal dVariables"+str(dicVersion)+"=dVariables"+str(dicVersion-1)
dicVersion = dicVersion +1
    ☞   This would create a new dictionary named dVariables2 with the same
        values as dVariables1.

To update all the variables to match a chosen dictionary and update forms you only need to run this (using dVariables1 in this case):

execute dumpdictionaryquoted(dVariables1)
execute "showvariables "+replace(listdictionarykeys(dVariables1),cr(),",")

If you want to eliminate any later dictionary versions you only need to reset the dicVersion variable back to an earlier number and optionally undefine the dictionaries you no longer need. For only a few variables as used in my example this is overkill but it is handy if you have to keep a handle on ten, twenty or more variables along the way.

Additional Idea: Record to Dictionary

This code will copy the current record to a dictionary retaining all the original data types (i.e. text, numeric, date or binary) unlike the exportline( function which converts everything to text.

Local recordDictionary
looparray info("fields"),cr(),element,index
    field element+""
    If index=1
        initializedictionary recordDictionary,
        setdictionaryvalue recordDictionary,

Now the dictionary contains the quoted field names along with the values for each one. I don’t know what you could use something like this for but it seemed interesting to me at the time.

I haven’t tested this, but I believe the code for converting a record to a dictionary could be quite a bit simpler. This takes advantage of the fact that "" is an empty dictionary. Gary, would you like to try it out?

let recordDictionary = ""
looparray info("fields"),cr(),fieldname
    setdictionaryvalue recordDictionary,fieldname,fieldvalue(fieldname+"")

I didn’t quote the field names as you did – I don’t see why you would ever want to do that. But you could easily modify this code to include the quotes if you wanted.

Later you could bring the dictionary back into a database record like this:

looparray info("fields"),cr(),fieldname
    set fieldname+"",getdictionaryvalue(recordDictionary,fieldname)

This code assumes that the dictionary exactly matches the fields in the database, if that is not the case, additional error checking will be needed.

Works the same as my longer version. The code to repopulate another record with the data also works. I also tried the code to populate the dictionary to include chevrons around the field names.

setdictionaryvalue recordDictionary,"«"+fieldname+"»",fieldvalue(fieldname+"")

This way the new record could be populated directly with:

execute dumpdictionaryquoted(recordDictionary)

Then the field names would not have match the exact order but would result in an error if there was a field missing. The only thing I notice in all cases is that blank numeric fields in the original record come out a 0 value in the new record. Could also be a problem in some cases.

As I stated to start with, I really don’t know where you would use this in practice.

Panorama X 10.2 Denali has a new function that can help with this, I should have thought of it:

So you can just use:

execute dictionaryassignmentscode(recordDictionary)

With this technique you don’t need to put the chevrons into the dictionary.