How I have joined …

How I have joined HMG Family – A Story

It was in the year 1990, I had seen a computer, first time in my life.

I was a commerce student, studying in a 100+ years old school in my home town Sivakasi (famous for fireworks ), Tamilnadu, Southern part of India. I was studying +2 (12th and final year in school). Once, the exams were over, it was time to join a College. Not to waste the exam holidays, one of my friends had asked me to accompany him for a part time computer course in a near by Polytechnic College. I told him, “Ok”. We joined that course which was for six months, conducted on Saturdays and Sundays. I studied some basics about computers and languages like BASIC, Pascal, COBOL and an introduction to Lotus 1-2-3, Wordstar and dBase III Plus.

After that I had joined a college and my main subject was Commerce. After finishing my Under Graduation degree in the year 1993, I had joined Chartered Accountancy Course. And, once I had finished this course in the year 1996, I joined my brother to manage our family business.

Even though there was a computer in our office, I was not allowed to touch that.  The reason was, at that time, computers (AT 386) were costlier and one cannot take the risk of losing precious data and computers were operated only by computer professionals.

In the year 1997, I had purchased a computer on my own, and started implementing something which I had studied some seven years back.

I had studied under the DOS environment and I had got Windows ’95 in my new system as my operating environment. Even though it was easy to operate, I could not do any programming. I had so much of works before hand and I was involved in them for about 1 year. It was a Costing project in Excel with about 150 sheets, everything interlinked. It was a nice experience. I had done some macro programming in Excel for introducing thousand’s comma according to Indian tradition.

In the year 1998, the computer professional who had programmed for the accounting, invoice processing and payroll processing for our business firm had gone for a better job. We could not create new reports according to the requirements and we had to play only with the old options. Only at that time, I had realized about the importance of Database programming. Having the knowledge of DBase III+ programming, I had started to write small utilities to take self configured reports from the existing tables. I had the reference of the source codes for the existing software. It was done in Clipper Summer ’87 version.

There is a saying, ‘Necessity is the mother of invention’. Yes. Necessity had driven me to do more and more programming. Actually, I had started staring huge prg files with various do while .not. eof() loops and virtually indefinite nested if … endif conditions. Once understood, I had the confidence of creating bigger software too. So, in addition to the accounting software, I had created order processing and inventory maintenance and integrated the same with the existing project.

It was in the mid, 1998, I could get an internet connection with a dial up modem. In the mean time, I had a dream of using GUI in my programming. I had tried Visual Basic. I could not link with my existing dbf tables and dropped. In the beginning, I misunderstood about OOPS and GUI. Actually I had an allergy over this OOPS, I don’t know why even now.  So, I had abandoned my dream  of GUI because of OOPS.

In the year 1999, while searching the net, I had come across a site called Oasis, a site which had shared source code/libraries for Clipper and many utilities. From that site, I had got an excellent library called Super. It had some assembly language routines too to capture mouse gestures and enabled Clipper code to be ‘RAT’ified. Apart from this mouse functionality, the library had so many small utilities like sorting multiple arrays, finding out Day of the week like that. Actually I had realized about the advantages of Open Source on seeing that library source codes. I had studied in depth, and I had known about optimization of codes, effective memory management, different routes to a same destination in programming etc.

With the SuperLib, I had changed all my projects to be mouse enabled, there by satisfying half of my dreams. However, I could not stop dreaming about creating a full fledged Windows program eliminating the dark DOS command box. As new versions of Windows came in the market, I was afraid that, one day there won’t be this DOS Command Box and all my programs would not be useful at all.

I was, why was? even now am, very fond of Open Source. In my system I don’t use any copyright protected software except the Windows Operating System. I would list all the open source software I use in a separate thread for all my requirements.

I had found out Harbour from the Oasis site I had already mentioned. I had tested a lot but could not use it because of SuperLib, as it could not be linked with Harbour.

I was in a confused state. I liked Harbour a lot because of its open nature. I could not abandon SuperLib too, since all of my projects were linked with this library. So, I was desperately searching for a GUI library (at least mouse enabled for Harbour).

On one fine day in my life, my search was fruitful.

I could even remember the date. It was 4th of October, 2002, I had downloaded Harbour MiniGUI and could create a window with my limited xBase programming knowledge. Hurray! I could not describe my feelings in words!

Immediately on compiling my hello world prg, I had commented in the yahoo group from where I had downloaded the 0.40 version of the library with the following words,

Dear Roberto,

It is wonderful to use your Harbour Minigui Library, an open
source library for Harbour.

Thank you very much for your sincere efforts.

I would like to make a small suggestion regarding text boxes.

Shall you please make provision for right aligning the text boxes
for numeric fields?

Thank you once again.

srgiri

From that day, Harbour MiniGUI page was literally my home page.  I read all the messages from various users of the library. It helped me a lot to understand programming, inside out of the library, even about calling C API.

I had happily started to convert all of my projects to HMG. As the product matured, all my projects were fully converted to HMG and I had seen my dream come true.

Most of my programs are used in-house. However, I had created software for many of my friends. I had earned some money from my programs too. As you know, my income is not based on programming, since basically I am a Chartered Accountant and managing business.

I wish to list some of my major HMG projects,

  1. PyroERP (an ERP software for manufacturing companies with accounting, inventory, order processing,payroll)
  2. PyroBase (a License maintenance software given to Explosives department, Government of India)
  3. FACE (Financial Accounting under Computer Environment)
  4. Interest(ing) Calculator (Calculate interesting part of interest for loans and deposits) – An open source product, hosted in Sourceforge.net.
  5. FBT Reference (Fringe Benefit Tax referencer)
  6. Phataka (A Cultural Event maintenance software used by local Rotary Club)
  7. DBU
  8. GSM Calculator

There are many tiny projects too. I am not listing them to show my talents. It shows the simplicity of HMG and my craze on HMG.

It is my nature, to escape from any politics. I wish to be good for all. During July 2005, when Roberto had decided to move on MingW and introduced HMG, MiniGUI Extended product had also born. I didn’t tell a word about anything in the group. I believe in Karma and thought, “if this happens, this is also for the good”.

I had actively participated in the yahoo group up to April, 2006 and kept silence after that.

However, I used HMG in a full fledged manner and I liked MingW version as Roberto distributed them. This is mainly because of the single installation of the whole thing (ie., MiniGUI library + Harbour Compiler + C Compiler) and full utilization of Open Source Software.

All the software projects listed above where developed by me during the period from 2004 till now. I too had contributed some parts like drawing pie graph, alternative syntax (with the valuable guidance of Roberto) (now Roberto had revamped in a better way!), Grid2Print which Roberto has accepted kindly.

Can you believe that this forum had been created in just 2 days time? On 29th of July 2008, I had asked Roberto, breaking my long silence, by an email about the need for a forum exclusively for HMG. Roberto also liked and generously accepted to guide and participate in the forum. On that day itself, I registered this domain hmgforum.com and installed PHPBB forum software and on 1st of August 2008, the forum was officially opened.

I am so HAPPY and PROUD to be part of the HMG family.

Thus, this story has a happy ending.

Sri Rathinagiri
Sivakasi, India

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Courtesy of author, this article borrowed from here.

IF

IF

Execute one of several alternative blocks of statements

Syntax

       IF <lCondition1>
          <statements>...
       [ELSEIF <lCondition2>]
          <statements>...
       [ELSE]
          <statements>...
       END[IF]

Arguments

<lCondition> is a logical control expression. If it evaluates to true (.T.), all following statements are executed until an ELSEIF, ELSE, or ENDIF is encountered.

ELSEIF <lCondition> identifies statements to execute if the associated condition evaluates to true (.T.) and all preceding IF or ELSEIF conditions evaluate to false (.F.). Any number of ELSEIF statements can be specified within the same IF…ENDIF control structure.

