Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Shaun E on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Subscript Outside Defined Range 4

Status
Not open for further replies.

keepingbusy

Programmer
Apr 9, 2000
1,470
GB
Hi All

I have a form that allows a user to paste information into a memo field, Example:

Dave Jones
21 Somewhere Avenue
The Greenacres
Evergreen District
Bristol
BS12 T67

I then use the following code in the click event of a command button to copy the above into seperate fields:
Code:
ALINES(aMyArray, ORDERTEXT)
REPLACE LINE01 WITH aMyArray(1)
REPLACE LINE02 WITH aMyArray(2)
REPLACE LINE03 WITH aMyArray(3)
REPLACE LINE04 WITH aMyArray(4)
REPLACE LINE05 WITH aMyArray(5)
REPLACE LINE06 WITH aMyArray(6)
REPLACE LINE07 WITH aMyArray(7)

Most of the time there will be 7 lines of text in the memo field, but there could be anything between 5 and 8 lines.
If there isn't the exact 7 lines then an error message appears as in the thread title Subscript Outside Defined Range

Please can anyone suggest a method or way around this error?

Many thanks
Lee...

VisFox Version 6 User / Windows ME
 

The error indicates that the array element does not exist in the current array (Ie. looking for the 8th element when it does not exist).

Mike Gagnon

If you want to get the best response to a question, please check out FAQ184-2483 first.
 
Hi Lee.

Most of the time there will be 7 lines of text in the memo field, but there could be anything between 5 and 8 lines.

The ALINES() function return a value - the number of lines. So you could ammend your code (untested!) like so:

Code:
lnLines = ALINES( aMyArray, ORDERTEXT )
lcReplace = []
FOR lnI = 1 TO lnLines
  lcField = [LINE] + PADL( lnI, 2, [0] )
  lcReplace = lcReplace + IIF( NOT EMPTY( lcReplace ), [, ], [] ) + lcField + [ WITH '] + ALLTRIM( aMyArray[ lnI ] ) + [']
ENDFOR
lcReplace = [REPLACE ] + lcReplace
&lcReplace

FWIW, I am pretty sure that the macro substitution on the REPLACE command is going to be a lot faster than using 7 separate REPLACEs.

Marcia G. Akins
 
Mike
The error indicates that the array element does not exist in the current array (Ie. looking for the 8th element when it does not exist).

Thank you for responding

Marcia
Many thanks to you. Your code (Untested!) works perfectly and solved the problem.

I am, as always, grateful for your (and others) assistance

Kindest regards
Lee

VisFox Version 6 User / Windows ME
 
Hi Marcia

Just encountered a small error and I was hoping you could suggest why this is happening?

The below code you supplied works perfect but when you enter a name that has the character ' contained in it, for example O'BRIEN or O'CALLAGHAN the following error message appears
Command contains unrecognized phrase/keyword

I'm guessing here Marcia its something to do with the line
Code:
lcField + [ WITH '] + ALLTRIM( aMyArray[ lnI ] ) + [']
as it contains the ' character. I did try changing this to another character such as * but the same error message appears. Here's the code...

Code:
IF NOT EMPTY(ORDERTEXT)
  lnLines = ALINES( aMyArray, ORDERTEXT )
  lcReplace = []
  FOR lnI = 1 TO lnLines
    lcField = [LINE] + PADL( lnI, 2, [0] )
    lcReplace = lcReplace + IIF( NOT EMPTY( lcReplace ), [, ], [] ) + ;
    lcField + [ WITH '] + ALLTRIM( aMyArray[ lnI ] ) + [']
  ENDFOR
  lcReplace = [REPLACE ] + lcReplace
  &lcReplace
ENDI

I am grateful as always
Lee....

VisFox Version 6 User / Windows ME
 
