Working with Lists

Tony Marston - 24th October 2002
amended 3rd Nov 2002


1. What is a 'list'?

A list is a string of one or more entries, where each entry is separated from the next by means of a delimiter (<GOLD> semicolon). Once a list of entries has been built it is then possible to reference individual entries by supplying an index number or a name. In other languages this construct is known as an ARRAY.

A list comes in two forms: INDEXED or ASSOCIATIVE.

1.1 An INDEXED list

An INDEXED list contains entries such as "Apple;Banana;Carrot". Entries in an INDEXED list can be referenced by using their index (sequence) number within the list. In the above example "Apple" = 1, "Banana" = 2 and "Carrot" = 3.

1.2 An ASSOCIATIVE list

An ASSOCIATIVE list contains entries such as "A=Apple;B=Banana;C=Carrot". Entries in an ASSOCIATIVE list can be referenced by using the 'name' part of each 'name=value' pair. This is also known as a valrep (value/representation) list and is used in such widgets as dropdown lists and radio groups. The 'value' part is what is used internally, and the 'representation' part is how that value is represented to the user.

A list can be a field name or a variable name, or $valrep(field) in order to manipulate the properties of a widget.

$valrep(field) = "string"       ; operates on all occurrences within current form

$fieldvalrep(field) = "string"  ; operates on current occurrence only

A list can be created with a simple assignment statement, as in:

$$list = "A=Apple;B=Banana;C=Carrot"

The contents may be defined on the message file, as in:

$$list = $text(v_star_signs)

A list can be emptied by assigning an empty string, as in:

$$list = ""

1.3 A list can be treated as either INDEXED or ASSOCIATIVE

Any list can be treated as either an indexed list or an associative list. Consider this list:

"A=Monday;B=Tuesday;C=Wednesday;D=Thursday;E=Friday;F=Saturday;G=Sunday"

It can be treated as an associative list by retrieving the representation whose value is "B".

It can be treated as an indexed list by retrieving the entire 2nd item ("B=Tuesday").

Consider this list:

"Monday;Tuesday;Wednesday;Thursday;Friday;Saturday;Sunday"

It can be treated as an indexed list by retrieving the 2nd item ("Tuesday").

It can be treated as an associative list by retrieving the representation whose value is "Tuesday". As there is no equals sign in this ValRep, the representation is the same as the value.

Within as associative list item, if an equal sign is preceded by a percent sign (%), it is not recognised as a ValRep separator. This allows you to use the equal sign in the value or representation of a list item, as in: "1=A%=B;2=A%=C".

2. Commands for manipulating Lists

2.1 PUTITEM -Add or replace an item in a list

for INDEXED lists: putitem list, N, source

N must be an integer greater than zero, with '-1' used to signify 'append'. If the number supplied is currently greater than the number of items in the list, empty items will be created until the Nth item is reached.

putitem $$list, -1, "Monday"    - appends
putitem $$list, -1, "Tuesday"   - appends
putitem $$list, -1, "Thursday"  - appends
putitem $$list, 3, "Wednesday"  - replaces item 3

If List contains an associative list, the entire ValRep of the associative item is replaced by Source.

for ASSOCIATIVE lists: putitem/id{/case} list, index{, source}

putitem/id $$list, "A", "Apple"   - appends
putitem/id $$list, "B", "Banana"  - appends
putitem/id $$list, "C", "Camel"   - appends
putitem/id $$list, "C", "Carrot"  - replaces item whose id = 'C'

By default the matching of Index with item values is not case-sensitive. If multiple entries exist, then the first one found in the list is deleted. If a specific match is required the /case switch must be used.

2.2 PUTLISTITEMS - Creates or updates a list with items from a specified source

for INDEXED lists: putlistitems $$list, field

(occurrence 1) day.calendar = "Monday"
(occurrence 2) day.calendar = "Tuesday"
(occurrence 3) day.calendar = "Wednesday"
setocc "calendar",1
putlistitems $$list, day.calendar  ; $$list =  "Monday;Tuesday;Wednesday"

for ASSOCIATIVE lists: putlistitems/id {/field} {/component} {/global} $$list

one.entity = "ONE"
$one$      = "$ONE$"
$$one      = "$$ONE"
$two$      = "$TWO$"
$$three    = "$$THREE"
$$list     = "one;$two$;$$three"

putlistitems/id/field $$list      ; "one=ONE;$two$=$TWO$;$$three="$$THREE"

putlistitems/id/component $$list  ; "one=$ONE$;$two$=$TWO$;$$three="$$THREE"

putlistitems/id/global $$list     ; "one=$$ONE;$two$=$TWO$;$$three="$$THREE"

If the Value part of a list item does not contain a dollar sign ($), the source is assumed to be a field unless one of the source switches /component or /global is present.

If the list is indexed it is converted to associative by adding in values from the current occurrence.

Note: /component is a synonym for /local, but it refers to the component variables, not local variables.

(a) putlistitems/id $$list, {SourceValue}, SourceRepresentation