ELSE identifies statements to execute if the IF and all preceding ELSEIF conditions evaluate to false (.F.).

Description

The IF control structure works by branching execution to statements following the first true (.T.) evaluation of the IF or any ELSEIF condition. Execution then continues until the next ELSEIF, ELSE, or ENDIF is encountered whereupon execution branches to the first statement following the ENDIF.

If no condition evaluates to true (.T.), control passes to the first statement following the ELSE statement. If an ELSE statement is not specified, control branches to the first statement following the ENDIF statement.

IF…ENDIF structures may be nested within IF…ENDIF structures and other control structure commands. These structures, however, must be nested properly. The IF…ELSEIF…ENDIF form of the IF construct is identical to DO CASE…ENDCASE. There is no specific advantage of one syntax over the other.

The IF construct is also similar to the IF() function which can be used within expressions.

Examples

       .  This example evaluates a number of conditions using an
       IF...ELSEIF...ENDIF construct:
       LOCAL nNumber := 0
       //
       IF nNumber < 50
          ? "Less than 50"
       ELSEIF nNumber = 50
          ? "Is equal to 50"
       ELSE
          ? "Greater than 50"
       ENDIF

Seealso

BEGIN SEQUENCE, DO CASE, DO WHILE, FOR, IF()

FOR…NEXT

FOR…NEXT

Execute a block of statements a specified number of times

Syntax

       FOR <idCounter> := <nStart> TO <nEnd> [STEP <nIncrement>]
           <statements>...
           [EXIT]
           <statements>...
           [LOOP]
       NEXT

Arguments

<idCounter> is the name of the loop control or counter variable. If the specified <idCounter> is not visible or does not exist, a private variable is created.

<nStart> is the initial value assigned to <idCounter>. If <nIncrement> is negative, <nStart> must be less than <nEnd>.

TO <nEnd> defines the final value of <idCounter>. If <nIncrement> is negative, <nStart> must be greater than <nEnd>; otherwise, <nStart> must be less than <nEnd>.

STEP <nIncrement> defines the amount <idCounter> is changed for each iteration of the loop. <nIncrement> can be either positive or negative. If the STEP clause is not specified, <idCounter> is incremented by one for each iteration of the loop.

EXIT unconditionally branches control from within a FOR…NEXT construct to the statement immediately following the nearest NEXT statement.

LOOP branches control to the most recently executed FOR or DO WHILE statement.

Description

FOR…NEXT is a control structure that executes a block of statements a specified number of times. The control structure loops from the initial value of <idCounter> to the boundary specified by <nEnd>, moving through the range of values of the control variable for an increment specified by <nIncrement>. All expressions in the FOR statement are reevaluated for each iteration of the loop. The <nStart> and <nEnd> expressions, therefore, can be changed as the control structure operates.

A FOR loop operates until <idCounter> is greater than <nEnd> or an EXIT statement is encountered. Control then branches to the statement following the corresponding NEXT statement. If a LOOP statement is encountered, control branches back to the current FOR statement.

If <nIncrement> is a negative value, <idCounter> is decremented rather than incremented. The FOR loop, however, continues until <idCounter> is less than <nEnd>. This means that <nEnd> must be less than <nStart> when the FOR loop begins.

FOR loops are useful for traversing arrays where <idCounter> is used as the array subscript. See the example below.

FOR…NEXT constructs may be nested within any other control structures to any depth. The only requirement is that each control structure is properly nested.

Examples

       .  This example traverses an array in ascending order:
       nLenArray := LEN(aArray)
       FOR i := 1 TO nLenArray
           <statements>...
       NEXT
       .  To traverse an array in descending order:
       nLenArray := LEN(aArray)
       FOR i := nLenArray TO 1 STEP -1
          <statements>...
       NEXT

Seealso

AEVAL(), BEGIN SEQUENCE, DO CASE, DO WHILE, IF, IF()

