Announcement

Collapse
No announcement yet.

Question on managing a set of arrays

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Question on managing a set of arrays

    I've got a situation where I need to maintain a set of arrays. The number of arrays is dynamic - determined by the user. The type of the arrays is known - they are all LONG arrays. The UBOUND of the arrays can vary. Any suggestions on how I can best manage them?

    If all the arrays had the same dimension (UBOUND), I'd just create a multi-dimension array to house them.
    Bernard Ertl
    InterPlan Systems

  • #2
    Bern
    You do keep asking fun questions. It seems you need a redimable array of arrays. To explain you would have a redimable array of pointers to array descriptors (obviously checking for Nulls if the array referenced no longer exists). Not sure how it could work with PB arrays (is late I will dream about it) but if all dynamically created arrays were Safe Arrays then then that pointer could easily be used to determine bounds and memory location of the first element so that you could do A Redim AT to access or change the data. I suspect it could be done with PB arrays as well but too late at night for me to look.
    John

    Comment


    • #3
      So, is memory an issue?

      If not, then go ahead and use a single multi-dimensioned array anyway but keep track of the "UBound" of each dimension in another array.

      Comment


      • #4
        Gary
        Were life so simple. Bern is an experienced programmer. Redim Preserve only allows you to change one bound so depending on choices that would be the number of arrays or the number of entries thus his question which require changing both.
        John

        Comment


        • #5
          Code:
             Redim [PRESERVE] MyArrayOfArrays (somenumber) AS VARIANT 
             LET  MyArrayofArrays(subscript) =  LongArray()
          Getting your array back will take a little effort, but it's doable. There are some safearray functions in the Samples\VB32 folder installed with compiler to get you started.

          However... I handle getting a PB array from a variant here:
          Generic 'ADO' Connection and Query Tester (CC 5+/Win 9+) 11-02-08

          You should be able to adapt that to a LONG array without a whole lot of effort.

          MCM
          Last edited by Michael Mattias; 13 May 2009, 02:49 PM.
          Michael Mattias
          Tal Systems (retired)
          Port Washington WI USA
          [email protected]
          http://www.talsystems.com

          Comment


          • #6
            Originally posted by John Petty View Post
            ... It seems you need a redimable array of arrays.
            Correct.

            I'm thinking I might have to simulate it using an array of strings and DIM AT for access. I might need to develop a DLL library (or INClude file of MACROs) to encapsulate all the array handling routines I need.

            Originally posted by Michael Mattias View Post
            Code:
               Redim [PRESERVE] MyArrayOfArrays (somenumber) AS VARIANT 
               LET  MyArrayofArrays(subscript) =  LongArray()
            Getting your array back will take a little effort, but it's doable. There are some safearray functions in the Samples\VB32 folder installed with compiler to get you started.

            However... I handle getting a PB array from a variant here:
            Generic 'ADO' Connection and Query Tester (CC 5+/Win 9+) 11-02-08

            You should be able to adapt that to a LONG array without a whole lot of effort.

            MCM
            Thanks. I'll need to investigate that.
            Bernard Ertl
            InterPlan Systems

            Comment


            • #7
              Code:
              INIT: 
                 REDIM AofA (number) AS LONG ' holds handles 
              
              ADD:
              'add LONG array "L()" to AofA() at end: 
                 LOCAL pl AS LONG PTR 
                 nEl            = ARRAYATTR(L(),4)              ' element count of new array L() 
                 GLOBALMEM ALLOC  (nEl+1) * 4 to Hmem   ' 4 = SIZEOF(LONG), but compiler won't accept that 
                 GLOBALMEM LOCK    hMem TO pL           ' pointer to start of block 
                 @pl            = nel                   ' store element count at offset zero 
                 INCR pl          ' up we go 
                 CopyMemory       BYVAL pL, VARPTR(L(LBOUND (L,1))), nel * 4 
                    ' there's that pesky old SIZEOF(LONG) thing again. Darn, that would be handy wouldn't it? 
                 GLOBALMEM  UNLOCK  hMem 
                 REDIM PRESERVE AofA (UBOUND(AofA,1) + 1) 
                 AofA (UBOUND(AofA,1)) = hMem       ' store handle as new last element 
              
              GET:
              ' get back a desired array subscript into a working array "L()" 
                 hMem   =    AofA (subscript) 
                 GLOBALMEM LOCK  hMem  TO pl 
                 nel    = pl 
                 INCR     pl 
                 REDIM    L(nel-1) AT pl   ' you could copy it off as above if you don't want
                                           ' to work with it in place. 
                 ' ------------------------
                 ' work with L() here 
                 ' ------------------------- 
                 GLOBALMEM UNLOCK hMem      ' CANNOT 'UNLOCK' until we are done with L() here
              MCM
              Michael Mattias
              Tal Systems (retired)
              Port Washington WI USA
              [email protected]
              http://www.talsystems.com

              Comment


              • #8
                Experienced he may be, but you didn't understand the solution I suggested.

                Bern didn't say he had a memory problem and I didn't say anything about ReDIM Preserve.

                The "UBound" in quotes referred to a pointer for each of the dimensions in the big array, where no data would exist past that pointer in each of the array dimensions.

                Pointer = number of the array element in a dimension of the big array corresponding to a "virtual" UBound for that dimension

                Code:
                    Dim BigArray(100,1000)  '100 arrays, each with 1000 elements
                    Dim iMax As Long        'number of valid "arrays" as defined by the user
                    Dim APointer(100)       'maximum position in each dimension (virtual UBound)
                Just don't use what you don't need.
                Last edited by Gary Beene; 13 May 2009, 03:51 PM.

                Comment


                • #9
                  Like I said fun, the real problem seems to be not how you access them but how do you create them.

                  Comment


                  • #10
                    John,

                    You fooled me! I was reading the responses and I'd have sworn it was MCM talking. I laughed out loud when I re-read your response and realized who had really spoken.

                    Please - take no offense nor compliment from my mistake! <g>

                    Comment


                    • #11
                      Why are you keeping me awake? Without resorting to my rusty assmbeler then if if the size of the new dynamic array can be predetermined in advance and won't change then I would add it to a redimmmable string array with a string size of expected entries x 4 as say spaces or chr$(0) then to access you could REDIM AT STRPTR(array member) AS LONG with a size of LEN(array member \ 4). You might need another array to keep track what each array member means. Of course if you need to change the size of the array then you would do it as a string length change, let the OLE engine reallocate space in basically a REDIM PRESEVE mode, determine its new address and REDIM AT that location.

                      Comment


                      • #12
                        Originally posted by Gary Beene View Post
                        John,

                        You fooled me! I was reading the responses and I'd have sworn it was MCM talking. I laughed out loud when I re-read your response and realized who had really spoken.

                        Please - take no offense nor compliment from my mistake! <g>
                        No offense taken, as I did miss both the simplicity and elegance of your solution. While it imposes fixed limitations on both array dimensions it may be the best solution.

                        Comment


                        • #13
                          I went ahead and developed a simple wrapper over an array of strings:
                          Code:
                          '  ==========================================================================
                          '  |                                                                        |
                          '  | Array of Arrays                                                        |
                          '  |                                                                        |
                          '  ==========================================================================
                          '  Allows you to manage a set of LONG arrays where the number of arrays is
                          '  variable and the UBOUND for each array in the set is also variable.
                          '
                          '  Notes: these routines were built for a specific circumstances:
                          '     * all arrays are assumed to be built sorted in ascending order
                          '     * all data in arrays are assumed to be >= 0 (ie. no negative numbers)
                          '     * all arrays use 0 as the LBOUND
                          '
                          '     -> .lMaxValue may not be necessary for a more general application
                          '     -> binary search does not work if arrays are not sorted
                          '     -> if you need to store negative numbers, you will need to adjust the
                          '          AoA_Get function to return value in a parameter separate from error code
                          '
                          '     * Critical sections to make thread safe not fully implemented (not sure I need it yet)
                          '  ==========================================================================
                          '  PUBLIC DOMAIN SOFTWARE
                          '  The author or authors of this code dedicate any and all
                          '  copyright interest in this code to the public domain.
                          '  Anyone is free to copy, modify, publish, use, compile,
                          '  sell, or distribute the original code, either in source
                          '  code form or as a compiled binary, for any purpose,
                          '  commercial or non-commercial, and by any means.
                          '  Bern Ertl - May 2009
                          '  ==========================================================================
                          
                          #COMPILE DLL
                          #DIM ALL
                          
                          '============================<[ Equates ]>=============================
                          
                          '=============================<[ Types ]>==============================
                          TYPE ArrayInfo
                          	lKey        AS LONG     'Each array is indexed to a 'key' supplied by the application
                          	plData      AS LONG PTR
                          	lUpperBound AS LONG
                          	lMaxValue   AS LONG
                          END TYPE
                          
                          '=============================<[ Macros ]>=============================
                          
                          '============================<[ Globals ]>=============================
                          GLOBAL gCriticalSection AS CRITICAL_SECTION ' Critical section handle
                          
                          GLOBAL gsArrayData() AS STRING
                          GLOBAL guArrayInfo() AS ArrayInfo
                          
                          '============================<[ Functions ]>===========================
                          
                          SUB AoA_DIM ALIAS "AoA_DIM" (BYVAL lKey AS LONG, _
                          			  BYVAL lUpperBound AS LONG) EXPORT
                          
                          	REGISTER I AS LONG, J AS LONG
                          
                          	J = UBOUND( guArrayInfo)
                          	'Check if key already exists
                          	FOR I = 0 TO J
                          		IF guArrayInfo( I).lKey = lKey THEN
                          			IF guArrayInfo( I).lUpperBound <> lUpperBound THEN
                          				guArrayInfo( I).lUpperBound = lUpperBound
                          				guArrayInfo( I).lMaxValue = 0
                          				gsArrayData( I) = STRING$( (lUpperBound + 1) * 4, 0)
                          				guArrayInfo( I).plData = STRPTR( gsArrayData( I))
                          			END IF
                          			EXIT SUB
                          		END IF
                          	NEXT
                          
                          	'Add lKey to guArrayInfo
                          	INCR J
                          	REDIM PRESERVE gsArrayData( J)
                          	REDIM PRESERVE guArrayInfo( J)
                          
                          	guArrayInfo( J).lKey = lKey
                          	guArrayInfo( J).lUpperBound = lUpperBound
                          	guArrayInfo( J).lMaxValue = 0
                          	gsArrayData( J) = STRING$( (lUpperBound + 1) * 4, 0)
                          	guArrayInfo( J).plData = STRPTR( gsArrayData( J))
                          
                          END SUB
                          
                          '------------------------------------------------------------------------
                          
                          SUB AoA_REDIM ALIAS "AoA_REDIM" (BYVAL lKey AS LONG, _
                          				BYVAL lUpperBound AS LONG) EXPORT
                          
                          	'ReDim PRESERVE
                          
                          	REGISTER I AS LONG, J AS LONG
                          
                          	J = UBOUND( guArrayInfo)
                          	'Check if key already exists
                          	FOR I = 0 TO J
                          		IF guArrayInfo( I).lKey = lKey THEN
                          			SELECT CASE AS LONG guArrayInfo( I).lUpperBound
                          				CASE < lUpperBound
                          					'Expand array
                          					gsArrayData( I) += STRING$( (lUpperBound - guArrayInfo( I).lUpperBound) * 4, 0)
                          					guArrayInfo( I).lUpperBound = lUpperBound
                          					guArrayInfo( I).plData = STRPTR( gsArrayData( I))
                          				CASE = lUpperBound
                          					'Do nothing
                          				CASE > lUpperBound
                          					'Reduce array
                          					gsArrayData( I) = LEFT$( gsArrayData( I), (lUpperBound + 1) * 4)
                          					guArrayInfo( I).lUpperBound = lUpperBound
                          					guArrayInfo( I).plData = STRPTR( gsArrayData( I))
                          					guArrayInfo( I).lMaxValue = CVL( gsArrayData( I), lUpperBound * 4 + 1)
                          			END SELECT
                          			EXIT SUB
                          		END IF
                          	NEXT
                          
                          	'Add lKey to guArrayInfo
                          	INCR J
                          	REDIM PRESERVE gsArrayData( J)
                          	REDIM PRESERVE guArrayInfo( J)
                          
                          	guArrayInfo( J).lKey = lKey
                          	guArrayInfo( J).lUpperBound = lUpperBound
                          	guArrayInfo( J).lMaxValue = 0
                          	gsArrayData( J) = STRING$( (lUpperBound + 1) * 4, 0)
                          	guArrayInfo( J).plData = STRPTR( gsArrayData( J))
                          
                          END SUB
                          
                          '------------------------------------------------------------------------
                          
                          FUNCTION AoA_Put ALIAS "AoA_Put" (BYVAL lKey AS LONG, _
                          				 BYVAL lSubscript AS LONG, _
                          				 BYVAL lValue AS LONG) EXPORT AS LONG
                          
                          	'Returns %True if successful
                          
                          	REGISTER I AS LONG, J AS LONG
                          
                          	J = UBOUND( guArrayInfo)
                          	'Find key
                          	FOR I = 0 TO J
                          		IF guArrayInfo( I).lKey = lKey THEN
                          			IF lSubscript >=0 AND lSubscript < guArrayInfo( I).lUpperBound THEN
                          				guArrayInfo( I)[email protected][ lSubscript] = lValue
                          				FUNCTION = %True
                          				EXIT FUNCTION
                          			ELSEIF lSubscript = guArrayInfo( I).lUpperBound THEN
                          				guArrayInfo( I)[email protected][ lSubscript] = lValue
                          				guArrayInfo( I).lMaxValue = lValue
                          				FUNCTION = %True
                          			END IF
                          			EXIT FUNCTION
                          		END IF
                          	NEXT
                          
                          END FUNCTION
                          
                          '------------------------------------------------------------------------
                          
                          FUNCTION AoA_Get ALIAS "AoA_Get" (BYVAL lKey AS LONG, _
                          				 BYVAL lSubscript AS LONG) EXPORT AS LONG
                          
                          	'Returns data from array if successful,
                          	  'returns -1 if lSubscript out of bounds
                          	  'returns -2 if lKey not found
                          
                          	REGISTER I AS LONG, J AS LONG
                          
                          	J = UBOUND( guArrayInfo)
                          	'Find key
                          	FOR I = 0 TO J
                          		IF guArrayInfo( I).lKey = lKey THEN
                          			IF lSubscript >=0 AND lSubscript <= guArrayInfo( I).lUpperBound THEN
                          				FUNCTION = guArrayInfo( I)[email protected][ lSubscript]
                          				EXIT FUNCTION
                          			ELSE
                          				'Need to expand UBOUND of array...
                          				FUNCTION = -1
                          			END IF
                          			EXIT FUNCTION
                          		END IF
                          	NEXT
                          
                          	'Need to add array for lKey...
                          	FUNCTION = -2
                          
                          END FUNCTION
                          
                          '------------------------------------------------------------------------
                          
                          FUNCTION AoA_BinarySearch ALIAS "AoA_BinarySearch" (BYVAL lKey AS LONG, _
                          				 BYVAL lValue AS LONG) EXPORT AS LONG
                          
                          	'Returns subscript from array matching lValue OR returns subscript from array containing largest value that is less than lValue
                          	'Assumes array(0) = 0
                          	  'returns 0 if lValue <= 0
                          	  'returns -1 if lValue > .lMaxValue
                          	  'returns -2 if lKey not found
                          
                          	REGISTER I AS LONG, J AS LONG
                          
                          	LOCAL lArraySubscript AS LONG, lIndex AS LONG
                          
                          	IF lValue <= 0 THEN
                          		FUNCTION = 0
                          		EXIT FUNCTION
                          	END IF
                          
                          	J = UBOUND( guArrayInfo)
                          	'Find key
                          	FOR I = 0 TO J
                          		IF guArrayInfo( I).lKey = lKey THEN
                          			IF lValue > guArrayInfo( I).lMaxValue THEN
                          				'Need to expand UBOUND of array...
                          				FUNCTION = -1
                          				EXIT FUNCTION
                          			END IF
                          
                          			lArraySubscript = I
                          
                          			I = 0
                          			J = guArrayInfo( lArraySubscript).lUpperBound
                          
                          			DO
                          			  lIndex = (I + J) \ 2
                          			  SELECT CASE AS LONG guArrayInfo( lArraySubscript)[email protected][ lIndex]
                          				  CASE > lValue
                          					  J = lIndex - 1
                          				  CASE < lValue
                          					  I = lIndex + 1
                          				  CASE ELSE  ' = lValue
                          					  FUNCTION = lIndex
                          					  EXIT FUNCTION
                          			  END SELECT
                          			LOOP UNTIL J < I
                          
                          			IF guArrayInfo( lArraySubscript)[email protected][ lIndex] > lValue THEN DECR lIndex
                          			FUNCTION = lIndex
                          			EXIT FUNCTION
                          		END IF
                          	NEXT
                          
                          	'Need to add array for lKey...
                          	FUNCTION = -2
                          
                          END FUNCTION
                          
                          '============================<[ Lib Main ]>============================
                          FUNCTION LIBMAIN(BYVAL InstDLL    AS LONG, _
                          		  BYVAL Reason   AS LONG, _
                          		  BYVAL Reserved AS LONG)AS LONG
                          ' InstDLL is the DLL's instance handle.  This handle is used by the calling
                          '    application to identify the DLL being called.
                          ' Reason specifies a flag indicating why the DLL entry-point is being called.
                          '    It can be one of the following values:
                          '    %DLL_PROCESS_ATTACH=1: Indicates that the DLL is being loaded by another process
                          '    (a DLL or EXE is loading the DLL).  DLLs can use this opportunity to initialize
                          '    any instance or global data, such as arrays.
                          '    %DLL_PROCESS_DETACH=0: Indicates that the DLL is being unloaded or detached from
                          '    the calling application.  DLLs can take this opportunity to clean up all
                          '    resources for all threads attached and known to the DLL.
                          '    %DLL_THREAD_ATTACH=2: Indicates that the DLL is being loaded by a new thread in
                          '    the calling application.  DLLs can use this opportunity to initialize any thread
                          '    local storage (TLS).
                          '    %DLL_THREAD_DETACH=3: Indicates that the thread is exiting cleanly.  If the DLL
                          '    has allocated any thread local storage, it should be released.
                          ' Reserved specifies further aspects of the DLL initialization and cleanup.  If
                          '    Reason is %DLL_PROCESS_ATTACH, Reserved is NULL (zero) for dynamic loads
                          '    and non-NULL for static loads.  If Reason is %DLL_PROCESS_DETACH, Reserved
                          '    is NULL if LibMain has been called by using the FreeLibrary API call and
                          '    non-NULL if LibMain has been called during process termination.
                          ' Return: If LibMain is called with %DLL_PROCESS_ATTACH, your LibMain function should
                          '    return a zero (0) if any part of your initialization process fails or a one (1)
                          '    if no errors were encountered.  If a zero is returned, Windows will abort and
                          '    unload the DLL from memory. When LibMain is called with any other value than
                          '    %DLL_PROCESS_ATTACH, the return value is ignored.
                          
                          	IF Reason = 1 THEN
                          		InitializeCriticalSection gCriticalSection
                          	ELSEIF Reason = 0 THEN
                          		DeleteCriticalSection gCriticalSection
                          	END IF
                          	FUNCTION = 1
                          END FUNCTION
                          
                          
                          #ENDIF
                          Bernard Ertl
                          InterPlan Systems

                          Comment


                          • #14
                            I am wondering if it may be easier (well, easier to read and maintain in the long run) to use a array of classes. Each class could contain any number of different types of variables, including an array. You could dim/redim your main array of classes and also work with properties/methods to manipulate the array within the class you're interested in.

                            (no code... just thinking out loud). You'd need PB9 of course.

                            I've used the manual approach to allocating arrays in arrays for several years and it is (in my opinion) much more cumbersome than using PB's new class features.
                            Paul Squires
                            FireFly Visual Designer (for PowerBASIC Windows 10+)
                            Version 3 now available.
                            http://www.planetsquires.com

                            Comment


                            • #15
                              A Dictionary Object may be worth investigating also.

                              James

                              Comment


                              • #16
                                Sound like you want a jagged array.

                                I posted a LONG jagged array in the Source forum.

                                LONG: Jagged and Two Dim Array (DLL & Class)


                                However, it's not made with PB arrays.

                                index always ONE based

                                ReDim is automatic for all operations, data always preserved.
                                Array automatically ReDim for append, insert and deletes.

                                You can append/insert/delet new arrays anywhere in host array.
                                You can append/insert/delet values at any position on a particular row.

                                You can ReDim the host array, or ReDim the array on a particular row.

                                However, it does require a DLL.
                                There's a different zip for a Class or handle reference.
                                There's too many dependencies to post the source.
                                stanthemanstan~gmail
                                Dead Theory Walking
                                Range Trie Tree
                                HLib ~ Free Data Container Lib ~ Arrays, Lists, Stacks, Queues, Deques, Trees, Hashes

                                Comment


                                • #17
                                  The way I would do it is to have one large array to hold all of the LONGs, and a dynamic string array to hold the location of each array's first element followed by an array name. Use REDIM PRESERVE and ARRAY INSERT or ARRAY DELETE to modify the arrays.

                                  Comment

                                  Working...
                                  X