(occurrence 1) num.calendar = "d1", day.calendar = "Monday" 
(occurrence 2) num.calendar = "d2", day.calendar = "Tuesday" 
(occurrence 3) num.calendar = "d3", day.calendar = "Wednesday"
setocc "calendar",1
putlistitems/id $$list, num.calendar, day.calendar ; $$list= "d1=Monday;d2=Tuesday;d3=Wednesday"

Note: Both SourceValue and SourceRepresentation are optional (but at least one must be specified), allowing either one or two fields to be copied.

(b) putlistitems/occ{/modonly} $$list, "entity"

This will result is a list of all field names and their associated values from the current occurrence of "entity". If the /modonly switch is used only the value from those fields where $fieldmod = 1 are inserted. Field ids which are unmodified are removed from the list.

You can use putlistitems/occ and getlistitems/occ to copy field values from one occurrence to another, or from one entity to another.

(c) putlistitems/id $$list

This will convert an indexed list of field names into an associative list of fieldnames and values, using the current occurrence as its source. If this is preceded by $keyfields('entity',1) this will effectively construct a list containing the primary key of the current occurrence.

For example, and entity called PERSON has a primary key called PERSON_ID. At run time the current occurrence of PERSON has a primary key value of 'AJM'. This information can be inserted into a list with the following commands:

$$pkey = $keyfields(PERSON, 1)   ; $$pkey contains 'person_id'
putlistitems/id $$pkey           ; $$pkey contains 'person_id=AJM'

This procedure works even if the primary key is made up of more than one field.

2.3 GETITEM - Copies an item to a designated variable or field

for INDEXED lists: getitem target, list, N

N must be an integer greater than zero, with '-1' used to signify the last entry. If the number supplied is currently greater than the number of items in the list nothing can be copied.

$$list = "Apple;Banana;Carrot"
getitem $1, $$list, 2     ; $1 = "Banana"
getitem $1, $$list, -1    ; $1 = "Carrot"

for ASSOCIATIVE lists: getitem/id{/case} target, list, index

$$list = "A=Apple;B=Banana;C=Carrot"
getitem/id $1, $$list, "B"    ; $1 = "Banana"
getitem/id $1, $$list, "C"    ; $1 = "Carrot"

By default the matching of Index with item values is not case-sensitive. If multiple entries exist, then the first one found in the list is retrieved. If a specific match is required the /case switch must be used.

getitem target, list, N

$$list = "A=Apple;B=Banana;C=Carrot"
getitem $1, $$list, 2    ; $1 = "B=Banana"

2.4 GETLISTITEMS - Copies the items from a list into a specified destination

for INDEXED lists: getlistitems $$list, field

$$list =  "Monday;Tuesday;Wednesday"
setocc "calendar",1
getlistitems $$list, day.calendar
(occurrence 1) day.calendar = "Monday"
(occurrence 2) day.calendar = "Tuesday"
(occurrence 3) day.calendar = "Wednesday"

for ASSOCIATIVE lists: getlistitems/id {/field} {/component} {/global} $$list

$$list = "one=ONE;$two$=$TWO$;$$three=$$THREE"
getlistitems/id/field $$list      ; one.entity = ONE
                                  ; $two$      = $TWO$
                                  ; $$three    = $$THREE

getlistitems/id/component $$list  ; $one$      = ONE
                                  ; $two$      = $TWO$
                                  ; $$three    = $$THREE

getlistitems/id/global $$list     ; $$one      = ONE
                                  ; $two$      = $TWO$
                                  ; $$three    = $$THREE

If the Value part of a list item does not contain a dollar sign ($), the target is assumed to be a field unless one of the target switches /component or /global is present.

Note: /component is a synonym for /local, but it refers to the component variables, not local variables.

(a) getlistitems/id $$list, {TargetValue}, TargetRepresentation

$$list =  "d1=Monday;d2=Tuesday;d3=Wednesday"
setocc "calendar",1
getlistitems/id $$list, num.calendar, day.calendar

(occurrence 1) num = "d1", day = "Monday"
(occurrence 1) num = "d2", day = "Tuesday"
(occurrence 3) num = "d3", day = "Wednesday"

Note: Both TargetValue and TargetRepresentation are optional (but at least one must be specified), allowing either one or two fields to be populated.

(b) getlistitems/occ $$list, "entity"

This fills in all specified fields in the current occurrence. You can use putlistitems/occ and getlistitems/occ to copy field values from one occurrence to another, or from one entity to another.

(c) getlistitems/occ/init $$list, "entity"

This does not set field or occurrence modification switches.

2.5 DELITEM - Delete an item from a list

for INDEXED lists: delitem list, N

N must be an integer greater than zero, with '-1' used to signify the last entry. If the number supplied is currently greater than the number of items in the list nothing can be deleted.

$$list = "Apple;Banana;Carrot"
delitem  $$list, 1     ; $$list = "Banana;Carrot"
delitem  $$list, -1    ; $$list = "Banana"

for ASSOCIATIVE lists: delitem/id{/case} list, index

$$list = "A=Apple;B=Banana;C=Carrot"
delitem/id  $$list, "A"    ; $$list = "B=Banana;C=Carrot"
delitem/id  $$list, "C"    ; $$list = "B=Banana"

