Uniform Arrays

 

If a multi-dimension array

–          have a fixed number elements in each dimension and

–          each column contains the same type of information for each row in array

called “uniform”.

This structure is similar to a table structure.

Built-in functions DBSTRUCT() and DIRECTORY() produces uniform arrays.

Array produced by DBSTRUCT() will have field count  in size and the structure of an array is:

Field Name  C
Field Type  C
Field Width N
Field Dec   N

And DIRECTORY() function produces an array with elements as file count and with this structure:

File Name        C
File Size        N
File Date        D
File Time        C
File Attributes  C

Building, maintaining and using those arrays is simple as possible.

Let’s look at a sample .prg:

-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._
#include "directry.ch"
#include "dbstruct.ch"
PROCEDURE Main()
  SET DATE GERM
  SET CENT ON
 
  ?
  ? "Uniform arrays :" 
  ?
 
  ?
  ? " Directory file list :"
  ?
 
  FileList()
 
  ?
  ? " Table structure list :"
  ?
  IF MakUseTable()
     DispStru() 
  ELSE
     ? "Couldn't USE or Make th table." 
  ENDIF
 
  ?
  @ MAXROW(), 0
  WAIT "EOF UF_Arrays.prg"
 
RETURN // UF_Arrays.Main
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._

PROCEDURE FileList()
  LOCAL aFList := DIRECTORY( "C:\Harbour\*.*" )
  LOCAL a1File
 
  FOR EACH a1File IN aFList 
     ? SPACE( 4 ),;
       PAD( a1File[ F_NAME ], 13 ),; /* File name */ 
       TRAN( a1File[ F_SIZE ], "999,999,999" ),; /* File size */
       a1File[ F_DATE ],; /* File date */
       a1File[ F_TIME ],; /* File time */
       a1File[ F_ATTR ] /* File attribute */
 NEXT
 
RETURN // FileList()
 
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._
FUNCTION MakUseTable() 
 
  LOCAL cTablName := "CUSTOMER.DBF"
  LOCAL lRetval, aStru 
 
  IF FILE( cTablName ) 
     USE (cTablName)
  ELSE
     aStru := { { "CUST_ID",    "C",  5, 0 },;
                { "CUST_NAME",  "C", 10, 0 },;
                { "CUST_SNAM",  "C", 10, 0 },;
                { "CUST_FDAT",  "D",  8, 0 },;
                { "CUST_ACTV",  "L",  1, 0 },;
                { "CUST_BLNCE", "N", 11, 2 } }
    * 
    * 5-th parameter of DBCREATE() is alias - 
    * if not given then WA is open without alias 
    *                              ^^^^^^^^^^^^^ 
    DBCREATE( cTablName, aStru, , .F., "CUSTOMER" ) 
 ENDIF 
 
 lRetval := ( ALIAS() == "CUSTOMER" )
 
RETURN lRetval // MakUseTable()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._
PROCEDURE DispStru()
   LOCAL nTotal := 1
 
   IF SELECT() > 0
      aStructur := DBSTRUCT()
      ? SPACE( 4 ), "No: Field Name Type Width Dec"
      ? SPACE( 4 ), "--- ---------- ---- ----- ---"
      AEVAL( aStructur, { | aF1, nFNo | ;
             QOUT( SPACE( 4 ),; // Left Marj 
                   PADL( nFNo, 3 ),; // Field No
                   PADR( aF1[ DBS_NAME ], 11 ),; // Field Name
                   PADC( aF1[ DBS_TYPE ], 4 ),; // Field Type
                   PADL( aF1[ DBS_LEN ], 4 ),; // Field Len
                   PADL( aF1[ DBS_DEC ], 3 )),; // Field Dec 
             nTotal += aF1[ 3 ] } )
 
      ? SPACE( 4 ), "--- ---------- ---- ----- ---"
      ? SPACE( 4 ), "** Total ** ", TRAN( nTotal, "9,999" )
   ELSE
      ? "Current work area is empty"
   ENDIF 
 
RETURN // DispStru()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._
 
UF_Arrays

