Logo

Hacker’s Guide to Visual FoxPro
An irreverent look at how Visual FoxPro really works. Tells you the inside scoop on every command, function, property, event and method of Visual FoxPro.

ACOPY()

This function copies data from one array to another. Sounds simple, doesn’t it? But it’s not because ACOPY() doesn’t require a one-to-one correspondence between the two arrays. You can copy from any consecutive set of elements in the source array to consecutive positions anywhere in the destination array. You can even copy data from one place to another in the same array! As with many array functions, ACOPY() doesn’t care if the arrays you hand it are one-dimensional or two-dimensional; it treats them as one-dimensional. You supply a starting element and number of elements to copy from the source array and a starting element in the destination array.

Usage

nNumberCopied = ACOPY(SourceArray, DestArray [, nSourceStart
                       [, nSourceLength [, nDestStart ] ] ] )

Parameter

Value

Meaning

SourceArray

Array Name

The array containing the data to be copied. It can be one- or two-dimensional, but must exist.

DestArray

Array Name

The array to which data is copied. If it doesn't exist, Visual FoxPro creates it and makes it identical in dimensions to SourceArray. If it's too small to hold the number of elements copied, as many elements as fit are copied and then an error is generated. If DestArray is larger than the number of items copied, extra array elements are left alone. If DestArray has a different shape, elements are simply copied in order and the array is not reshaped to match the source.

nSourceStart

Numeric

The first element of SourceArray to be copied. This is always an element number, even if SourceArray is two-dimensional.

Omitted

All elements in SourceArray are copied.

nSourceLength

Numeric

The number of elements copied.

Omitted

All elements from nSourceStart to the end of the array are copied.

nDestStart

Numeric

The first element of DestArray where copied data is placed. Copying continues from that element toward the end of the array.

Omitted

Data is placed in DestArray beginning at the first element.

nNumberCopied

Numeric

The number of items copied.

As with other array functions, one of the tricks to understanding ACOPY() is the dual nature of FoxPro’s two-dimensional arrays. Much of FoxPro’s internal processing treats two-dimensional arrays as if they were one-dimensional, assigning each element an “element number” by going across each row in turn. (For example, the first item in the third row of a 3x2 array is element 5.) The two starting values in ACOPY(), nSourceStart and nDestStart, use this element notation.

Example

DIMENSION aSample[3,2]
* fill the array
FOR nCnt = 1 TO 6
    aSample[nCnt]=nCnt
ENDFOR
* copy the whole array
= ACOPY(aSample, aNew)
DISPLAY MEMORY LIKE A*  && shows new array
                        && identical to the original
* copy the last row
= ACOPY(aSample, aOneRow, 5)
DISPLAY MEMORY LIKE A*  && shows new array
                        && with one row filled in
* copy the second row
= ACOPY(aSample, aSecond, 3, 2)
DISPLAY MEMORY LIKE A*  && shows new array
                        && again one row filled in
* create a large, one-dimensional array
DIMENSION aLarge[50]
* copy the entire first array positioning at item 11
= ACOPY(aSample, aLarge, 1, 6, 11)
DISPLAY MEMORY LIKE A*  && show result
* now copy last 3 elements positioning at item 25
= ACOPY(aSample, aLarge, 4, 3, 25)
DISPLAY MEMORY  && show result

The examples show one really weird behavior of ACOPY(). When the destination array doesn't exist, FoxPro creates it and makes it match the source array. Sounds sensible, except for one thing. It does it even when copying only a subset of the element in the source. Take a look again at what happens when you copy only one row of the source to aOneRow—the new array is still 3x2 even though only the first row contains data. This behavior is documented, but we can't see why it was designed this way.