Array Basics

    An array is a distinct data type which may contains multiple data items under same name.  Data items stored in an array referred as an “element” and can be any data type. An individual element of array referenced by array name and position number of element as an integer in array, called “index” or “subscript”.

 Defining / Building:

    An array is a variable and like all variables has “scope”; arrays can be defined PRIVATE, PUBLIC and LOCAL as well as STATIC.

   Building an array is quite simple: for example to define an array named “aColors” with 5 elements we can use a statement like this:

LOCAL aColors[ 5 ]

or

LOCAL aColors := ARRAY( 5 )

or

LOCAL  aColors := {  , , , , }

Results of these three methods are exactly same; we can inspect easily:

? ValType( aColors )      // A

? LEN( aColors )          // 5
? HB_ValToExp( aColors )  // {NIL, NIL, NIL, NIL, NIL}

NIL is a special data type with meaning “not defined”.

We can define an array with initial value(s)

aColors := {  “green”, ”yellow” ,  “red”,  “black”, “white” }

or assign values after defined:

aColors[ 1 ] := ”green”
aColors[ 2 ] := ”yellow”
aColors[ 3 ] := ”red”
aColors[ 4 ] := ”black”
aColors[ 5 ] := ”white”
? HB_ValToExp( aColors ) // {"green", "yellow", "red", "black", "white"}

Retrieve:

As seen in our first array statement

LOCAL aColors[ 5 ]

used a special sign square brackets as “Array element indicator” ( used also as string delimiter ).

As cited at the beginning, an individual element of array is referenced by array name and position number of element as an integer (enclosed by square brackets) in array, called “index” or “subscript”, and this notation called “subscripting”.

Note that subscribing begins with one.

To specify more than one subscript ( i.e. when using multi-dimensional arrays), you can either enclose each subscript in a separate set of square brackets, or separate the subscripts with commas and enclose the list in square brackets. For example, if aArray is a two dimensional array, the following statements both addresses the second column element of tenth row:

aArray[ 10 ][ 2]
aArray[ 10, 2]

Of course it’s illegal to address an element that is outside of the boundaries of the array (lesser than one or greater than array size / length; one is also illegal for an empty array). Attempting to do so will result in a runtime error.

When making reference to an array element using a subscript, you are actually applying the subscript operator ([]) to an array expression, not only an array identifier (array variable name). An array expression is any valid expression that evaluate to an array. This includes function calls, variable references, subscripting operations, or any other expression that evaluate to an array. For example, the following are all valid:

 { “a”, “b”, “c” }[ 2 ]

x[ 2 ]
ARRAY(3)[ 2 ]
&(<macro expression>)[ 2 ]
(<complex expression>)[ 2 ]

Syntax :

 <aArrayName> [ <nSubscript> ]

<nSubscript> is integer value and indicate sequence number of element into this array.

With this way we can also traverse an array:

FOR nColor := 1 TO LEN( aColors )
     ? aColors[ nColor ]
NEXT nColor

In fact, with the other FOR loop, traversing an array doesn’t require subscripting:

cColor := “” // FOR EACH loop require a predefined loop element

FOR EACH cColor IN aColors
  ? cColor
NEXT nColor

Elements of an array may be different data type; thus arrays called as “ragged” arrays in Clipper language.

aRagged := { 1, "One", DATE(),  .T. } 
FOR nIndex := 1 TO LEN( aRagged )
   x1Elem := aRagged[ nIndex  ]
   ? VALTYPE( x1Elem ), x1Elem
NEXT nIndex

A build-in array function AEVAL() can be use instead of a loop :

AEVAL( aRagged, { | x1 | QOUT(x1 ) } )

 

Adding one element to end of an array:

The architect of array is quite versatile. Array may change ( in size and element values ) dynamically at run time.

 AADD() function can be used for add a new element to the end of an array

AADD(<aTarget>, <expValue>) --> Value

<aTarget> is the array to which a new element is to be added.

<expValue> is the value assigned to the new element.

AADD() is an array function that increases the actual length of the target array by one.  The newly created array element is assigned the value specified by <expValue>.