Multi-Dimensional Arrays

Since elements of an array may be any data type, may be also an array. And when any element of an array is an array, it called multi-dimensional array.

As cited in array-basics, multi-dimensional array too subscripted like single dimension array. Only difference is specifying separate subscript for each dimension. This is correct for both defining and retrieving.

For example:

aArray2D := ARRAY( 2, 3 )
? HB_ValToExp( aArray2D )  // {{NIL, NIL, NIL}, {NIL, NIL, NIL}}
aArray2D := { { 1, 2, 3 }, { 4, 5, 6 } }
? HB_ValToExp( aArray2D )  // {{1, 2, 3}, {4, 5, 6}}

A multi-dim array doesn’t have to be “uniform”; any element of an array may be an array :

aArrMDim := { 1, "One", DATE(), .T., { 1, 2, 3, 4 } }
? HB_ValToExp(aArrMDim )  // {1, "One", 0d20130108, .T., {1, 2, 3, 4}}

In such case we can’t talk about aArrMDim[ 1, 1 ], aArrMDim[ 3, 2 ], … only aArrMDim[ 5, 1 ], aArrMDim[ 5, 2 ], …

Since fifth element of aArrMDim is a array, we can any array operation on it:

ASIZE( aArrMDim[ 5 ], 2 )
? HB_ValToExp(aArrMDim[ 5 ] )  // {1, 2}
? LEN( aArrMDim[ 5 ] )         // 2

Language doesn’t enforce any rules on what can be stored in an array; this level of control must be implemented programmatically.

The fact that you can assign an array ( or any data type else ) to an existing array element allows you to dynamically change structure of an array. For example, after an array built there is nothing prevent you from doing something like this:

aArrMDim[ 2 ] := { 2.5, 5.75, 3.14 }

This statement change second element of array from string to array. Now, aArrMDim[ 2, 3 ], aArrMDim[ 5, 3 ] are valid, but aArrMDim[ 3, 1 ] result a runtime error.

This feature of assigning an array reference to an array element can come in handy in lot of applications. The thing to remember is that you as the programmer must exercise whatever control you think is necessary for storing and addressing array elements. You cannot make any assumption about the structure of an array unless you enforce that structure.

PROCEDURE Main()
   ?
   ? "Multi-dim arrays :"
   ?

   aArray2D := ARRAY( 2, 3 )
   ShowValue( aArray2D, "two-dim"  )
   Show2DArr( aArray2D )

   aArray2D := { { 1, 2, 3 }, { 4, 5, 6 } }
   ShowValue( aArray2D, "two-dim"  )
   Show2DArr( aArray2D )

   aArrMDim := { 1, "One", DATE(), .T., { 1, 2, 3, 4 } }
   ShowValue( aArrMDim, "Multi-dim"  )

   ? "Before ASIZE() :", LEN( aArrMDim )
   ASIZE( aArrMDim[ 5 ], 2 )
   ShowValue( aArrMDim, "Shrinked subarray"  )
   ? "After ASIZE() :", LEN( aArrMDim )
   ? HB_ValToExp(aArrMDim[ 5 ] )  // {1, 2}
   ? LEN( aArrMDim[ 5 ] ) //
   ShowValue( aArrMDim, "Shrinked"  )

   ?
   @ MAXROW(), 0
   WAIT "EOF MD_Arrays.prg"

RETURN // MD_Arrays.Main

*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._

PROCEDURE ShowValue( xValue, cComment )
   LOCAL cType := VALTYPE( xValue )
   ? cComment, ":",;
     ValType( xValue ),;
     IF( cType $ "CMA", HB_ValToExp( LEN( xValue ) ),''),;
     HB_ValToExp( xValue )
   ?
RETURN // ShowValue()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._
PROCEDURE Show2DArr( a2DArr )
   ?
   FOR nD1 := 1 TO 2
      FOR nD2 := 1 TO 3
         ?? a2DArr[ nD1, nD2 ]
      NEXT
      ?
   NEXT
RETURN // Show2DArr()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._

 MD_Arrays