Consulting

Results 1 to 19 of 19

Thread: save find all to an array

  1. #1
    VBAX Regular
    Joined
    Oct 2006
    Posts
    30
    Location

    Exclamation save find all to an array

    I want to search a word document for all instances of a certain word, for example "hello" and save the results of the search as an array of .range objects so I can manipulate it later in macro. I've done it in other apps but can't get it to work in ms word.
    Thanks for yor time

  2. #2
    VBAX Master TonyJollans's Avatar
    Joined
    May 2004
    Location
    Norfolk, England
    Posts
    2,291
    Location
    It would be nice, wouldn't it? But I'm afraid you can't do it - except by building the array manually one find result at a time.
    Enjoy,
    Tony

    ---------------------------------------------------------------
    Give a man a fish and he'll eat for a day.
    Teach him how to fish and he'll sit in a boat and drink beer all day.

    I'm (slowly) building my own site: www.WordArticles.com

  3. #3
    VBAX Wizard
    Joined
    May 2004
    Posts
    6,713
    Location
    Is that not what is being asked? To build that array?

    In a code module:[vba]Option Explicit
    Public myRanges() As Range

    Sub BuildRangeArray()
    Dim i As Integer

    Selection.HomeKey Unit:=wdStory
    With Selection.Find
    Do While (.Execute(findtext:="Hello", _
    Forward:=True) = True) = True
    ReDim Preserve myRanges(i)
    Set myRanges(i) = Selection.Range
    i = i + 1
    Loop
    End With
    End Sub

    Sub TestRanges()
    myRanges(4).Select
    End Sub
    [/vba]The Sub TestRanges will select the Range of the fifth instance of "Hello"....assuming of course there is one. Oh, and assuming that the Sub BuildRangeArray was executed first!

    Error trapping is required!

  4. #4
    VBAX Wizard
    Joined
    May 2004
    Posts
    6,713
    Location
    Yeah, and you probably would put a .ClearFormatting in there as well....

  5. #5
    VBAX Wizard
    Joined
    May 2004
    Posts
    6,713
    Location
    Oh, and if the array was to be used for other code - which of course it could be, otherwise what would be the point? - one may have to be careful. The Ranges that are in the array are the ranges of only the found FindText string. They do not contain any leading or following spaces.

  6. #6
    VBAX Regular
    Joined
    Oct 2006
    Posts
    30
    Location
    Thanks All for the reply. I was really looking to do so without a loop since many times I am dealing with large documents with 30000 or more results and the loop goes pretty slow.
    Thanks all

  7. #7
    VBAX Master TonyJollans's Avatar
    Joined
    May 2004
    Location
    Norfolk, England
    Posts
    2,291
    Location
    Might I ask why you need to build the array? Why can't you just find them you need them. Also what processing it is you are doing exactly. This is more than idle curiosity - there might be a better way to do what you want given that performance is an issue.
    Enjoy,
    Tony

    ---------------------------------------------------------------
    Give a man a fish and he'll eat for a day.
    Teach him how to fish and he'll sit in a boat and drink beer all day.

    I'm (slowly) building my own site: www.WordArticles.com

  8. #8
    Knowledge Base Approver
    The King of Overkill!
    VBAX Master
    Joined
    Jul 2004
    Location
    Rochester, NY
    Posts
    1,727
    Location
    As Tony is suggesting, there are other ways of doing this. Instead of using Word's find method, you could use regular expressions (you may not even need the array if you want to just use the Match Collection). Heres an example, the bulk of the runtime is just creating a document with "The quick brown fox jumped over the lazy dogs. " 10,000 times:[vba]Sub qazplFindArray()
    Dim i As Long, vStr As String, MatchArr() As Range
    Dim RegEx As Object, RegC As Object, RegM As Match
    Dim vTimer As Double

    Documents.Add
    For i = 1 To 10000
    Selection.TypeText "The quick brown fox jumped over the lazy dogs. "
    Next

    'put document text into vStr variable
    Selection.WholeStory
    vStr = Selection.Text

    'initialize RegExp object
    Set RegEx = CreateObject("vbscript.regexp")
    With RegEx
    .Global = True
    .MultiLine = True
    .IgnoreCase = True
    .Pattern = "The"
    End With
    'begin timer
    vTimer = Timer
    If RegEx.Test(vStr) Then
    Set RegC = RegEx.Execute(vStr) 'RegC now holds a collection of every match
    ReDim MatchArr(RegC.Count - 1)
    i = 0
    For Each RegM In RegC
    Set MatchArr(i) = ActiveDocument.Range(RegM.FirstIndex, _
    RegM.FirstIndex + RegM.Length)

    Next
    End If

    'end timer
    MsgBox "The range array was created in " & Timer - vTimer & " seconds"
    End Sub[/vba]Matt

  9. #9
    VBAX Wizard
    Joined
    May 2004
    Posts
    6,713
    Location
    Matt, very nice.

    gazpl - you post a question on how to search for a word and get an array of Range objects for the results.

    You have a solution. Now, you may have a problem with large document getting into the string variable that Matt uses. He selects the whole document and puts that in the string vStr.

    I am with Tony on this one. You need to specify what it is you are doing, and wanting to do.

    I would also suggest that when you post, that you post relevant requirements. Oh....say that it will be for huge documents and that there may be 30000 possible occurances of the searched for word.....seems a little relevant...hmmmmm?

    In any case, you have two ways to make an array of Range objects that cover all instances of a looked for word. This answers your post.

    If this does not work for you, please explain why not, and give actual details of what you want to do.

    Frankly, I am having difficulties thinking about the usefulness of an array of 30000 Ranges.

  10. #10
    VBAX Regular
    Joined
    Oct 2006
    Posts
    30
    Location
    Thanks all for your response and sorry for my being unclear and delayed. As for Tony's question, the reason I must find all instances is because I am searching a document for all its abbreviations through a wildcard search then sorting them and giving the user a dialog to change all similar abbreviations at one time. since I want to be able to changr the words and retain formatting I want to return a range object.
    It seems like there isn't really a solution like in adobe scripting where "set mySearch = Find....." returns an array of the search. Thanks for the input and please reply anything you can add to help me.

  11. #11
    VBAX Wizard
    Joined
    May 2004
    Posts
    6,713
    Location
    I don't understand. The solutions DO return an array of the search.

    It seems you are mixing logic requirements. The solution, again, DOES return an array of your search. You search for "hello" and you get an array of all ranges of "hello". This is what you asked for. Your logic requirements, though, are more difficult than that.

    The solutions offered - which meet what you asked for - return an array of an explicit search string. YOUR logic is now asking for a variable. OK, that certainly could be done.

    But then that starts building more arrays. The solutions build an array for EACH string.

    Nevertheless, with the solutions, you could get an array (for example) of ranges for all instances of "Can." Then offer the user the ability to change that abbreviation througout the document.

    I used my procedure (although I think Matt has a better one) on a 100 page document with 6,000 + instances of "Can." to build the array myRanges. Then I ran:[vba] For var = 0 To UBound(myRanges())
    myRanges(var).Text = "Canada"
    Next[/vba]It took 37 seconds to change all the instances. As they ARE ranges, all formatting remains the same.

    So I am not sure what exactly your problem is.

  12. #12
    VBAX Regular
    Joined
    Oct 2006
    Posts
    30
    Location
    fumai, you are right the solutions provided above do work.
    However I am looking for something faster which would be similar to the find all instances which is available from word xp and on. With such a search it would only take one line of code to fill an array. That is why I used Adobe Scripting to compare, because even though adobe incopy & indesign are slowwww saving a search to an array is considerably quicker with one line of code.
    if I am still confusing you let me know and I will try again to explain my issue Thanks Fumei for your passion

  13. #13
    VBAX Wizard
    Joined
    May 2004
    Posts
    6,713
    Location
    Sorry, but I am finding this more and more pointless.

    You have not given any real reason for doing this. I am also a bit off with your statement:
    However I am looking for something faster which would be similar to the find all instances which is available from word xp and on
    Does that mean you are using 2000 or earlier? These pieces of information are important to tell us when you post.

    I also think it very strange that you are trying to compare Adobe InDesign with Word. If this is what you are doing, then tell us...

    Give me real numbers. HOW slow? What IS the difference? These vague comparisons, and vague (or NO) explanations of what you are wanting to do - REALLY want to do. Besides, it is silly to compare a layout application (Adobe InDesign) with a word processor (Word). They are very very different beasts.

    Well, I think I am done here.

    The bottom line is - I don't see the point of what you are doing, you have been given exactly what you asked for, and so.....

    Good luck.

  14. #14
    Knowledge Base Approver
    The King of Overkill! VBAX Master
    Joined
    Jul 2004
    Location
    Rochester, NY
    Posts
    1,727
    Location
    Gerry,
    Agreed.

    However after looking above I seem to have messed something up (though the version above doesnt match exactly what I have in my normal.dot file..?).
    Anyways, I changed it to be a function:[vba]Function FindAll(ByRef vDoc As Document, ByVal vPattern As String, Optional _
    ByVal vMatchCase As Boolean = False) As Range()
    Dim i As Long, vStr As String, mArr() As Range, RegEx As Object, RegC As Object

    'put document text into vStr variable
    ReDim mArr(0)
    Set mArr(0) = Selection.Range
    Selection.WholeStory
    vStr = Selection.Text
    mArr(0).Select

    'initialize RegExp object
    Set RegEx = CreateObject("vbscript.regexp")
    With RegEx
    .Global = True
    .MultiLine = True
    .IgnoreCase = Not vMatchCase
    .Pattern = vPattern
    End With
    ReDim mArr(0)
    If RegEx.Test(vStr) Then
    Set RegC = RegEx.Execute(vStr)
    ReDim mArr(RegC.Count - 1)
    For i = 0 To RegC.Count - 1
    With RegC.Item(i)
    Set mArr(i) = ActiveDocument.Range(.FirstIndex, .FirstIndex + .Length)
    End With
    Next
    End If
    FindAll = mArr
    Set RegC = Nothing
    Set RegEx = Nothing
    Erase mArr
    End Function[/vba]
    So if you have that in your project, you can get all the matched ranges (including wildcards if you want, using Regular Expressions (link is excel example but same patterns) in a single line:[vba]Sub GetTheRangeExample()
    Dim RG() As Range, vTimer As Double
    vTimer = Timer
    RG = FindAll(ActiveDocument, "the")
    vTimer = Timer - vTimer
    MsgBox "Found " & CStr(UBound(RG) + 1) & " instances of 'the' in document." & _
    vbCrLf & "Function took " & Format(vTimer, "0.00") & " seconds."
    Stop
    'erasing your range array (either using this or when it hits 'end sub'
    ' could take a while depending on the size of it!
    Erase RG
    End Sub[/vba]The 'single line' [vba] RG = FindAll(ActiveDocument, "the")[/vba]obviously is calling the function, but you can use just the one line in multiple subs.
    Matt

  15. #15
    VBAX Wizard
    Joined
    May 2004
    Posts
    6,713
    Location
    Sorry, but that is so lame. All that so you can write it as a single line? Hmmmm.

    1. You can do the very same thing using a Sub - not a Function - and call it with a single line.[vba] Call FindAll(ActiveDocument, "the")[/vba]I tested both and they are identical in terms of speed in building the array. Which makes sense as the Sub and the Function use the same instructions.

    2. Using the function in a Sub creates RG which is the array of ranges. RG itself has no properties. To do anything you have to use RG(x). So again, what are you DOING with this????

    3. Using the function in a Sub allows the use of RG(x) only within the scope of that Sub. So it is still much better to make RG a Public variable...then you can use it anywhere, including multiple Subs.

  16. #16
    Knowledge Base Approver
    The King of Overkill! VBAX Master
    Joined
    Jul 2004
    Location
    Rochester, NY
    Posts
    1,727
    Location
    (I think you confused me with the OP--was just giving another example)
    EDIT: Also, the function could be good if you wanted to search for all instances of "fumei" and save that to one array, and save all instances of "mvidas" and save it to another array. Using a sub with a public variable could limit it's use (IMO)

  17. #17
    VBAX Wizard
    Joined
    May 2004
    Posts
    6,713
    Location
    Ooops, sorry.

    This is why I have been trying to find out what the purpose of this is.

    The purpose of a public variable is to expand the ability to DO something with the array.

    Yes, you could make different arrays for "fumei" and "mvidas". However, you would still have to declare them as separate arrays, and you would still only have them in scope of the sub that created them.

    I am not disagreeing with you...it totally depends on what is the USE of the array.

    I must admit though...I learned a bit from this thread. Which is always good.

  18. #18
    Knowledge Base Approver
    The King of Overkill! VBAX Master
    Joined
    Jul 2004
    Location
    Rochester, NY
    Posts
    1,727
    Location
    While I'm as confused as you, often times I know I like to ask questions about things that won't actually accomplish anything other than knowing it can be done.
    For what this could be used for (again, dont know why...):[vba]Sub GetTheRangeExample()
    Dim fumeiRG() As Range, mvidasRG() As Range, tRG1 As Range, tRG2 As Range
    Dim i As Long, j As Long
    Documents.Add
    Selection.TypeText GetWebIE("http://www.vbaexpress.com/forum/showthread.php?p=76518")
    fumeiRG = FindAll(ActiveDocument, "fumei")
    mvidasRG = FindAll(ActiveDocument, "mvidas")
    For i = 0 To UBound(fumeiRG)
    For j = 0 To UBound(mvidasRG)
    Set tRG1 = fumeiRG(i)
    tRG1.Expand wdParagraph
    Set tRG2 = mvidasRG(j)
    tRG2.Expand wdParagraph
    If tRG1 = tRG2 Then
    tRG1.Select
    MsgBox "Found a paragraph with both words"
    End If
    Next
    Next
    End Sub
    Function GetWebIE(ByVal vWebSite As String) As String
    Dim IE As Object
    Set IE = CreateObject("internetexplorer.application")
    IE.Navigate2 vWebSite
    Do While IE.readyState <> 4 'READYSTATE_COMPLETE
    DoEvents
    Loop
    GetWebIE = IE.Document.Body.InnerText
    Set IE = Nothing
    End Function[/vba]
    Quote Originally Posted by Gerry
    I must admit though...I learned a bit from this thread. Which is always good
    Agreed on both parts

  19. #19
    VBAX Wizard
    Joined
    May 2004
    Posts
    6,713
    Location
    Matt...very neat. Cool.

Posting Permissions

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