By now, you may have noticed that while ACOPY() is great for copying all the elements in an array or copying a group of rows, it’s totally useless if you want to copy one or more columns. That’s because of the way FoxPro handles two-dimensional arrays. Since we get asked about this a lot, here’s a function that copies the data from one or more columns. Unlike ACOPY(), you do have to create the second array before you call the function in order to pass it (by reference) as a parameter. If the array is too small, aColCopy() redimensions it to the appropriate size to hold the copied data in the specified position. (If you pass a variable that’s not an array, it gets turned into an appropriate array.) aColCopy() does require you to pass the number of the first column to copy since there’s no reason to use it to copy a whole array.

FUNCTION aColCopy
* Copy one or more columns from a source array to a destination array.
 
LPARAMETERS aSource, aDest, nStartCol, nColCount, nDestCol
  * aSource = array to be copied
  * aDest = destination array
  * nStartCol = first column to copy - required
  * nColCount = number of columns to copy - optional. 
  *             Go to end of aSource, if omitted
  * nDestCol = first column of destination to receive copied data - optional
  *            1 if omitted
 
LOCAL nRetVal,nOldRows,nOldCols,nOldCount,nItem
  * nRetVal = return value, number of columns copied.
  *         = -1, if can't copy
    
* Check source array
IF TYPE("aSource[1]")="U" OR ALEN(aSource,2)=0
  * not a 2-d array, can't do it
  RETURN -1
ENDIF
 
* Check for starting column
IF TYPE("nStartCol")<>"N"
  RETURN -1
ENDIF
 
* Check number of columns. Compute if necessary
IF TYPE("nColCount")<>"N" OR nStartCol+nColCount>ALEN(aSource,2)
  nColCount=ALEN(aSource,2)-nStartCol+1
ENDIF
 
* Check destination column.
IF TYPE("nDestCol")<>"N"
  nDestCol=1
ENDIF
  
* Check destination array for size. It must exist to be passed in.
* First, make sure it's an array.
* Then, see if it's shaped right for all the data.
* Two cases - if enough cols, but not enough rows, can just add
* If not enough cols, have to move data around.
IF TYPE("aDest[1]")="U"
  DIMENSION aDest[ALEN(aSource,1),nColCount+nDestCol-1]
ELSE
  IF ALEN(aDest,2)>=nColCount+nDestCol-1  && enough columns
    IF ALEN(aDest,1)<ALEN(aSource,1) && not enough rows
      DIMENSION aDest[ALEN(aSource,1),ALEN(aDest,2)] && add some
    ENDIF
  ELSE
    * now the hard one
    * not enough columns, so need to add more (and maybe rows, too)
    nOldRows=ALEN(aDest,1)
    nOldCols=ALEN(aDest,2)
    nOldCount=ALEN(aDest)
    DIMENSION aDest[MAX(nOldRows,ALEN(aSource,1)),nColCount+nDestCol-1]
    
    * DIMENSION doesn't preserve data location, so we need to adjust the data
    * We go backward from the end of the array toward the front, moving data 
    * down, so we don't overwrite any data by accident
 
    FOR nItem=nOldCount TO 2 STEP -1
      * Use new item number and old dimensions to determine
      * new item number for each element
      IF nOldCols<>0
        nRow=CEILING(nItem/nOldCols)
        nCol=MOD(nItem,nOldCols)
        IF nCol=0
          nCol=1
        ENDIF
      ELSE
        nRow=nItem
        nCol=1
      ENDIF
    
      aDest[nRow,nCol]=aDest[nItem]
    ENDFOR
  ENDIF
ENDIF
 
* finally ready to start copying
FOR nCol=1 TO nColCount
  FOR nRow=1 TO ALEN(aSource,1)
    aDest[nRow,nDestCol+nCol-1]=aSource[nRow,nStartCol+nCol-1]
  ENDFOR
ENDFOR
 
RETURN nColCount*ALEN(aSource,1)

For example, suppose you want just the filenames from an ADIR() listing. You could use aColCopy() like this:

=ADIR(aFiles)
DIMENSION aNames[1]
=aColCopy(@aFiles,@aNames,1,1)

See Also

ADel(), AElement(), AIns(), Array Manipulation, ASubscript(), Copy To Array, Dimension