AADD() is used to dynamically grow an array.  It is useful for building dynamic lists or queues.

For example an array may build empty and later add element(s) to it :

aColors := {}
AADD( aColors, ”green” )
AADD( aColors, ”yellow” )
AADD( aColors, ”red” )
AADD( aColors, ”black” )
AADD( aColors, ”white” )
? HB_ValToExp( aColors ) // {"green", "yellow", "red", "black", "white"}
Inserting one element to an array:

AINS() function can be use for insert a NIL element into an array

AINS (<aTarget>, <nPosition>) --> aTarget

<aTarget> is the array into which a new element will be inserted.

<nPosition> is the position at which the new element will be  inserted.

AINS() is an array function that inserts a new element into a specified array.  The newly inserted element is NIL data type until a new value is assigned to it.  After the insertion, the last element in the array is discarded, and all elements after the new element are shifted down one position.

For a lossless AINS() ( HL_AINS() ) look at attached .prg.

Deleting one element from an array:

ADEL(<aTarget>, <nPosition>) --> aTarget

ADEL() is an array function that deletes an element from an array.  The content of the specified array element is lost, and all elements from that position to the end of the array are shifted up one element.  The last element in the array becomes NIL. So, ADEL() function doesn’t change size of array.

For an another ADEL()  ( HL_ADEL() ) look at attached .prg.

Resizing:

 ASIZE() function can be use for grow or shrink, that is changing size of an array.

ASIZE( <aTarget>, <nLength>) --> aTarget

<aTarget> is the array to grow or shrink.

<nLength> is the new size of the array.

 

     ASIZE() is an array function that changes the actual length of the   <aTarget> array.  The array is shortened or lengthened to match the specified length.  If the array is shortened, elements at the end of the array are lost.  If the array is lengthened, new elements are added to the end of the array and assigned NIL.

     ASIZE() is similar to AADD() which adds a single new element to the end   of an array and optionally assigns a new value at the same time.  Note that ASIZE() is different from AINS() and ADEL(), which do not actually      change the array’s length.

Assigning a fixed value to all elements of an array:

Changing values of all element of an array can not be accomplish by a simple assign statement. For example:

aTest := ARRAY( 3 )
aTest := 1

change type of aTest from array to numeric with a value 1.

Instead, AFILL() function gives a short way to fill an array with a fixed value.

AFILL() :  Fill an array with a specified value

Syntax :

AFILL(<aTarget>, <expValue>,
    [<nStart>], [<nCount>]) --> aTarget
    <aTarget> is the array to be filled.

<expValue> is the value to be placed in each array element.  It can be an expression of any valid data type.

<nStart> is the position of the first element to be filled.  If this argument is omitted, the default value is one.

<nCount> is the number of elements to be filled starting with  element <nStart>.  If this argument is omitted, elements are filled from the starting element position to the end of the array.

Code evaluation on an array:

AEVAL()

Execute a code block for each element in an array

Syntax:

AEVAL(<aArray>, <bBlock>,
    [<nStart>], [<nCount>]) --> aArray

Arguments:

<aArray> is the array to traverse.

<bBlock> is a code block to execute for each element encountered.

<nStart> is the starting element.  If not specified, the default is  element one.

<nCount> is the number of elements to process from <nStart>.  If not specified, the default is all elements to the end of the array.

Returns:

     AEVAL() returns a reference to <aArray>.

Description:

AEVAL() is an array function that evaluates a code block once for each element of an array, passing the element value and the element index as  block parameters.  The return value of the block is ignored.  All      elements in <aArray> are processed unless either the <nStart> or the  <nCount> argument is specified.

AEVAL() makes no assumptions about the contents of the array elements it is passing to the block.  It is assumed that the supplied block knows  what type of data will be in each element.

AEVAL() is similar to DBEVAL() which applies a block to each record of a  database file.  Like DBEVAL(), AEVAL() can be used as a primitive for  the construction of iteration commands for both simple and complex array  structures.

