Results 1 to 10 of 10

Thread: For Each (XP)

  1. #1
    2 Star Lounger
    Join Date
    Mar 2001
    Location
    Oregon, USA
    Posts
    129
    Thanks
    0
    Thanked 0 Times in 0 Posts

    For Each (XP)

    Hmmm. I ran the following code.

    <pre>For j = ActiveDocument.Tables.Count To 1 Step -1
    sngWid = 6.5 / ActiveDocument.Tables(j).Columns.Count
    For i = 1 To ActiveDocument.Tables(j).Columns.Count
    ActiveDocument.Tables(j).Columns(i).Width = InchesToPoints(sngWid)
    Next
    Next
    </pre>


    On one particular document it took about 4.5 seconds either incrementing or decrementing. I then put it in a For Each Table construct. It took over 9 seconds! Is this common with For Each? Should one always stick to the index?

    Chris

  2. #2
    Gold Lounger
    Join Date
    Dec 2000
    Location
    New Hampshire, USA
    Posts
    3,386
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Re: For Each (XP)

    Try this:

    <pre>Dim lngCount as Long
    Dim tabSomeTable as Table
    With ActiveDocument
    For j = .Tables.Count To 1 Step -1
    Set tabSomeTable = .Tables(j)
    With tabSomeTable
    lngCount = .Columns.Count
    sngWid = InchesToPoints(6.5 / lngCount)
    For i = 1 To lngCount
    .Columns(i).Width = sngWid
    Next
    End With
    Next
    End With
    </pre>


    Then write the corresponding code using For ... Each.

    Which is faster may depend on which version of Word and which Collection.

    P.S. I have not tried the above code.

  3. #3
    Super Moderator jscher2000's Avatar
    Join Date
    Feb 2001
    Location
    Silicon Valley, USA
    Posts
    23,112
    Thanks
    5
    Thanked 93 Times in 89 Posts

    Re: For Each (XP)

    In my test, the For Each is marginally faster for 16 tiny tables (.02 seconds):
    <pre>Sub ColumnIndexTest()
    Dim sngTick As Single
    sngTick = Timer
    Dim sngWid As Single, i As Integer, j As Integer
    For j = ActiveDocument.Tables.Count To 1 Step -1
    sngWid = 6.5 / ActiveDocument.Tables(j).Columns.Count
    For i = 1 To ActiveDocument.Tables(j).Columns.Count
    ActiveDocument.Tables(j).Columns(i).Width = InchesToPoints(sngWid)
    Next
    Next
    MsgBox Timer - sngTick
    End Sub

    Sub ColumnCollectionTest()
    Dim sngTick As Single
    sngTick = Timer
    Dim tbl As Table, col As Column, sngWid As Single
    For Each tbl In ActiveDocument.Tables
    sngWid = 6.5 / tbl.Columns.Count
    For Each col In tbl.Columns
    col.Width = InchesToPoints(sngWid)
    Next
    Next
    MsgBox Timer - sngTick
    End Sub</pre>

    As they say, your mileage may vary! One advantage of the index method is you can control the sequence in which the tables and columns will be edited. With the collection method, Word decides.

  4. #4
    Super Moderator jscher2000's Avatar
    Join Date
    Feb 2001
    Location
    Silicon Valley, USA
    Posts
    23,112
    Thanks
    5
    Thanked 93 Times in 89 Posts

    Re: For Each (XP)

    Moving the InchesToPoints method call makes sense, but when I ran timings, the results were not consistently better.

    <pre>Sub ColumnCollectionTest()
    Dim sngTick As Single
    sngTick = Timer
    Dim tbl As Table, col As Column, sngWid As Single
    For Each tbl In ActiveDocument.Tables
    sngWid = InchesToPoints(6.5 / tbl.Columns.Count)
    For Each col In tbl.Columns
    col.Width = sngWid
    Next
    Next
    Debug.Print Timer - sngTick
    End Sub</pre>

    This is the part of programming where you say "whatever."

  5. #5
    Gold Lounger
    Join Date
    Dec 2000
    Location
    New Hampshire, USA
    Posts
    3,386
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Re: For Each (XP)

    Both sets of code need to minimize object references, otherwise, the test is biased against the For Next
    Also, other coding factors need to be equalized. Try the following (not tested):
    <pre>Option Explicit

    Sub ColumnIndexTest()
    Dim sngTick As Single
    Dim sngWid As Single
    Dim tbl As Table

    sngTick = Timer

    Dim i As Long
    Dim j As Long
    Dim lngCols As Long

    With ActiveDocument
    For j = 1 To .Tables.Count
    Set tbl = .Tables(j)
    With tbl
    lngCols = .Columns.Count
    sngWid = 6.5 / InchesToPoints(lngCols)
    For i = 1 To lngCols
    .Columns(i).Width = sngWid
    Next
    End With
    Next
    End With
    MsgBox Timer - sngTick
    End Sub

    Sub ColumnCollectionTest()
    Dim sngTick As Single
    Dim tbl As Table
    Dim sngWid As Single

    sngTick = Timer

    Dim col As Column

    For Each tbl In ActiveDocument.Tables
    With tbl
    sngWid = InchesToPoints(6.5 / .Columns.Count)
    For Each col In .Columns
    col.Width = sngWid
    Next
    End With
    Next
    MsgBox Timer - sngTick
    End Sub
    </pre>


  6. #6
    Gold Lounger
    Join Date
    Dec 2000
    Location
    New Hampshire, USA
    Posts
    3,386
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Re: For Each (XP)

    Here's code with more accurate timing. For ... Each is much faster.

    T2: 3074 (slower due to not yet compiled)
    T2: 2473
    T2: 2524
    T1: 5838
    T1: 5789
    T1: 5838
    T1: 5899

    <pre>Option Explicit
    Private Declare Function GetTickCount Lib "kernel32" () As Long

    Sub ColumnIndexTest()
    Dim sngWid As Single
    Dim tbl As Table

    Dim lngTimeEnd As Long
    Dim lngTimeStart As Long
    lngTimeStart = GetTickCount()

    Dim i As Long
    Dim j As Long
    Dim lngCols As Long

    With ActiveDocument
    For j = 1 To .Tables.Count
    Set tbl = .Tables(j)
    With tbl
    lngCols = .Columns.Count
    sngWid = 6.5 / InchesToPoints(lngCols)
    For i = 1 To lngCols
    .Columns(i).Width = sngWid
    Next
    End With
    Next
    End With
    lngTimeEnd = GetTickCount()
    Debug.Print "T1: " & lngTimeEnd - lngTimeStart
    End Sub

    Sub ColumnCollectionTest()
    Dim sngWid As Single
    Dim tbl As Table

    Dim lngTimeEnd As Long
    Dim lngTimeStart As Long
    lngTimeStart = GetTickCount()

    Dim col As Column

    For Each tbl In ActiveDocument.Tables
    With tbl
    sngWid = InchesToPoints(6.5 / .Columns.Count)
    For Each col In .Columns
    col.Width = sngWid
    Next
    End With
    Next
    lngTimeEnd = GetTickCount()
    Debug.Print "T2: " & lngTimeEnd - lngTimeStart
    End Sub
    </pre>


  7. #7
    Gold Lounger
    Join Date
    Dec 2000
    Location
    New Hampshire, USA
    Posts
    3,386
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Re: For Each (XP)

    The code you used is not an unbiased test of the differences.
    See the code I posted.

  8. #8
    2 Star Lounger
    Join Date
    Mar 2001
    Location
    Oregon, USA
    Posts
    129
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Re: For Each (XP)

    Thanks for responding, guys. Interesting little lesson, here.

    I took my work machine home. Wanted to test on the same file. It's got 48 tables, some of them rather long. It's also got a lot of embedded graphics. Original file size over 5 Mb. I stripped out the graphics to get a svelte 450 Kb file so I could get it on floppy.

    After stripping out the graphics, there's no appreciable difference. A hundredth of a second or so between the two routines. It looks like graphics/file size really slows down the collection method (in this case, twice as long).

    Also, I was surprised at the nominal difference between the two machines on the file without graphics. Home machine: Win 98 SE, AMD K3 600 MHz, 192 MB, Word 2K ran at 3.5 seconds. Work machine Win XP, P4 1.7 GHz, 512 MB, Word XP ran at 3.3 seconds.

    <pre>Sub TestInc()

    Dim i As Integer, j As Integer
    Dim Begin: Begin = Timer
    Dim sngWid As Single

    For j = ActiveDocument.Tables.Count To 1 Step -1
    sngWid = 6.5 / ActiveDocument.Tables(j).Columns.Count
    For i = 1 To ActiveDocument.Tables(j).Columns.Count
    ActiveDocument.Tables(j).Columns(i).Width = InchesToPoints(sngWid)
    Next
    Next

    MsgBox "It took " & Timer - Begin & " seconds."

    End Sub

    Sub TestFor()
    Dim oTab As Table
    Dim i As Integer, j As Integer
    Dim Begin: Begin = Timer
    Dim sngWid As Single

    For Each oTab In ActiveDocument.Tables
    sngWid = 6.5 / oTab.Columns.Count
    For i = 1 To oTab.Columns.Count
    oTab.Columns(i).Width = InchesToPoints(sngWid)
    Next
    Next

    MsgBox "It took " & Timer - Begin & " seconds."

    End Sub
    </pre>


  9. #9
    2 Star Lounger
    Join Date
    Mar 2001
    Location
    Oregon, USA
    Posts
    129
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Re: For Each (XP)

    Ahh. I see it now. When I first tested, I didn't compile before testing. It was the compile time that caused the 9 to 4.5 second disparity. After compile, both your code and the stuff I wrote runs faster with For Each. Thanks.

  10. #10
    Gold Lounger
    Join Date
    Dec 2000
    Location
    New Hampshire, USA
    Posts
    3,386
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Re: For Each (XP)

    In general, For Each will run faster if you need to operate on ALL the members of a collection.
    However, there are cases in which one MUST use For ... Next instead of For Each ... Next.

    Note also that when operating on a collection, it is usually faster to step forward, rather than backward.
    Stepping backward is only necessary if:
    1. Some aspect of your algorithms requires walking backwards.
    2. Removing some/all members of some/all collections. Cannot generalize this.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •