There has to be a faster way

  • Thread starter Robert Flanagan
  • Start date
R

Robert Flanagan

I use code like the following:

N = ActiveDocument.Paragraphs.Count
For I = N To 1 Step -1
If ActiveDocument.Paragraphs(I).Style =
ActiveDocument.Styles("Heading 2") Then
'code to do tasks
End If
Next

However, the document is very long, about 500 pages and the paragraph count
is about 6,500. So, when I run the macro, I go for a long walk or take a
nap. I do the code using "Step -1" as in quite a few instances I am
deleting the heading 2 paragraph if they meet a certain test.

Most of the time is takend doing the check of each paragraph. I put
application.statusbar = I to confirm this. What is a faster way to find the
styles and check? I will often have two heading 2 styles next to each other
and delete one.

Bob
 
G

Graham Mayor

Try

Dim oPara As Paragraph
For Each oPara In ActiveDocument.Paragraphs
If oPara.Style = "Heading 2" Then
With oPara.Range
'Do stuff
End With
End If
Next oPara


--
<>>< ><<> ><<> <>>< ><<> <>>< <>><<>
Graham Mayor - Word MVP


<>>< ><<> ><<> <>>< ><<> <>>< <>><<>
 
T

Tony Jollans

This probably depends on exactly what you are doing, and how many paragraphs
are in Heading 2 style, and how many you are deleting. You are correctly
working from the end of the document backwards because you are deleting
paragraphs - unfortunately, as you have found, it can be painfully slow
using index numbers because each time you execute ...paragraphs(I)... Word,
counts from the beginning of the document.

One way to avoid this would be along the lines of:

Set p = ActiveDocument.Paragraphs.Last
Do
' do your stuff
Set p = p.Previous
Loop while Not p Is Nothing

You will need to do something slightly different when you are deleting
paragraph p, however, perhaps:

Set p = ActiveDocument.Paragraphs.Last
Do
Set q = p.Previous
' do your stuff including deleting p
Set p = q
Loop while Not p Is Nothing
 
R

Robert Flanagan

Graham and Tony, I suspect both approaches will take just as much time as
they involve testing each paragraph for a property. My experience with VBA
is having VBA doing such checking is always far longer than have the
application do the work. Is there a way to return a collection that is just
paragraphs formatted with Heading 2? Some way to do a Find that stores an
array of paragraph numbers that can then be used to have VBA process?

I've written the following, but it doesn't stop! How do I make it stop
after it finds the last occurrence of Heading 2?

Sub FindMacro()
Dim I As Integer
'go to end of document
Selection.EndKey Unit:=wdStory
I = 0

With Selection.Find
'clear find, initialize
.ClearAllFuzzyOptions
.ClearFormatting
.Text = ""
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
.Style = ActiveDocument.Styles("Heading 2")
'set to search upward and stop if none found
.Forward = False
.Wrap = wdFindStop

'loop until all foud
Do
.Execute
I = I + 1
Application.StatusBar = I
Loop
End With
MsgBox I & " heading 2 paragraphs found"
End Sub
 
R

Robert Flanagan

Found it.

Do while .execute = True
'code
Loop

And, setting Application.Screenupdating = false at the start makes it even
faster.

Bob
 
J

Jay Freedman

If the number of Heading 2 paragraphs is only a small fraction of the
total, it will probably be fastest to use the .Find member of a Range
object to locate the next paragraph to work on.

In this case it isn't necessary to work backward from the end of the
document, as the Find operation isn't affected by changes in the
Paragraphs collection. However, it is critical to collapse the Range
object to its end and move it out of the current Heading 2 paragraph
if the paragraph isn't being deleted; otherwise the Find would find
the same paragraph over and over.

Here's a little sample code:

Sub FixHeading2s()
' As a demonstration, delete any Heading 2 paragraph
' that contains the word 'delete', and add a counter
' value to the end of any other Heading 2 paragraph.

Dim oRg As Range
Dim Counter As Integer
Dim strTemp As String

Set oRg = ActiveDocument.Range
With oRg.Find
.Format = True
.Style = ActiveDocument.Styles(wdStyleHeading2)
While .Execute
If InStr(oRg.Text, "delete") Then
oRg.Delete
Else
Counter = Counter + 1
' pull back from paragraph mark
oRg.MoveEnd unit:=wdCharacter, Count:=-1
strTemp = oRg.Text
strTemp = strTemp & " " & CStr(Counter)
oRg.Text = strTemp
' move range to start of next paragraph
oRg.Collapse wdCollapseEnd
oRg.Move unit:=wdCharacter, Count:=1
End If
Wend
End With
End Sub

--
Regards,
Jay Freedman
Microsoft Word MVP
Email cannot be acknowledged; please post all follow-ups to the
newsgroup so all may benefit.
 
T

Tony Jollans

Graham and Tony, I suspect both approaches will take just as much time as
they involve testing each paragraph for a property.

You are making a false assumption about what is taking the time. I did
explain this in my post. Whilst it may not be the best possible, I am sure
you will find my approach considerably faster than your original one.

I did also say the best approach depends on many factors. Using Find, as
suggested by Jay, is certainly one way and it sounds as though it suits your
situation.
 
R

Robert Flanagan

Tony, thanks for the followup message. I had missed the sentence on Word
repeating the count.

I had noticed at one time that Word was exceedingly slow and at other times
fast on what appears to be the same action, specifying the paragraph. I
could not find a cause and simply re-writing the macro got it back to
running. I'll use one of the approaches in this topic to improve the code's
speed.

Bob
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top