home *** CD-ROM | disk | FTP | other *** search
- Dynamically expanding arrays in Clipper
- By Dirk Lesko
-
- One problem many Clipper programers have bumped into is the need to
- declare an array without knowing what the final size of the array is
- going to be. Clipper does not let you dynamically increase or decrease
- the number of elements in an array. This however can be circumvented
- with a little bit of clever programming.
-
- The need to dynamically expand an array does not usually come up very
- often. There are however times when it could be most useful. One
- instance that comes to mind is when you want to fill an array with
- field information from a database so that a menu can be displayed via
- the achoice() function. This would not be a problem if you knew how many
- records you were going to have in the array. If you wanted to fill the
- array with only those records that matched a certain criteria however,
- then you would hit the 'I can't resize the array' syndrome. This comes
- up because you do not know before hand how many records are going to
- meet the search criteria.
-
- One solution is to declare an array that is equal to the amount of
- records returned by the lastrec() function. This of course works, but it
- usually ends up allocating hundreds and hundreds of extra elements that
- will go unused. It also proves difficult when using the len() function
- to get the length of the array for doing any kind of indexing into the
- array. This is where some smart programming can help.
-
- Let's say the example database is named CLIENTS, and it is indexed on
- last name to lname.ntx. Your application needs to display a menu with
- all occurences of 'SMITH' that have purchased more than $300.00 dollars
- worth of material from your company, and you only want to display those
- occurences where the criteria matches. One way of doing this is to use
- dbedit() and SET FILTER TO (If you have a few hours to spare on every
- lookup). Dbedit() also does not have source code so complex filter
- conditions are virtually impossible, and not to mention. Another method
- would be to use achoice() to display the list from an array, but first,
- you have to get the records that match the criteria into the array.
- Here's where you run into the wall. You don't know how many records are
- going to match the criteria, so you don't know how many elements you
- will need to declare.
-
- One obvious solution is to do two passes over the database, one to count
- records that match the criteria, and another to load the array with each
- record that matched. This approach is workable, but can be unwieldy if
- you have thousands of records in your DBF file. The second solution is
- to create an array and do one pass over the database file, filling
- elements with matched records as you go along, and expanding the array
- as you need extra elements. How you ask?, the answer is simple. The
- acopy() function in extend.lib can be used to make a temporary copy of
- the array holding the matched elements while you re-declare that array
- to it's new size. Heres how you accomplish that feat:
-
- 1) use CLIENTS index LNAME
- 2) seek "SMITH"
-
- 3) if (.not. found())
- return
- endif
-
- 4) x = 1
- 5) declare array1[x]
-
- 6) do while (LNAME = "SMITH")
-
- 7) if (SALES >= 300)
-
- 8) array1[x] = FNAME+LNAME+" Record #:"+str(recno())
-
- 9) declare array2[x]
- 10) acopy(array1,array2)
- 11) x = x+1
- 12) declare array1[x]
- 13) acopy(array1,array2)
-
- 14) endif
-
- 15) skip 1
-
- 16) enddo
-
- 17) choice = achoice(10,10,20,40,array2)
- 18) goto (val(right(array2[choice],8)))
-
- Whats happenning here? - The code we are interested in is inside the
- IF clause in the DO WHILE loop. I think you all understand the first
- five lines of code so I will go right to the important stuff. Line 6
- ensures that our loop ends when we are no longer on the 'SMITH' part of
- our index. Line 7 verifies that the record contains the information we
- are looking for. Line 8 copies the fields we want to display into
- element [x] of array1. Line 9 is where the expanding starts to take
- place. When a match is found, array2 is declared to contain [x] elements
- and the found data is copied into array2 from array1 in line 10. Now
- room must be made for an extra element in array1 so that another loop
- can be done. Line 11 increments our counter by one, and in line 12,
- array1 is re-declared to [x] number of elements. That gives us the extra
- element we need for another loop. Line 13 copies the contents of array2
- back into array1. At this point we have array2 which contains exactly
- the amount of elements that have matched the criteria, and array1
- contains the identical information plus an extra element to hold the
- next match. Line 14 is necessary to end the IF statement properly, and
- line 15 skips to the next record so that the whole process can be
- repeated.
-
- When the DO WHILE loop hits a name other than "SMITH", it drops down to
- the ENDDO statement. At this point, array2 contains the information we
- were looking for, and array1 has the same information plus one extra
- element that was put there 'just in case' a match was found. This array
- is no longer needed and can be released.
-
- Array2 contains exactly the number of elements that matched the search
- criteria so you can use FOR NEXT loops to index into it, and you can use
- achoice(), or any other array processing functions without having to
- worry about undefined elements. Notice in the example that the record
- number was appended to the end of each element for viewing, and for the
- GOTO after the achoice().
-
- This example is not intended to be a cure all for undefined elements, it
- does however show how a complex problem can be solved by creative
- programming. I would not recommend this approach if you are planning to
- build arrays of thousands of elements, due to the memory requirements of
- the acopy() function when working on very long arrays. I do recommend it
- highly if you need to display subsets of your data quickly without
- having to sort to complex database functions like dbedit(). I have found
- the speed of this approach to be entirely acceptable, and using
- achoice() instead of dbedit() usually results in faster screen displays
- as well.
-
- End.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-