Panorama supports a special data structure called a data dictionary (often referred to as just a dictionary). Like a conventional dictionary, a data dictionary contains a list of entries, and each entry comes as a pair - a key (which identifies the entry) and a value (some data associated with this entry). These entries are referred to as key/value pairs (sometimes the key is referred to as the name of the entry). If you know the key to a data dictionary entry, you can find out what the value is. The key identifies the pair, and must be unique within that dictionary.
The key can contain any text character you want, including spaces and punctuation. The value is not restricted to text – you can use any type of data that Panorama supports, including text, numbers, dates, and binary items such as Colors, Data Arrays, and Graphic Coordinates. So, if you want to store a collection of data items of different types where you want data values to be associated with unique keys, a data dictionary is ideal. For instance, here’s a set of key/value pairs tailor-made for a data dictionary - the values include text, integer, floating point and binary values:
Key | Value |
---|---|
Category | Orange paint |
Quantity/pack | 12 |
Color | rgb( 65535 , 23356 , 2936 ) |
Price | 17.95 |
If you are familiar with Panorama Data Arrays, data dictionaries are in some ways similar, but with two important distinctions: values in a data dictionary are always associated with a key and the key/value pairs are not stored in any specific order - the concept of the first or last entry is meaningless. Like data arrays, dictionaries are stored as opaque Binary Data. You can store a dictionary in a field or variable, but you cannot directly display or edit it. If you want to store a dictionary in a field, it must be a binary field. You can nest data dictionaries, data arrays and text arrays within dictionaries to as many levels as you like, noting of course that, whilst a data array or a dictionary can contain binary entities, a text array cannot.
A data dictionary allows you to combine any number of key/value pairs into a single structure that can be stored in a variable, a field or a procedure parameter. For example, in a database you could use a data dictionary to store additional, seldom-used data that you don’t want to devote an entire field to, or to store information that you didn’t anticipate when you created the database. When a procedure (or custom statement) has a large and variable number of parameters, you can combine these into a data dictionary that can be passed back and forth easily (many of Panorama’s Internet access statements work this way). Data dictionaries are also an excellent way to store preferences, and they are used that way by many of Panorama’s wizards.
The remainder of this entry describes the ways in which you can create, modify and access the contents of data dictionaries. The various statements and functions involved are not described in detail - that information is provided in each individual Help Wizard entry.
Use the initializedictionary statement or the initializedictionary( function to create a dictionary containing any number of key/value pairs, as in:
local mailingAddress
mailingAddress = initializedictionary(
"Address","3987 Olive Court",
"City","Tustin", "State","CA", "Zip","92841")
Here is the contents of this dictionary. (Note, however, that if you examine the contents they may not be in this order.)
Address ☞ 3987 Olive Court
City ☞ Tustin
State ☞ CA
Zip ☞ 92841
A dictionary value can be modified with the setdictionaryvalue statement. This statement can change one or more dictionary values simultaneously.
setdictionaryvalue mailingAddress,
"Address","3987 Olive Court",
"Zip","92841-8437"
If a specified key doesn’t exist, a new entry is created with that key. For instance, this code adds a Status key to the * mailingAddress* dictionary:
setdictionaryvalue mailingAddress,"Status","Inactive"
There was no Status key, so one was created and the contents of the dictionary will now be (in no particular order):
Zip ☞ 92841-8437
Address ☞ 3987 Olive Court
City ☞ Tustin
State ☞ CA
Status ☞ Inactive
You can also modify an entry by appending additional text to the value portion of an existing key/value pair. If, for instance, the value component of a dictionary item was a log of events and their dates, you might want to add a new event and date to the existing value. This code adds a new line to the value associated with a dictionary entry with the key of Log in the field or variable called Operation.
appenddictionaryvalue Operation,"Log",cr(),"Approval received on May 25, 2016"
The changedictionaryvalues( function provides an alternate method for changing values – it can be used in a formula as well as in procedure code.
mailingAddress = changedictionaryvalues(mailingAddress,
"Address","3987 Olive Court","Zip","92841-8437")
The setdictionaryvalue statement is also used to add a key/value pair to an existing dictionary. If we had a field or variable called ColorPalette, this code would place a single key/value pair in it and then add two more:
local ColorPalette
ColorPalette=initializedictionary("TextColor","red")
setdictionaryvalue ColorPalette,"ButtonColor","blue","Background","white"
Now ColorPalette contains three key/value pairs - TextColor/red, ButtonColor/blue and Background/white. You can change the value of each key/value pair individually at any time. This line of code will change the ButtonColor to green.
setdictionaryvalue ColorPalette,"ButtonColor","green"
In unusual situations you may need to modify a key/value pair by changing the key instead of the value – this can be done with the changedictionaryname statement. This example changes the key of an entry in the mailingAddress example above:
changedictionaryname mailingAddress,"Zip","Zip Code"
Only one key can be changed at a time.
Once values have been stored in a dictionary they can be retrieved (one at a time) with the getdictionaryvalue( function, like this:
theColor=getdictionaryvalue(ColorPalette,"TextColor")
The first parameter is the name of the field or variable that contains the dictionary, while the second parameter is the key of the item you want to retrieve.
If you are not sure if a dictionary contains a particular key/value pair you can add a default option to the function. In this example, if there is no HighlightColor stored in the dictionary, then yellow will be returned.
theColor=getdictionaryvalue(ColorPalette,"HighlightColor","yellow")
You can also use the dictionaryvalueexists( function to explicitly check if a key/value pair exists. Although the function name implies that it looks for a value, it actually looks for a key name. This example checks to see if a button color has been defined and, if so, calls a subroutine.
if dictionaryvalueexists(Palette,"ButtonColor")
call a subroutine
endif
In some cases it may be possible to look up a key if all you know is the value. This is the reverse of the normal operation, which is to look up the value given the key. The getdictionarykey statement and the getdictionarykey( function can do this. If the value is not unique (if two or more key/value pairs have the same value) then it is unpredictable what key will be returned, so you should only use this statement if you know that the value is unique.
getdictionarykey(mailto,"3987 Olive Court") ☞ Address
If necessary, you can delete individual key/value pairs with the deletedictionaryvalue statement or the deletedictionaryvalue( function. The following statement completely removes the ButtonColor key/value pair from the ColorPalette dictionary:
deletedictionaryvalue ColorPalette,"ButtonColor"
if the Key parameter is an empty string, all key/value pairs will be removed but you can do that more easily by setting the ColorPalette variable to an empty string.
You can delete the entire contents of a dictionary by setting its value to an empty string, as in:
myDictionary = ""
This is the only text value that can be used as the contents of a dictionary. Any attempt to use a non-zero length text value as a dictionary will result in an error.
To obtain a complete list of all of the keys used in a dictionary, use the listdictionarykeys( function. This function has one parameter, the name of the field or variable that contains the dictionary.
message listdictionarykeys(ColorPalette)
The output is a carriage return delimited text array that lists the keys stored in the dictionary.
The dumpdictionary statement outputs a carriage return delimited text array that contains all of the keys and their values, with an equals sign separating each key and value.
local ColorPalette,PaletteDump
dumpdictionary ColorPalette,PaletteDump
message PaletteDump
The output displayed by the message statement will look something like this:
TextColor=red
ButtonColor=green
Background=white
The dumpdictionaryquoted statement is similar, but any key containing a space is surrounded by chevrons and each text value is surrounded by quotes. Numeric values are not quoted. A typical output could be:
«Background color»="red"
«Highlight color»="green"
Depth=12
The dictionarydifference statement takes two dictionaries and lists the differences (if any). It does this by getting the list of keys in each dictionary, and then comparing the values of matching keys. The keys that have different values in the two dictionaries are output as a carriage return delimited text array. When this next example is run, the differences field or variable will contain a carriage return delimited text array listing the keys which have different values in the primaryContact dictionary to those in the secondaryContact dictionary.
dictionarydifference primaryContact, secondaryContact, differences
You can modify some or all of the key/value pairs in a dictionary with the help of the looparray statement and the listdictionarykeys( function. This example looks at all of the key/value pairs of the dictionary stored in the variable, Product, and changes all text values to upper case:
local pkey,pvalue
looparray
listdictionarykeys(Product), cr(), pkey
pvalue = getdictionaryvalue(Product,pkey)
if datatype(pvalue)="Text"
setdictionaryvalue Product,pkey,upper(pvalue)
endif
endloop
Earlier versions of Panorama allowed you to directly append one dictionary to another but this is no longer the case. You must use either the mergedictionaries statement which can merge the contents of two dictionaries or the mergedictionaries( function which can merge the contents of two or more dictionaries. Suppose you created three dictionaries with information about a person named Mary Wilson.
fileglobal mailInfo,phoneInfo,emailInfo,contactInfo
mailInfo=initializedictionary("First","Mary","Last","Wilson",
"Address","3987 Olive Court","City","Tustin","State","CA","Zip","92841")
phoneInfo=initializedictionary("First","Mary","Last","Wilson","Phone","562-309-5923")
emailInfo=initializedictionary("First","Mary","Last","Wilson","Email","mwilson@someisp.com")
Later, you could use the mergedictionaries( function to create a combined dictionary that contains all of Ms. Wilson’s contact information:
contactInfo=mergedictionaries(mailInfo,phoneInfo,emailInfo)
When merging dictionaries, it is possible that two dictionaries will contain the same keys, like First and Last in the above example. See mergedictionaries( for how to handle that.
The copypartialdictionary statement and the copypartialdictionary( function take one or more key names and copy the associated key/value pairs from the original dictionary into a new one. For example, suppose you created a dictionary and stored it in a variable named contact:
fileglobal contact,mailto
contact=initializedictionary("First","John","Last","Wilson",
"Address","3987 Olive Court","City","Tustin","State","CA","Zip","92841",
"Phone","562-309-5923","Email","jwilson@someisp.com")
Later, you could use the copypartialdictionary( function to create a second dictionary, called mailto, that contains just the address information (without the phone number, e-mail, or any other entries that may have been added to the original dictionary):
mailto=copypartialdictionary(contact,"Address","City","State","Zip")
Panorama provides several statements that allow you to copy one or more procedures into a dictionary. This allows you to store procedures separately from the database, or to transfer procedures from one database to another. The Panorama Enterprise server uses this technique to transfer databases from the client to the server.
The saveallprocedures statement saves all of the procedures in a specified database into a field or variable as a dictionary with the procedure name as the key and the procedure text as the value.
saveallprocedures databasename, variablename
Once saved this way, you can access individual procedures with the getdictionaryvalue( function. In addition to saving all procedures, you can also store just those procedures which are open:
saveopenprocedures databasename, variablename
Or just a single procedure:
saveoneprocedure databasename, procedurename, variablename
Once you have a variable that contains a dictionary of procedures, you can load them into another database. This can provide a quick way to transfer multiple procedures from one database to another. To do this, use the loadallprocedures statement which loads all the procedures from a dictionary into the specified database. If a procedure doesn’t exist, it will be created.
loadallprocedures variablename, procedurelist
The variablename parameter is the variable that contains the dictionary. The procedurelist parameter is also the name of a variable. This variable will be set to a carriage return delimited list of the procedures that were actually changed by the loadallprocedures statement. If no procedures were changed then the list will be empty. This second parameter is optional - if you don’t care what procedures were modified you can omit it.
Here is an example that copies the .ModifyRecord procedure from the current database to the Contact database.
local pxfr,pupdates
saveoneprocedure "",".ModifyRecord", pxfr
openfile "Contacts"
loadallprocedures pxfr, pupdates
if pupdates=""
message ".ModifyRecord was already copied"
endif
The comparedictprocedures statement allows you to compare procedures that have been saved into a dictionary with the procedures in a specified database. It returns information about which procedures have changed.
comparedictprocedures databasename, dictionary, modified, new, removed
The databasename parameter is the name of the database, or “” for the current database and the dictionary parameter is the variable that contains the dictionary (built with the saveallprocedures statement).
The modified parameter is a field or variable which will contain a carriage return delimited list of procedures that were actually modified. The new parameter is a field or variable will contain a carriage return delimited list of procedures that exist in the dictionary but not in the database. The removed parameter is a field or variable will contain a carriage return delimited list of procedures that exist in the database but not in the dictionary.
Our example is in two parts. The first part saves the current procedures into a permanent variable.
permanent savedProcedures
saveallprocedures "",savedProcedures
Use this procedure to save the current state of the procedures. Then do some work, editing various procedures, creating new ones and deleting old ones. After doing this you can use the following code to see which procedures were changed.
local modded,added,xed
comparedictprocedures "",savedProcedures,modded,added,xed
displaydata "Modified Procedures:"+cr()+modded+cr()+cr()+
"New Procedures:"+cr()+added+cr()+cr()+
"Deleted Procedures:"+cr()+xed
The statements and functions listed below use dictionaries in their operation (this list is not all-inclusive):
See Also