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.