Refer to the Code Blocks section in the “Basic Concepts” chapter of the   Programming and Utilities Guide for more information on the theory and syntax of code blocks.

Examples :

This example uses AEVAL() to display an array of file names  and file sizes returned from the DIRECTORY() function:

#include “Directry.ch”

//

LOCAL aFiles := DIRECTORY(“*.dbf”), nTotal := 0

AEVAL(aFiles, { | aDbfFile |;
     QOUT( PADR(aDbfFile[F_NAME], 10), aDbfFile[F_SIZE]),;
     nTotal += aDbfFile[F_SIZE])} )
//
?
? "Total Bytes:", nTotal

This example uses AEVAL() to build a list consisting of  selected items from a multi-dimensional array:

#include "Directry.ch"
//
LOCAL aFiles := DIRECTORY("*.dbf"), aNames := {}
AEVAL(aFiles, { | file | AADD(aNames, file[F_NAME]) } )

This example changes the contents of the array element depending on a condition.  Notice the use of the codeblock  parameters:

 

LOCAL aArray[6]
AFILL(aArray,"old")
AEVAL(aArray,;
{|cValue,nIndex| IF( cValue == "old",;
aArray[nIndex] := "new",)})

Searching a value into an array :

ASCAN() : Scan an array for a value or until a block returns true (.T.)

Syntax:

ASCAN(<aTarget>, <expSearch>,
       [<nStart>], [<nCount>]) --> nStoppedAt

 Arguments:

<aTarget> is the array to be scanned.

<expSearch> is either a simple value to scan for, or a code block.   If <expSearch> is a simple value it can be character, date, logical, or  numeric type.

<nStart> is the starting element of the scan.  If this argument is not specified, the default starting position is one.

<nCount> is the number of elements to scan from the starting  position.  If this argument is not specified, all elements from the  starting element to the end of the array are scanned.

 Returns:

ASCAN() returns a numeric value representing the array position of the last element scanned.  If <expSearch> is a simple value, ASCAN() returns the position of the first matching element, or zero if a match is not  found.  If <expSearch> is a code block, ASCAN() returns the position of the element where the block returned true (.T.).

 Description:

ASCAN() is an array function that scans an array for a specified value and operates like SEEK when searching for a simple value.  The  <expSearch> value is compared to the target array element beginning with  the leftmost character in the target element and proceeding until there are no more characters left in <expSearch>.  If there is no match,  ASCAN() proceeds to the next element in the array.

Since ASCAN() uses the equal operator (=) for comparisons, it is  sensitive to the status of EXACT.  If EXACT is ON, the target array  element must be exactly equal to the result of <expSearch> to match.

If the <expSearch> argument is a code block, ASCAN() scans the <aTarget>   array executing the block for each element accessed.  As each element is encountered, ASCAN() passes the element’s value as an argument to the code block, and then performs an EVAL() on the block.  The scanning operation stops when the code block returns true (.T.), or ASCAN()  reaches the last element in the array.

  Examples:

This example demonstrates scanning a three-element array using simple values and a code block as search criteria.  The code block criteria show how to perform a case-insensitive search:

aArray := { "Tom", "Mary", "Sue" }
? ASCAN(aArray, "Mary")               // Result: 2
? ASCAN(aArray, "mary")               // Result: 0
//
? ASCAN(aArray, { |x| UPPER(x) == "MARY" }) // Result: 2

This example demonstrates scanning for multiple instances of a search argument after a match is found:

LOCAL aArray := { "Tom", "Mary", "Sue","Mary" },; 
      nStart := 1
//
// Get last array element position
nAtEnd := LEN(aArray)
DO WHILE (nPos := ASCAN(aArray, "Mary", nStart)) > 0
   ? nPos, aArray[nPos]
   //
   // Get new starting position and test
   // boundary condition
   IF (nStart := ++nPos) > nAtEnd
      EXIT
   ENDIF
ENDDO

This example scans a two-dimensional array using a code block.  Note that the parameter aVal in the code block is an array:

