PDA

View Full Version : Solved: Check whether a loop has encountered a table and then skip it?



DaVinney
05-17-2011, 06:41 PM
As my routine processes paragraphs using a 'For Each oPara In oRange.Paragraphs' loop, is there a way to detect if the loop encounters a table and then skip over the table entirely, then continue processing?

gmaxey
05-17-2011, 07:29 PM
With a "For Each loop" you are going to have to process every paragraph in the collection whether it is in a table or not. You can ignore paragraphs in table but you can't skip them in the For Each loop:

Sub ScratchMacro()
'A quick macro scratch pad created by Greg Maxey
Dim oPar As Paragraph
For Each oPar In ActiveDocument.Range.Paragraphs
If Not oPar.Range.Information(wdWithInTable) Then
MsgBox oPar.Range.Text
End If
Next
End Sub


You could step through the paragraphs using the range object and skip tables:


Sub ScratchMacro2()
'A quick macro scratch pad created by Greg Maxey
Dim oRng As Word.Range
Dim oSkipRng As Word.Range
Set oRng = ActiveDocument.Paragraphs(1).Range
Do While oRng.Paragraphs(1).Range.End <> ActiveDocument.Range.End
If Not oRng.Information(wdWithInTable) Then
MsgBox oRng.Text
oRng.Collapse wdCollapseEnd
oRng.MoveEnd wdParagraph, 1
Else
Set oSkipRng = oRng.Tables(1).Range
oSkipRng.Collapse wdCollapseEnd
oSkipRng.MoveEnd wdParagraph, 1
Set oRng = oSkipRng.Paragraphs(1).Range
End If
Loop 'Until oRng.End = ActiveDocument.Range.End
'Check the last paragraph
If Not oRng.Information(wdWithInTable) Then
MsgBox oRng.Text
End If
End Sub

DaVinney
05-18-2011, 08:27 AM
Greg, wow, that's enlightening.

I'll need to rethink the loops based on your information and code.

Thanks very much for the alternative method :hi:

Frosty
05-18-2011, 11:38 AM
Yah, there are a lot of ways to approach this. For Each loops are really powerful, but there are also dangers to them (especially if any of your "processing" involves insertion/deletion of a member of the collection).

Check out the .Information object (as Greg has shown) for "detecting" if you're in a table. But you could also (depending on how much the range covers), "split" your big range into small ranges by, basically, doing a For Each loop of each of the Tables, and grabbing the range in front of the table to the last table... for example...

Public Sub AnotherWayToDoHorribleThingsToACat()
Dim oTable As Table
Dim rngWorking As Range
Dim lStart As Long

'get our starting position
lStart = ActiveDocument.Content.Start
'intialize our range
Set rngWorking = ActiveDocument.Content

'get the range of everything in front of the current table
For Each oTable In ActiveDocument.Tables
'redefine the start
rngWorking.Start = lStart
'and the end
rngWorking.End = oTable.Range.Start
'select it
rngWorking.Select
'pause to see what we're doing
Stop
'now reset and on to the next
lStart = oTable.Range.End
Next

'now that we're done, we need everything after the last table to the end of the document
rngWorking.Start = ActiveDocument.Tables(ActiveDocument.Tables.Count).Range.End
rngWorking.End = ActiveDocument.Content.End
'and select it
rngWorking.Select
End Sub

For the record, I'd probably do something more like what Greg has done... but this was just a little bit of a brain teaser, so I tried something different. The "real" answer to your question is in the .Information object.

Obviously you could, instead of .Select ing your discovered range, pass that range to the function which does the "For each paragraph in myrange.paragraphs")... but the most efficient way to attack this really depends on how many tables you're likely to encounter, and how big they are. If, when you run it, it feels "slow" then there may be another way of articulating your processing in a way that doesn't make Word go "hold up, that's a lot of stuff going on"

DaVinney
05-22-2011, 12:56 PM
I studied and incorporated both Greg's and Frosty's methods into my project. Both worked, although I had a some trouble with the 2nd method where it processes the range from the last table to the end of doc. The ranges in my project often change because paragraphs may be deleted. Rather than do a workaround, I used Greg's method for my needs which seems reliable so far.

My project processes a variety of docs that may or may not have a mix of paragraphs within and outside of tables. Currently, I process all tables first and deal with the quirks that happen when there is no paragraph mark at the end of the cell (different thread topic). Then the routine starts again from the top of the doc, but on the 2nd pass I thought it might be worthwhile to skip the tables since they've already been processed. Interestingly, I found that approach to be slower than just reprocessing the whole doc including all the paragraphs within tables, skipping in-table paragraphs based on .information(wdwithintable). In short, skipping over entire tables is somehow (approx. 15%) slower than reprocessing every single paragraph in the doc.

Thanks for the education, suggestions, and code guys :hi:

I learned a lot.

Mike

Frosty
05-22-2011, 05:15 PM
That's interesting, but not totally surprising. Some collections are simply more efficient than others, for what I'm sure are a variety of reasons. That's why a variety of approach strategies can be useful... you aren't necessarily stuck if one approach sort of stinks.

I'm not surprised MS spent more time optimizing the .Paragraphs collection over the .Tables collection. There are so many issues/bugs with Word Tables, whereas a paragraph is one of the 3 primary building blocks of word formatting (the other two being character and section). So, MS probably devoted a lot of resources to getting the .Paragraphs right, and you're seeing the fruits of that labor in what seems counter-intuitive.

Glad to be of help!