By default the matching of Index with item values is not case-sensitive. If multiple entries exist, then the first one found in the list is deleted. If a specific match is required the /case switch must be used.

delitem list, N

$$list = "A=Apple;B=Banana;C=Carrot"
delitem $$list,2    ; deletes "B=Banana"

2.6 SORT/LIST - Sorts the items in a list

for INDEXED lists: sort/list field {,"D(escending) U(nique)"}

If the 'Unique' option is specified then duplicate entries will be removed.

$$list = "Mon;Tue;Wed;Thu;Fri" 
sort/list $$list
$$list = "Fri;Mon;Thu;Tue;Wed"

$$list = "Mon;Tue;Wed;Thu;Fri"
sort/list $$list,(d)
$$list = "Wed;Tue;Thu;Mon;Fri"

for ASSOCIATIVE lists: sort/list field {,"D(escending) U(nique)"}

If the list is associative items are sorted by the Representation (after the '='), not the Value (before the '=').

$$list = "1=Mon;2=Tue;3=Wed;4=Thu;5=Fri"
sort/list $$list
$$list = "5=Fri;1=Mon;4=Thu;2=Tue;3=Wed"

$$list = "1=Mon;2=Tue;3=Wed;4=Thu;5=Fri"
sort/list $$list,(d)
$$list = "3=Wed;2=Tue;4=Thu;1=Mon;5=Fri"

2.7 IDPART and VALUEPART

There may be an occasion when you are traversing the contents of an associative list and you do not know the current item's ID, therefore you cannot use getitem/id. In this situation the ID and VALUE portions of a list item can be extracted individually by using the functions IDPART and VALUEPART as follows:

$$list = "B=Tuesday"
$1 = idpart($$list)    ; $1 now contains "B"
$2 = valuepart($$list) ; $2 now contains "Tuesday"

2.8 ITEM item(id, associative_list)

This is a quick way to return the value that belongs to a given id from an associative list. This function acts in the same way as the getitem/id statement, but may also be used as part of an expression.

The following example shows how to find and directly pass an associative list item to an operation:

if (item("MEDIUM", myList) = "Book" )
   call selectBook(item("ISBN_NR", myList) )
endif

NOTE: If the item contains leading numeric characters then you should convert the expression into a string when using it as a parameter otherwise only the leading digits will be extracted. For example: "%%ITEM(id, list)%%%"

3. Lists within Lists

It is possible to construct a list that contains other lists. For example, you can construct a list containing all the values from an occurrence with the following command:

putlistitems/occ $$list1, "entity"

$$list1 will now contain "field1=value1;field2=value2;...;fieldn=valuen".

But supposing you wanted a list which contained the data from more than one occurrence. How could this be done? Consider the following command:

putitem/id $$list2, "occ/%%$curocc", $$list1

$$list2 will now contain "occ/1=field1=value1!;field2=value2!;...!;fieldn=valuen"

If you put this command into a loop you could construct a list containing the data from all available occurrences. You could use code similar to the following:

setocc "entity",1                                ; start at first occurrence
repeat
  putlistitems/occ $$list1,"entity"              ; put occurrence data into $$list1
  putitem/id $$list2, "occ/%%$curocc", $$list1   ; insert into $$list2 with a unique id
  setocc "entity",$curocc(entity)+1              ; look for next occurrence
until ($status < 1)                              ; until no more occurrences

Uniface accomplishes this task of embedding one list inside another by inserting a '!' character in front of each existing delimiter inside the list which is being embedded. This process of adding a '!' character to the delimiter will continue even if the list being embedded already contains an embedded list. Thus it is possible to construct a list which contains any number of lists within lists, and you will see several '!' characters in front of each <GOLD>semi-colon delimiter.

Note that in order to access the contents of an embedded list you must reverse the process, i.e. extract the contents of an embedded list into a non-embedded list until all the '!' characters have been stripped from the delimiters. This is where the VALUEPART command comes in handy.

getitem $$list3, $$list2, 1    ; extract the first 'occ/n=list' pair
$$list4 = valuepart($$list3)   ; extract 'list' into $$list4

At the end of these commands $$list4 will contain the data for a single occurrence which can then be put back by using getlistitems/occ.

To transfer all data into occurrences in the current form you could use code similar to the following:

while ($$list2 != "")                 ; while $$list2 is not empty
  getitem $$list3, $$list2, 1         ; extract the first 'occ=list' pair into $$list3
  delitem $$list2,1                   ; delete this pair
  $$list4 = valuepart($$list3)        ; extract 'list' into $$list4
  creocc "entity",-1                  ; create empty occurrence
  getlistitems/occ $$list4,"entity"   ; fill occurrence with data
endwhile

Tony Marston
24th October 2002

mailto:tony@marston-home.demon.co.uk
mailto:TonyMarston@hotmail.com
http://www.tonymarston.net

Amendment history:

3rd Nov 2002 Added section 2.8 ITEM (my thanks to Ulrich Merkel for pointing this function out to me).

counter