LOCAL aArr:={}
CLS
AADD(aArr,{"one","two"})
AADD(aArr,{"three","four"})
AADD(aArr,{"five","six"})
? ASCAN(aArr, {|aVal| aVal[2] == "four"})         // Returns 2

Sorting an array:

ASORT() :  Sort an array

Syntax:

ASORT(<aTarget>, [<nStart>],
[<nCount>], [<bOrder>]) --> aTarget

 Arguments:

<aTarget> is the array to be sorted.

<nStart> is the first element of the sort.  If not specified, the default starting position is one.

<nCount> is the number of elements to be sorted.  If not specified, all elements in the array beginning with the starting element are sorted.

<bOrder> is an optional code block used to determine sorting order.  If not specified, the default order is ascending.

 Returns:

ASORT() returns a reference to the <aTarget> array.

 Description:

 ASORT() is an array function that sorts all or part of an array  containing elements of a single data type.  Data types that can be  sorted include character, date, logical, and numeric.

If the <bOrder> argument is not specified, the default order is ascending.  Elements with low values are sorted toward the top of the  array (first element), while elements with high values are sorted toward the bottom of the array (last element).

If the <bOrder> block argument is specified, it is used to determine the sorting order.  Each time the block is evaluated; two elements from the target array are passed as block parameters.  The block must return true     (.T.) if the elements are in sorted order.  This facility can be used to create a descending or dictionary order sort.  See the examples below.

When sorted, character strings are ordered in ASCII sequence; logical values are sorted with false (.F.) as the low value; date values are sorted chronologically; and numeric values are sorted by magnitude.

 Notes:

ASORT() is only guaranteed to produce sorted output (as defined by the block), not to preserve any existing natural order in the process.

Because multidimensional arrays are implemented by nesting sub-arrays within other arrays, ASORT() will not directly sort  a multidimensional array.  To sort a nested array, you must supply a code block which properly handles the sub-arrays.

 Examples:

This example creates an array of five unsorted elements, sorts the array in ascending order, then sorts the array in descending  order using a code block:

aArray := { 3, 5, 1, 2, 4 }
ASORT(aArray)
// Result: { 1, 2, 3, 4, 5 }
ASORT(aArray,,, { |x, y| x > y })
// Result: { 5, 4, 3, 2, 1 }

This example sorts an array of character strings in ascending order, independent of case.  It does this by using a code block that  converts the elements to uppercase before they are compared:

aArray := { "Fred", Kate", "ALVIN", "friend" }
ASORT(aArray,,, { |x, y| UPPER(x) < UPPER(y) })

This example sorts a nested array using the second element of each sub-array:

aKids := { {"Mary", 14}, {"Joe", 23}, {"Art", 16} }
aSortKids := ASORT(aKids,,, { |x, y| x[2] < y[2] })

Result:

{ {“Mary”, 14}, {“Art”, 16}, {“Joe”, 23} }

Last element in an array:

ATAIL() : Return the highest numbered element of an array

Syntax:

      ATAIL(<aArray>) --> Element

Arguments:

<aArray> is the array.

 Returns:

ATAIL() returns either a value or a reference to an array or object.  The array is not changed.

 Description:

ATAIL() is an array function that returns the highest numbered element  of an array.  It can be used in applications as shorthand for <aArray>[LEN(<aArray>)] when you need to obtain the last element of an      array.

Examples:

The following example creates a literal array and returns that last element of the array:

     aArray := {"a", "b", "c", "d"}
     ? ATAIL(aArray)                     // Result: d

Getting directory info:

ADIR() is a array function to obtain directory information. But it’s  a compatibility function and therefore not recommended. It is superseded by the DIRECTORY() function which returns all file information in a multidimensional array.

A sample .prg : ArrayBasics.prg 

 

ArrBass

Using code blocks, again

Using code blocks again (.pdf)

Simple Clipper Extensions

Simple extensions

Exact comparisons,
Name precedence,
SELECT 0,
Alias functions,
Call by reference and value,
Other Clipper extensions