Announcement

Collapse
No announcement yet.

What am I doing wrong on ARRAY SCAN

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

  • What am I doing wrong on ARRAY SCAN

    According to the docs, PB9 should be able to scan arrays of UDTs:
    ARRAY SCAN cannot be used on arrays within UDT structures. However, ARRAY SCAN can be used with arrays of UDT structures - simply treat them as if they were an array of fixed-length strings.
    So why doesn't this work?

    Code:
    #Compile Exe
    #Dim All
    
    TYPE TESTUDT
       sName as string * 15
       sAddr AS STRING * 15
       sJunk AS STRING * 15
    END TYPE
    
    Function PBMain()
       Local iCount   as long
       Local iFound   as long
       Local sFind    as STRING
       Local uList()  as TESTUDT
       REDIM uList(1 to 5)
       For iCount = 1 to 5
          uList(iCount).sName = "TestName" & format$(iCount)
          uList(iCount).sAddr = "TestAddr" & format$(iCount)
          uList(iCount).sJunk = "TestJunk" & format$(iCount)
       NEXT iCount
    
       sFind = uList(3).sAddr
    
       ARRAY SCAN uList(), COLLATE UCASE, =UCASE$(sFind), TO iFound
    
       Msgbox "ARRAY SCAN returned: " & str$(iFound),,uList(3).sAddr & " found at:"
    
       Msgbox sFind,, uList(3).sAddr
    
    
    END FUNCTION
    Last edited by William Burns; 25 Sep 2008, 05:35 PM.
    "I haven't lost my mind... its backed up on tape... I think??" :D

  • #2
    Never mind. I think I figured it out.
    Code:
    ARRAY SCAN uList(), FROM 16 to LEN(sFind) + 15, =sFind, TO iFound
    You have to give it the exact position to search since it treats the UDT like a fixed string.

    I was kinda hoping it would search the entire UDT. (kinda like a wildcard for a match anywhere inside the string... guess not)
    "I haven't lost my mind... its backed up on tape... I think??" :D

    Comment


    • #3
      Search the specific positions of the "field" in the Udt. In this case pos 16 to 30.

      Array Scan uList(), Collate UCase, From 16 To 30, = UCase$(sFind), To iFound


      Originally posted by William Burns View Post
      According to the docs, PB9 should be able to scan arrays of UDTs:


      So why doesn't this work?

      Code:
      #Compile Exe
      #Dim All
      
      TYPE TESTUDT
         sName as string * 15
         sAddr AS STRING * 15
         sJunk AS STRING * 15
      END TYPE
      
      Function PBMain()
         Local iCount   as long
         Local iFound   as long
         Local sFind    as STRING
         Local uList()  as TESTUDT
         REDIM uList(1 to 5)
         For iCount = 1 to 5
            uList(iCount).sName = "TestName" & format$(iCount)
            uList(iCount).sAddr = "TestAddr" & format$(iCount)
            uList(iCount).sJunk = "TestJunk" & format$(iCount)
         NEXT iCount
      
         sFind = uList(3).sAddr
      
         ARRAY SCAN uList(), COLLATE UCASE, =UCASE$(sFind), TO iFound
      
         Msgbox "ARRAY SCAN returned: " & str$(iFound),,uList(3).sAddr & " found at:"
      
         Msgbox sFind,, uList(3).sAddr
      
      
      END FUNCTION

      Comment


      • #4
        Brian, just a side note, but if the search string was not the full 15 chars it will not work either. For example this does not work:
        Code:
        Array Scan uList(), Collate UCase, From 16 To 30, =UCase$("TestAddr3"), To iFound
        That is why I had to use the LEN() in my code above.
        "I haven't lost my mind... its backed up on tape... I think??" :D

        Comment


        • #5
          >Array Scan uList(), Collate UCase, From 16 To 30, = UCase$(sFind), To iFound

          Inline numeric literals, yuck.

          You can let the compiler figure out the "from" and "to" the same way as shown at CC3+/Win7+: ARRAY SORT UDT array on member name September 04, 2002

          Change the size of UDT members? The number of members? No Problem: no changes required to ARRAY SORT or ARRAY SCAN.....

          I think you should post a "MACRO to ARRAY SCAN a UDT array within a named member", don't you?
          Michael Mattias
          Tal Systems (retired)
          Port Washington WI USA
          [email protected]
          http://www.talsystems.com

          Comment


          • #6
            Another way to search a UDT array is to locate the UDT array inside a dynamic string and then search the array inside the string - I do this using the offset to search each element incrementally, because I'm just using the string as a handy way to allocate memory, but INSTR would work on the whole string, though you would need a strategy for spurious matches.

            Comment


            • #7
              I left this to be figured out. I was just pointing out you missed the parameters for scanning the correct positions of the udt as string.

              I probably should have taken the time to post a more correct solution.

              Originally posted by William Burns View Post
              Brian, just a side note, but if the search string was not the full 15 chars it will not work either.

              Comment


              • #8
                Not tested but I think it will work.

                Code:
                MACRO FUNCTION array_scan_udt_member(arrayname,membername,lookfor)
                 MACROTEMP Scanfrom, Scanto, ScanHit 
                 LOCAL Scanfrom AS LONG, ScanTo AS LONG, ScanHitAS LONG
                 LOCAL lb  AS LONG
                   lb       = LBOUND(arrayname,1) 
                   ScanFrom = VARPTR(arrayname(lb).membername) - VARPTR(arrayName(lb)) + 1
                   SortTo   = Scanfrom + SIZEOF(arrayname(lb).membername)
                   Array Scan ArrayName(), FROM ScanFrom to ScanTo, =lookfor TO ScanHit
                END MACRO = Scanhit 
                
                TYPE MyType
                   A AS STRING  * something
                   B AS STRING  * something
                   C AS STRING  * something
                END TYPE 
                
                FUNCTION Calling_function() AS LONG 
                 LOCAL MyArray AS MyType, searchfor as STRING 
                 LOCAL bHit AS LONG 
                 
                   REDIM MyArray(somenumber) 
                   CALL FillArray (MyArray())
                
                   ' ------------------------------------
                   '  Search the "B" member of all array 
                   '  elements for a token
                   ' ------------------------------------- 
                   SearchFor = "Hello World"  
                
                   bHit = Array_scan_udt_member (myarray,B,searchFor)
                   IF ISTRUE bHit THEN 
                       found_subscript =  bHit - LBOUND(MyArray,1) + 1 
                   ELSE
                      Display "Not found" 
                   ......
                
                
                
                .....
                MCM
                Last edited by Michael Mattias; 26 Sep 2008, 09:25 AM.
                Michael Mattias
                Tal Systems (retired)
                Port Washington WI USA
                [email protected]
                http://www.talsystems.com

                Comment


                • #9
                  FWIW, I know there is a "LIKE" function floating around here somewhere... that might be handy for searching fixed string UDT members against variable-length dynamic strings... for that matter REGEXPR might be useful.

                  You could insert that logic right into the MACRO FUNCTION .....


                  MCM
                  Michael Mattias
                  Tal Systems (retired)
                  Port Washington WI USA
                  [email protected]
                  http://www.talsystems.com

                  Comment


                  • #10
                    MCM, I think that is close, but I see one issue. The length of the search string was not taking in to account. For example if you have a UDT memeber that is 10 chars long, but you try to search for something shorter, like "TESTNAME" it would not find the match even if the UDT member had a "TESTNAME " value.

                    So maybe a better way would be to calc the len of the search string:
                    Code:
                    MACRO FUNCTION array_scan_udt_member(arrayname,membername,lookfor)
                     MACROTEMP ScanFrom, ScanTo, ScanHit, lb 
                     LOCAL ScanFrom AS LONG, ScanTo AS LONG, ScanHit AS LONG, lb  AS LONG
                       lb       = LBOUND(arrayname) 
                       ScanFrom = VARPTR(uList(lb).membername) - VARPTR(uList(lb)) + 1
                       ScanTo   = ScanFrom + MIN(Len(lookfor), SIZEOF(arrayname(lb).membername)) - 1
                       Array Scan arrayname(), FROM ScanFrom to ScanTo, =lookfor, TO ScanHit
                    END MACRO = ScanHit
                    At least that is what I found during my tests.

                    If you have a program that does tons of searches, it would be faster to calc the startpoint at the begining of the program since it would always be the same. Then you would only have to calc the end point based on the start point and len of string to search. 99% of the time it makes more sense to keep it as a handy macro like you have listed. But I do have a program that does milions of searches in an 8 hour period, so any speed increase helps.
                    Last edited by William Burns; 26 Sep 2008, 12:58 PM.
                    "I haven't lost my mind... its backed up on tape... I think??" :D

                    Comment


                    • #11
                      >So maybe a better way would be to calc the len of the search string

                      I think that is application-dependent, isn't it?

                      For that matter you could always....
                      Code:
                        SearchFor  = LSET$("Hello World", SIZEOF(myArray(0).B) USING $SPC)
                      .. couldn't you?

                      MCM
                      Michael Mattias
                      Tal Systems (retired)
                      Port Washington WI USA
                      [email protected]
                      http://www.talsystems.com

                      Comment


                      • #12
                        >If you have a program that does tons of searches...

                        ... you would probably at least consider options other than ARRAY SCAN , right?

                        After all is said and done you might end up using ARRAY SCAN anyway, but at least you will have made a carefully-considered rather than knee-jerk decision.
                        Michael Mattias
                        Tal Systems (retired)
                        Port Washington WI USA
                        [email protected]
                        http://www.talsystems.com

                        Comment


                        • #13
                          you would probably at least consider options other than ARRAY SCAN , right?
                          Yep, I did. In fact I was originally doing direct searches from the database as needed, but the speed compared to a memory array is not even close. (it went from taking 3 days to complete a full update to 8 hours using the memory array) And yes I tried other items like delimited strings using instr() and other other database options. In the end, the memory arrays won by a landslide. And in case you are reading the "3 days", "8 hours" comment and thinking I must be doing something wrong or there has to be a better way... I should mention that my tool is replacing a very expensive "canned" product that took 3 times as long to run the same items.
                          "I haven't lost my mind... its backed up on tape... I think??" :D

                          Comment


                          • #14
                            William, you might consider the trusty ol' memory overlay absolute array, which allows sort of like you wanted:
                            I was kinda hoping it would search the entire UDT. (kinda like a wildcard for a match anywhere inside the string... guess not)
                            It seems reasonably fast.
                            Code:
                            #COMPILE EXE
                            #DIM ALL
                            
                            TYPE TESTUDT
                               sName AS STRING * 15
                               sAddr AS STRING * 15
                               sJunk AS STRING * 15
                            END TYPE
                            
                            %ARRsIZE = 50000
                            
                            FUNCTION PBMAIN()
                               LOCAL iCount   AS LONG, ii AS LONG
                               LOCAL iFound   AS LONG
                               LOCAL sFind    AS STRING
                               LOCAL uList()  AS TESTUDT
                               LOCAL memStr AS STRING
                               memStr = SPACE$(45 * %ARRsIZE)
                               REDIM uList(1 TO %ARRsIZE) AT STRPTR(memStr)
                               REDIM udtArr(1 TO %ARRsIZE) AS STRING * 45 AT STRPTR(memStr)
                            
                               FOR iCount = 1 TO %ARRsIZE
                                  uList(iCount).sName = "TestName" & FORMAT$(iCount)
                                  uList(iCount).sAddr = "TestAddr" & FORMAT$(iCount)
                                  uList(iCount).sJunk = "TestJunk" & FORMAT$(iCount)
                               NEXT iCount
                            
                               sFind = uList(30000).sAddr
                            
                               FOR ii =  1 TO %ARRsIZE
                                  IF INSTR(udtArr(ii), sFind) THEN
                                     iFound = ii
                                     MSGBOX " returned: " & STR$(iFound),,uList(30000).sAddr & " found at:"
                                  END IF
                               NEXT
                            
                               MSGBOX sFind,, uList(30000).sAddr
                            
                            END FUNCTION

                            Comment


                            • #15
                              Well, looks like my post went over like the proverbial lead balloon. From your last post William I see perhaps you already tried that technique? I've added some code below to the example to limit the search just to the member selected. This will likely be faster.
                              Code:
                              #COMPILE EXE
                              #DIM ALL
                              
                              TYPE TESTUDT
                                 sName AS STRING * 15
                                 sAddr AS STRING * 15
                                 sJunk AS STRING * 15
                              END TYPE
                              
                              %ARRsIZE = 300000
                              %UDTsIZE = 45
                              
                              FUNCTION PBMAIN()
                                 LOCAL iCount   AS LONG, ii AS LONG
                                 LOCAL iFound   AS LONG, locMember AS LONG, lenMember AS LONG
                                 LOCAL sFind    AS STRING
                                 LOCAL uList()  AS TESTUDT
                                 LOCAL memStr AS STRING
                                 memStr = SPACE$(%UDTsIZE * %ARRsIZE)
                                 REDIM uList(1 TO %ARRsIZE) AT STRPTR(memStr)
                                 REDIM udtArr(1 TO %ARRsIZE) AS STRING * %UDTsIZE AT STRPTR(memStr)
                              
                                 FOR iCount = 1 TO %ARRsIZE
                                    uList(iCount).sName = "TestName" & FORMAT$(iCount)
                                    uList(iCount).sAddr = "TestAddr" & FORMAT$(iCount)
                                    uList(iCount).sJunk = "TestJunk" & FORMAT$(iCount)
                                 NEXT iCount
                              
                                 sFind = uList(250000).sAddr
                                 locMember = VARPTR(uList(1).sAddr) - VARPTR(uList(1).sName) + 1 '+1 to move to 1st char of sAddr
                                 lenMember = VARPTR(uList(1).sJunk) - VARPTR(uList(1).sAddr)     'limit length to be searched too
                                 
                                 ? "UDT's all filled, finding now...",, "Ready to Find"
                                 FOR ii =  1 TO %ARRsIZE
                                    IF INSTR(locMember, udtArr(ii), sFind) THEN                  'now searches just from designated member
                                       IF INSTR(locMember, udtArr(ii), sFind) > locMember + lenMember THEN EXIT IF 'if your find isn't within member's len, exit
                                       iFound = ii
                                       MSGBOX " returned: " & STR$(iFound),,uList(250000).sAddr & " found at:"
                                    END IF
                                 NEXT
                              
                                 MSGBOX sFind,, uList(250000).sAddr
                              
                              END FUNCTION

                              Comment


                              • #16
                                taking liberties with John Gleason's code along the lines I suggested in post #6:
                                - and getting it slightly wrong, apparently. Oh well, you know what I meant...

                                Code:
                                ' untested code!!!
                                #COMPILE EXE
                                #DIM ALL
                                
                                TYPE TESTUDT
                                   sName AS STRING * 15
                                   sAddr AS STRING * 15
                                   sJunk AS STRING * 15
                                END TYPE
                                
                                %ARRsIZE = 50000
                                
                                FUNCTION PBMAIN()
                                   LOCAL iCount   AS LONG, ii AS LONG
                                   LOCAL iFound, lsize, p, q   AS LONG
                                   LOCAL s, sFind    AS STRING
                                   LOCAL uList()  AS TESTUDT
                                   LOCAL memStr AS STRING
                                   
                                   memStr = SPACE$(45 * %ARRsIZE)
                                   REDIM uList(1 TO %ARRsIZE) AT STRPTR(memStr)
                                   REDIM udtArr(1 TO %ARRsIZE) AS STRING * 45 AT STRPTR(memStr)
                                
                                   FOR iCount = 1 TO %ARRsIZE
                                      uList(iCount).sName = "TestName" & FORMAT$(iCount)
                                      uList(iCount).sAddr = "TestAddr" & FORMAT$(iCount)
                                      uList(iCount).sJunk = "TestJunk" & FORMAT$(iCount)
                                   NEXT iCount
                                
                                   sFind = uList(30000).sAddr
                                
                                   p = INSTR(memstr,sfind)
                                   lsize = SIZEOF(testudt)
                                   q = p - 1 - (p \ lsize) * lsize
                                   SELECT CASE q
                                       CASE 0        : s = "name field starts with "
                                       CASE 1 TO 14  : s = "name field contains"
                                       CASE 15       : s = "addr field starts with "
                                       CASE 16 TO 29 : s = "addr field contains"
                                       CASE 30       : s = "junk field starts with"
                                       CASE 31 TO 44 : s = "junk field contains"
                                       CASE ELSE     : s = "programming error!"
                                   END SELECT
                                
                                   ? "array has element #" + STR$(1 + p\lsize) + " in which the " + s + " the target value"
                                   
                                END FUNCTION
                                Last edited by Chris Holbrook; 27 Sep 2008, 09:20 AM. Reason: adjust code

                                Comment


                                • #17
                                  Don't forget... it might be worth the effort (and frankly it's not a whole lot of effort) to use a binary search instead of the linear search performed by ARRAY SCAN...

                                  Binary Search of an array February 14 2000, July 15 2003
                                  Michael Mattias
                                  Tal Systems (retired)
                                  Port Washington WI USA
                                  [email protected]
                                  http://www.talsystems.com

                                  Comment


                                  • #18
                                    Originally posted by Michael Mattias View Post
                                    ... binary search instead of the linear search performed by ARRAY SCAN...
                                    ...and even faster if you can build a hash index table when adding data to the array.

                                    Comment


                                    • #19
                                      You are not going to believe who needed this today....and found an error. (The same error Mr. Burns found, I needed to subtract one from the ScanFrom)

                                      This one IS tested and DOES work!

                                      Code:
                                      ' new macro 10/2/08
                                      MACRO FUNCTION array_scan_udt_member (arrayname,membername,lookfor)
                                       MACROTEMP Scanfrom, Scanto, ScanHit
                                       DIM Scanfrom AS LONG, ScanTo AS LONG, ScanHit AS LONG
                                       DIM lb  AS LONG
                                         lb       = LBOUND(arrayname,1)
                                         ScanFrom = VARPTR(arrayname(lb).membername) - VARPTR(arrayName(lb)) + 1
                                         ScanTo   = Scanfrom + SIZEOF(arrayname(lb).membername) -1 
                                         
                                         STDOUT USING$ ("ScanFrom # SCANTO # LookFor &", ScanFrom, Scanto, lookFor)
                                         ARRAY SCAN ArrayName(), FROM ScanFrom TO ScanTo, =lookfor, TO ScanHit
                                      END MACRO = Scanhit
                                      
                                      TYPE ConfigKey
                                          Qual AS STRING * 2
                                          ID   AS STRING * 82
                                      END TYPE
                                      TYPE ConfigFileType
                                          K             AS ConfigKey
                                          Expr (1 TO 2) AS ASCIIZ * 512
                                      END TYPE
                                      
                                      
                                      FUNCTION  (.... cfg() AS ConfigFileType...)  ..... 
                                      
                                        LOCAL wPEID   AS CONFIGKEY
                                      
                                        ......
                                                         LSET wPEID.Qual = PARSE$(S(iSeg), elsep,4)   ' element three
                                                         LSET wPEID.ID = PARSE$(S(iSeg), elsep,5)   ' element four
                                                         iHit =  Array_scan_udt_member(cfg,K,wPeid)
                                      
                                      ...
                                      MCM
                                      Michael Mattias
                                      Tal Systems (retired)
                                      Port Washington WI USA
                                      [email protected]
                                      http://www.talsystems.com

                                      Comment

                                      Working...
                                      X