Anytime your data can include the quote characters (", ', []), then you can have this problem. I have a routine that will check for these characters, and as long as any string doesn't use all three characters, you can formulate a string using the one of the other delimeters.
Code:
FUNCTION checkforquotes
PARAMETERS p_cString, p_lSingleQuote, p_lDoubleQuote, p_lBracket
p_lSingleQuote = AT("'", p_cString) > 0
p_lDoubleQuote = AT('"', p_cString) > 0
p_lBracket = (AT("[", p_cString) > 0) OR (AT("]", p_cString) > 0)

RETURN IIF(p_lSingleQuote,1,0) ;
                  +IIF(p_lDoubleQuote,1,0)+IIF(p_lBracket,1,0)


FUNCTION FORMATSTRING
LPARAMETER p_cString
STORE .F. TO llSingleQuote, llDoubleQuote, llBracket
  lnDelimiterTypes = checkforquotes(p_cString, @llSingleQuote, ;
                               @llDoubleQuote, @llBracket)
   DO CASE
   CASE lnDelimiterTypes = 0 ;
     OR (lnDelimiterTypes = 1 AND (llSingleQuote OR llBracket));
     OR (lnDelimiterTypes = 2 AND !llDoubleQuote)
     lcString = ["]+ Alltrim(p_cString)+["]

   CASE lnDelimiterTypes = 1 AND llDoubleQuote ;
     OR (lnDelimiterTypes = 2 AND llDoubleQuote AND llBracket)
     lcString = [']+ Alltrim(p_cString)+[']

   CASE lnDelimiterTypes = 2 AND llSingleQuote AND llDoubleQuote
     lcString = "["+ Alltrim(p_cString)+"]"

    OTHERWISE
      lcString = ""
   ENDCASE
RETURN lcString
examples:
Field has: abc'de - returns: "abc'de"
Field has: abc'de[ - returns: "abc'de["
Field has: abc"de' - returns: [abc"de']
Field has: abc"de - returns: 'abc"de'
Field has: abc"de] - returns: 'abc"de]'
Field has: abc"d[' - returns: <empty>

Rick
 
I am pretty sure that the macro substitution on the REPLACE command is going to be a lot faster than using 7 separate REPLACEs.

Marcia, I don't think so. A while back I did some tests and REPLACE had a negligible effect on speed whereas macrosubstitution did. Trying again, I built loops similar to the two methods discussed here for replacing 7 fields. Replacing 500,000 times singly took 10 seconds. Replacing 500,000 times grouping the fields into a concatenated string and executing in one "&" list took 50 seconds. Five times slower!

Granted we're often just replacing a few fields at a time, but the old wisdom over the years is still true: avoid macrosubstitution when possible and especially so in huge loops.
 
Hi Lee.

lcField + [ WITH '] + ALLTRIM( aMyArray[ lnI ] ) + [']

Just change the delimiters (unless there is a chance that you will have double quotes (") embedded in your field:

Code:
lcField + " WITH [" + ALLTRIM( aMyArray[ lnI ] ) + "]"

If your fields are not guaranteed to all be of character data type, you will also need to add some code to do the conversion and check to see if you need the delimiters.



Marcia G. Akins
 
Hello dbMark.

Marcia, I don't think so. A while back I did some tests and REPLACE had a negligible effect on speed whereas macrosubstitution did. Trying again, I built loops similar to the two methods discussed here for replacing 7 fields. Replacing 500,000 times singly took 10 seconds. Replacing 500,000 times grouping the fields into a concatenated string and executing in one "&" list took 50 seconds. Five times slower!

Well, your post made me curious, so I re-tested this in VFP 9 and my results were the exact opposite of yours. The 8 Replace statements took .32 and the macro subsitution loop took .10

Here is my code if you care to verify this yourself:

Code:
LOCAL lnstart, lnI, lnEnd
USE junk
lnstart = SECONDS()
FOR lnI = 1 to 1000
  APPEND BLANK
  REPLACE fld1 WITH PADL( TRANSFORM( lni ), 10, [0] )
  REPLACE fld2 WITH PADL( TRANSFORM( lni ), 10, [0] )
  REPLACE fld3 WITH PADL( TRANSFORM( lni ), 10, [0] )
  REPLACE fld4 WITH PADL( TRANSFORM( lni ), 10, [0] )
  REPLACE fld5 WITH PADL( TRANSFORM( lni ), 10, [0] )
  REPLACE fld6 WITH PADL( TRANSFORM( lni ), 10, [0] )
  REPLACE fld7 WITH PADL( TRANSFORM( lni ), 10, [0] )
  REPLACE fld8 WITH PADL( TRANSFORM( lni ), 10, [0] )
ENDFOR
lnend = SECONDS()
? lnEnd - lnStart
lnstart = SECONDS()
lcReplace = []
FOR lnI = 1 TO 8
  lcField = [fld] + TRANSFORM( lnI ) 
  lcReplace = lcReplace + IIF( NOT EMPTY( lcReplace ), [, ], [] ) + lcField + [ WITH '] + PADL( lnI, 10, [0] ) + [']
ENDFOR
lcReplace = [REPLACE ] + lcReplace
FOR lnI = 1 TO 1000
  APPEND BLANK
  &lcReplace
ENDFOR
lnend = SECONDS()
? lnEnd - lnStart



Marcia G. Akins
 

Rick
Thank you for the very valuable post. I will look it into this and use it at a later date (At the moment we've gone for the "Quick fix"). I will update this thread when done.

Marcia
I have changed the line as you posted and that is the "Quick fix" I mentioned above. I am also grateful to you for time.

Looking at Rick's post, yes. There may be a time when the user encounters " ' ] so I'm thinking here that this is a MUST.

Looking at dbmarks post, I'm not sure I'll get into that discussion as my APP only converts a memo field with 8 lines into 8 fields so speed here isn't an issue.

Please feel free to continue your findings on this thread!
Thanks again all, your time is much appreciated

Lee
"Alone we can do so little, together we can do so much"

VisFox Version 6 User / Windows ME
 
MarciaAkins,

While I agree with the point you are trying to make, I would respectfully submit that a few aspects of your test are flawed. PADL( lnI, 10, [0] ) is only evaluated 8 times for the macro substitution portion of your test whereas for the replace statements it is reevaluated 8000 times and carries the extra burden of a nested Transform(). There is a slight cache advantage for the macro substitution as well as it is running second in the test.

However, even with these corrections, the macro substitution will still win by a nose (though a slight one).

boyd.gif

 
Hello Craig.

However, even with these corrections, the macro substitution will still win by a nose (though a slight one).

My point, however, was that the macro substitution was going to be faster than eight separate replace statements ;-)

I seem to recall from my days as a computer science student that I/O operations take longer than anything else...



Marcia G. Akins
 
Hi again Craig.

There is a slight cache advantage for the macro substitution as well as it is running second in the test.

I am the curious type, so I ammended my code as follows. Time for the macro substitution was .1 and time for the 8 replaces was .25

Code:
LOCAL lnstart, lnI, lnEnd
USE junk
lnstart = SECONDS()
lcReplace = []
FOR lnI = 1 TO 8
  lcField = [fld] + TRANSFORM( lnI ) 
  lcReplace = lcReplace + IIF( NOT EMPTY( lcReplace ), [, ], [] ) + lcField + [ WITH '] + PADL( lnI, 10, [0] ) + [']
ENDFOR
lcReplace = [REPLACE ] + lcReplace
FOR lnI = 1 TO 1000
  APPEND BLANK
  &lcReplace
ENDFOR
lnend = SECONDS()
? lnEnd - lnStart
lnstart = SECONDS()
FOR lnI = 1 to 1000
  APPEND BLANK
  REPLACE fld1 WITH [0000000001]
  REPLACE fld2 WITH [0000000002]
  REPLACE fld3 WITH [0000000003]
  REPLACE fld4 WITH [0000000004]
  REPLACE fld5 WITH [0000000005]
  REPLACE fld6 WITH [0000000006]
  REPLACE fld7 WITH [0000000007]
  REPLACE fld8 WITH [0000000008]
ENDFOR
lnend = SECONDS()
? lnEnd - lnStart

Marcia G. Akins
 
Why introduce bigger problems to solve a minor one?

The issue with stringdelimiters '," and [] can be solved, when building a replace statement with the array elements instead of the array elements VALUEs:

Code:
lnLines = ALINES( aMyArray, ORDERTEXT )
lcReplace = ''
FOR lnI = 1 TO lnLines
  lcField = 'LINE' + PADL( lnI, 2, '0')
  lcReplace = lcReplace + ', ' + lcField +;
    ' WITH aMyArray['+TRANSFORM(lnI)+'] '
ENDFOR
lcReplace = SUBSTR(lcReplace,3)
Replace &lcReplace

To cite from the lyrics of Dream Warriors' song "My definition of a boombastic jazz style":
replace a replacable replacement with this.

Bye, Olaf.
 
Hi Olaf

Why introduce bigger problems to solve a minor one?
Even though I did mention earlier on in this thread that
Looking at dbmarks post, I'm not sure I'll get into that discussion as my APP only converts a memo field with 8 lines into 8 fields so speed here isn't an issue
I don't think that this is an "introducing bigger problems to solve a minor one" but just a healthy discussion between Marcia and Craig, both of which have helped me out in the past :)

I have no doubt that others visiting this thread who may require a resolution "With speed" may find it beneficial.

By the way, who are the Dream Warriors or am I just getting old?
Thank you for your post Olaf

Lee.....

VisFox Version 6 User / Windows ME
 
Marcia, I was using VFP6 in WinMe so there must be a substantial performance difference between our versions. Here's the code I used, in case you can see some other reason for the discrepancy:
Code:
SET TALK OFF
LOCAL x, xx, xvar
USE junk EXCL
? time()
for x = 1 to 500000
   replace fld1 with x
   replace fld2 with x
   replace fld3 with x
   replace fld4 with x
   replace fld5 with x
   replace fld6 with x
   replace fld7 with x
   replace fld8 with x
next
? time()
for x = 1 to 500000
   xvar=""
   for xx= 1 to 8
      xvar=xvar+IIF( NOT EMPTY(xvar),[, ],[])+"fld"+str(xx,1)+[ WITH ]+ltrim(str(x,8))
   next
   if not empty(xvar)
      xvar="REPLACE "+xvar
      &xvar
   endif
next
? time()
Could the difference be that I turned TALK off? When TALK is on, then the results are reversed with the first routine with individual replaces taking 5 times longer.

dbMark
 
Hello dbMark.

Marcia, I was using VFP6 in WinMe so there must be a substantial performance difference between our versions. Here's the code I used, in case you can see some other reason for the discrepancy:

My results were the same whether SET( [TALK] ) = [ON] or SET( [TALK] ) = [OFF].

It is not surprising to me that the macro substitution took so much longer in your test. Why on earth would you put this line of code:

Code:
xvar=""
for xx= 1 to 8
  xvar=xvar+IIF( NOT EMPTY(xvar),[, ],[])+"fld"+str(xx,1)+[ WITH ]+ltrim(str(x,8))
next

inside the loop? It only needs to be constructed once, before you enter the loop ;-) Also, the IF test is superfluous.

The point I was making is that traditionally, I/O operations (e.g., REPLACE) are the slowest computer operations.

However, I also noticed that your fields were integer data type and mine were character. So I ran a couple of other tests and discovered that this is what determines whether the macro substitution is going to be faster or slower than the separate replace statements. It appears that with integers, the separate replace statements are faster. With character data, the single macro expanded line was faster.

Marcia G. Akins
 
Marcia, I placed the string creation code inside the loop since what was inside each test loop had to reflect the equivalent functionality. Building the replace string had to be included for real world equivalency, at least that was my goal. On the other hand, I see now why one macrosubstitution line could be faster than several separate replaces.

Also, I had already tried both strings and integers and they performed about the same or so I thought.
 
Lee said:
I have no doubt that others visiting this thread who may require a resolution "With speed" may find it beneficial.

My solution was not meant to speed things up, it's merely to solve the string delimiter problem, for which rgbean gave you a function. But that's not needed at all with my modification of Marcia's solution, as that results in replace statements like:
Code:
REPLACE Line01 WITH aMyArray[1], ...
instead of marcias solution, which results in:
Code:
REPLACE Line01 WITH 'O'Brian', ...

That's why I said Marcia introduced a bigger problem by solving a minor one. Marcia solved this by using " instead of ', but this may cause another problem, when your Memo field lines contain ". And although rgbeans function could solve most of these problems - unlikely as it may be - when you have all kinds of string delimiters that would still be a problem. And you have much more code as before just to care for string delimiters.

To add to the discussion about macrosubstitution or not: There was a thread in the german foxpro newsgroup about it and finally wOOdy (FoxPro MVP) came up and said: Why avoid macro substitution? It's one of the killer features of foxpro!

If you care about the compilation overhead in a big loop, you could still create the whole loop programmatically and execute that with execscript, which would reduce to one time compiling. Textmerge or TEXT..ENDTEXT is your friend in programmatically generating such scripts.

Lee said:
By the way, who are the Dream Warriors or am I just getting old? Thank you for your post Olaf

It's not a very well known band, was a one hit wonder here in germany in the 90ies, although no german band. Just a line of the lyrics that popped up in my mind.

Bye, Olaf.
 
OlafDoschke, I agree that macrosubstitution is a killer feature of foxpro. Indispensible! It just should be used judiciously and avoided when there is a simple alternative since those alternatives can be as much as 2-10 times faster.

My personal rule-of-thumb over the years, not necessarily correct of course:

& ... "slow"
() or EVAL() ... 2-3 times faster
{regular line of code} ... 10-11 times faster

Thank you all for your input and time spent testing the suggestions and alternatives. This has been an enlightening discussion.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top