How can I concatenate endnotes in a Word doc paragraph? - vba

I have a *.docx file with more than 4000 endnotes. My task is to combine endnotes inside every paragraph (where there is 3 and more endnotes) to one big endnote.
For example.
Input:
word word{1} word{2} word word{3} word.
Endnotes:
{1} endnote
{2} endnote
{3} endnote
Output:
word word word word word word{1}.
Endnotes:
{1} endnote; endnote; endnote
So I'll have one big endnote instead of several small ones.
What I have tried:
Sub Macro1()
Dim i As Integer
Dim t As String
Dim tmp As String
tmp = ""
For i = 1 To Selection.Endnotes.Count Step 1
t = Selection.Endnotes(i).Range.Text
If tmp = "" Then
tmp = Selection.Endnotes(i).Range.Text
Else
tmp = tmp & "; " & Selection.Endnotes(i).Range.Text
End If
Next i
For i = Selection.Endnotes.Count To 1 Step -1
Selection.Endnotes(i).Delete
Next i
Selection.Expand wdParagraph
ActiveDocument.Endnotes.Add Range:=Selection.Range, Text:=tmp
End Sub
It works but a user must select proper paragraph by hand. What I don't know is how to do this process automatically - find bad paragraphs with more than 3 endnotes and apply the code above to these paragraphs. ActiveDocument.Paragraphs item don't have Endnotes property.
Please help)

So the decision is:
Sub Macro1()
Dim i As Integer
Dim t As String
Dim tmp As String
Dim p As Paragraph
For Each p In ActiveDocument.Paragraphs
If p.Range.Endnotes.Count > 3 Then
tmp = ""
For i = 1 To p.Range.Endnotes.Count Step 1
t = p.Range.Endnotes(i).Range.Text
If tmp = "" Then
tmp = t
Else
tmp = tmp & "; " & t
End If
Next i
For i = p.Range.Endnotes.Count To 1 Step -1
p.Range.Endnotes(i).Delete
Next i
ActiveDocument.Endnotes.Add Range:=p.Range, Text:=tmp
End If
Next p
End Sub

Related

How to clean a Word table before saving to a Word bookmark?

I am writing Word VBA that:
(1) assigns values from a Word table to VBA variables,
(2) cleans the variables' values of non-text,
(3) uses the variables' names and values to create Bookmarks in that same bookmark_value cell of the table, and
(4) repeats 1-2-3 until the end of table.
This table is the first table in the document and has two columns, something like this:
_________________________________
| bookmark_name | bookmark_value|
| bm1 | 88 |
| foo | 66 |
|_____bar_______|______44_______|
The code picks up the bookmark_names and posts into Word Bookmarks, and also picks up the bookmark_values but fails to clean the table coding out of the value.
The result is the Bookmarks displaying these unwanted cells in Word with the value inside it. It is strange that first column works and not the second.
Some things I tried:
I found on the Internet and on this site, what I thought were solutions, those are marked in the code below with comments, the header saying, "tried and failed".
I am nearly sure I need to "unformat" the text, or something like that.
Public Sub BookmarkTable()
Dim selectedTable As Table
Dim curRow As Range
Dim rngSelect1 As Range
Dim rngSelect2 As Range
Dim intTableIndex As Integer
Dim rng As Range
Dim Cell1 As Cell, Cell2 As Cell
Dim strBookmarkName As String, strBookmarkValue As String, strBV As String
Dim strTstBookmark As String
Dim Col1 As Integer, Col2 As Integer
Dim i As Integer, t As Integer
Dim intRow As Integer
' Dim
Col1 = 1 'set the bookmark name from column 1
Col2 = 2 'set the bookmark's value from column 2
'For t = 1 To ActiveDocument.Tables.Count
t = 1 'select the Table to use(only using the first table right now)
Set selectedTable = ActiveDocument.Tables(t)
selectedTable.Select 'selects the table
For intRow = 2 To selectedTable.Rows.Count 'iterate through all rows
If Selection.Information(wdWithInTable) Then
Set Cell1 = ActiveDocument.Tables(t).Cell(intRow, Col1)
Set Cell2 = ActiveDocument.Tables(t).Cell(intRow, Col2)
Cell2.Select
intTableIndex = ActiveDocument.Range(0, Selection.Tables(1).Range.End).Tables.Count
rngColumnStart = Selection.Information(wdStartOfRangeColumnNumber)
rngRowStart = Selection.Information(wdStartOfRangeRowNumber)
End If
strTstBookmark = "BM_Table" & CStr(intTableIndex) & "_R" & CStr(rngRowStart) & "_C" & CStr(rngColumnStart)
' strBookmarkValue = strTstBookmark
Set rngSelect1 = ActiveDocument.Range(Start:=Cell1.Range.Start, End:=Cell1.Range.End - 1)
strBookmarkName = Strip(rngSelect1.Text)
Set rngSelect2 = ActiveDocument.Range(Start:=Cell2.Range.Start, End:=Cell2.Range.End - 1)
strBookmarkValue = Strip(rngSelect2.Text)
Set rng = ActiveDocument.Tables(intTableIndex).Cell(rngRowStart, rngColumnStart).Range
rng.End = rng.End - 1
'--------------------------------------------------------------------------
'tried and failed)
'--------------------------------------------------------------------------
'Stop
If ActiveDocument.Bookmarks.Exists(strBookmarkName) = True Then
ActiveDocument.Bookmarks(strBookmarkName).Delete
End If
If ActiveDocument.Bookmarks.Exists(strTstBookmark) = True Then
ActiveDocument.Bookmark(strTstBookmark).Delete
End If
ActiveDocument.Bookmarks.Add Name:=strTstBookmark
ActiveDocument.Bookmarks.Add Name:=strBookmarkName
ActiveDocument.Bookmarks(strBookmarkName).Range.Text = strBookmarkValue
Next intRow
'Next t
End Sub
'--------------------------------------------------------------------------
'tried and failed
Private Function Strip(ByVal fullest As String)
' fuller = Left(fullest, Len(s) - 2)
Strip = Trim(Replace(fullest, vbCr & Chr(7), ""))
End Function
'--------------------------------------------------------------------------
That's truly horrible code you're using. Try:
Sub BkMkDemo()
Application.ScreenUpdating = False
Dim r As Long, BkMkNm As String, BkMkTxt As String
With ActiveDocument
For r = 2 To .Tables(1).Rows.Count
BkMkNm = Split(.Tables(1).Cell(r, 1).Range.Text, vbCr)(0)
BkMkTxt = Split(.Tables(1).Cell(r, 2).Range.Text, vbCr)(0)
If Not .Bookmarks.Exists(BkMkNm) Then .Bookmarks.Add BkMkNm, .Range.Characters.Last
Call UpdateBookmark(BkMkNm, BkMkTxt)
Next
End With
Application.ScreenUpdating = True
End Sub
Sub UpdateBookmark(BkMkNm As String, BkMkTxt As String)
Dim BkMkRng As Range
With ActiveDocument
If .Bookmarks.Exists(BkMkNm) Then
Set BkMkRng = .Bookmarks(BkMkNm).Range
BkMkRng.Text = BkMkTxt
.Bookmarks.Add BkMkNm, BkMkRng
End If
End With
Set BkMkRng = Nothing
End Sub
If all you want to do is to apply the bookmark to the content of the second cell, you need nothing more complex than:
Sub BkMkDemo()
Application.ScreenUpdating = False
Dim r As Long, BkMkNm As String, BkMkRng As Range
With ActiveDocument
For r = 2 To .Tables(1).Rows.Count
BkMkNm = Split(.Tables(1).Cell(r, 1).Range.Text, vbCr)(0)
Set BkMkRng = .Tables(1).Cell(r, 2).Range
BkMkRng.End = BkMkRng.End - 1
.Bookmarks.Add BkMkNm, BkMkRng
Next
End With
Application.ScreenUpdating = True
End Sub
After a great deal of research and learning by this VBA neophyte, here is the solution that I finally got to work. I found the fix by accident on the Windows Dev Center at msdn dot microsoft dot com posted by Cindy Meister...thank you. Turns out there are a combination of three characters needing to be cleaned when extracting text from a Word table cell: Chr(10) & Chr(13), Chr(11).
I simplified the code using the suggestions of macropod above. Thank you.
Sub aBookmarkTable()
'
'a subroutine compiled by Steven McCrary from various sources
'on the Internet, to use values in the second column of the
'first table in a Word document to create Bookmarks in that second
'column, in place of the value input there.
'
'To use the macros, modify the values in the table and run the macro.
'Then place Field Code references in Word to use the Bookmarks.
'The Bookmarks can be seen through Word menu: Insert>Links>Bookmark
'
'The table has just two columns, looking something like this:
'_________________________________
'| bookmark_name | bookmark_value|
'| bm1 | 88 |
'| foo | 66 |
'|_____bar_______|______44_______|
'
'The code places each Bookmark in the second column of each row, using
'the name given in the first column.
'
'The two critical functions of the macro occur in these two lines of code:
' rngBM.End = rngBM.End - 1
' Strip = Replace(fullest, Chr(10) & Chr(13), Chr(11))
'
' both are explained below where they are used.
Application.ScreenUpdating = False
Dim rng1 As Range, rng2 As Range, rngBM As Range
Dim Cell_1 As Cell, Cell_2 As Cell
Dim strBMName As String, strBMValue As String
Dim r As Integer
Call RemoveBookmarks 'removing bookmarks helped to simlify the coding
With ActiveDocument
For r = 2 To .Tables(1).Rows.Count 'iterate through all rows
Set Cell_1 = ActiveDocument.Tables(1).Cell(r, 1)
Set Cell_2 = ActiveDocument.Tables(1).Cell(r, 2)
Cell_2.Select
Set rng1 = .Range(Cell_1.Range.Start, Cell_1.Range.End - 1)
strBMName = Strip(rng1.Text)
Set rng2 = .Range(Cell_2.Range.Start, Cell_2.Range.End - 1)
Set rngBM = ActiveDocument.Tables(1).Cell(r, 2).Range
'When using data contained in a cell of a Word table,
'grabbing the cell's contents also grabs several other
'characters, which therefore need removed in two steps.
'
'The first step is to clean the extra characters from the text.
strBMValue = Strip(rng2.Text)
'
'The second step is to decrease the range size to put in the
'Bookmark.
rngBM.End = rngBM.End - 1
rngBM.Text = strBMValue
.Bookmarks.Add strBMName, rngBM
Next r
End With
Application.ScreenUpdating = True
Selection.WholeStory
ActiveDocument.Fields.Update
End Sub
Sub RemoveBookmarks()
Dim bkm As Bookmark
For Each bkm In ActiveDocument.Bookmarks
bkm.Delete
Next bkm
End Sub
Private Function Strip(ByVal fullest As String)
' the next line of code is the tricky part of the clean
' process because of how Word formats tables and text
' ASCII code Chr(10) is Line Feed
' Chr(13) is Carriage Return
' Chr(13) + Chr(10): vbCrLf or vbNewLine New line character
' Chr (11) is Vertical Tab, but per Word VBA Manual -
' manual line break (Shift + Enter)
'
Strip = Replace(fullest, Chr(10) & Chr(13), Chr(11))
End Function
Thank you again.
SWM

Read a file from text and create loop

I have a text file with some line example
I want to loop through each line until an create format line +1 number all words And of course, a max value must be adjustable
Like this
thanks for all the help
My code
Sub homework()
' Dim words(5) As String
Dim words As String() = File.ReadLines("test.txt")
Dim i As Integer
Dim outerLoop As Integer
Dim innerLoop As Integer
For i = 0 To words.Length ' i dont know how to set file line length
words(i) = words(i) & i + 1
Next i
For outerLoop = 0 To UBound(words)
For innerLoop = 0 To UBound(words)
If outerLoop = innerLoop Then
ListBox1.Items.Add(words(outerLoop) & "-" & words(innerLoop))
End If
Next
Next outerLoop
End Sub
after output
The problem here doesn't read all the lines in my text file, it's only processing last line.
File.ReadLines returns and IEnumerable of String whereas File.ReadAllLines returns a good old Array of String. They play by slightly different rules. Right now you will probably be more comfortable with an array.
Using a For Each loop for the outer loop is a bit easier because you don't have to worry about Index out or Range or getting UBound(words) which translates to words.GetUpperBound(0) in .net.
For the inner loop we can start with 1 and go to the value passed to you method.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
homework(3)
End Sub
Sub homework(UpperLimit As Integer)
Dim words = File.ReadAllLines("C:\Users\***\Desktop\test.txt")
For Each word In words
For i = 1 To UpperLimit
ListBox1.Items.Add(word & i & "-" & word & i)
Next
Next
End Sub
The First loop is not required and modify the second loop like below:
Sub homework()
' Dim words(5) As String
Dim words As String() = File.ReadLines("test.txt")
Dim i As Integer
Dim outerLoop As Integer
Dim innerLoop As Integer
' This Loop is not required.
'For i = 0 To words.Length
' words(i) = words(i) & i + 1
'Next i
For outerLoop = 0 To UBound(words)
For innerLoop = 1 To MaxValue
Dim str as string
str = words(outerloop) & "-" & innerLoop
ListBox1.Items.Add(str & "-" & str)
Next
Next outerLoop
End Sub

Array insertion of Duplicated and not duplicated data to different column in VBA

Good day! in my worksheet i have (1) textbox as TextBox1 and 1 button for submit button. I have here sample code that gives splitted text as an output. I just want that if there's duplicated word in textbox1 and the user enters the submit button it will saves to worksheet(DatabaseStorage) and categorize the output from No Duplicated Word and With duplicated Word. Because this two different fields will be needed for some function of the system.
Private Sub CommandButton1_Click()
Call SplitText
End Sub
Sub SplitText()
Dim WArray As Variant
Dim TextString As String
TextString = TextBox1
WArray = Split(TextBox1, " ")
If (TextString = "") Then
MsgBox ("Error: Pls Enter your data")
Else
With Sheets("DatabaseStorage")
.Cells(.Rows.Count, 1).End(xlUp).Offset(1, 0).Resize(UBound(WArray) + IIf(LBound(WArray) = 0, 1, 0)) = Application.Transpose(WArray)
End With
MsgBox ("Successfully inserted")
End If
End Sub
This should accomplish what you need. I loop through the array to check if the given value exists in the "No Duplicates" column. If not, don't print it there.
Any time I encounter a situation where I need to check a single value against a list (ex. check for duplicates, GT/LT, etc.), I consider looping.
Sub SplitText()
Dim WArray As Variant
Dim TextString As String
Dim col_no_dup As Long
Dim col_dup As Long
Dim counter As Integer
Dim sht_database As Worksheet
With ThisWorkbook
Set sht_database = .Sheets("DatabaseStorage")
TextString = LCase(.Sheets("Sheet1").Shapes("Textbox1").DrawingObject.Text)
End With
WArray = Split(TextString, " ") 'load array
If (TextString = "") Then
MsgBox ("Error: Pls Enter your data")
End
Else: End If
'set column locations for duplicates/no duplicates
col_no_dup = 1
col_dup = 2
With sht_database
.Range("A2:B10000").ClearContents 'clear existing data. Change this as needed
'Print whole array into duplicates column
.Cells(Cells.Rows.Count, col_dup).End(xlUp).Offset(1, 0).Resize(UBound(WArray) + IIf(LBound(WArray) = 0, 1, 0)) = Application.Transpose(WArray)
'Loop through array
For i = LBound(WArray) To UBound(WArray)
counter = 0
lrow_no_dup = .Cells(Cells.Rows.Count, col_no_dup).End(xlUp).Row
For n = 1 To lrow_no_dup 'loop through and check each existing value in the no dup column
If .Cells(n, col_no_dup).Value = WArray(i) Then
counter = counter + 1 'account for each occurence
Else: End If
Next n
If counter = 0 Then 'counter = 0 implies the value doesn't exist in the "No Duplicates" column
.Cells(lrow_no_dup + 1, col_no_dup).Value = WArray(i)
Else: End If
Next i
End With
MsgBox ("Successfully inserted")
End Sub

Reverse text word by word

I want to reverse words in a Word document like this: "elpmaS texT" becomes "Sample Text".
I tried something like this:
For Each word In ActiveDocument.Words
word = StrReverse(word)
Next word
However it doesn't work.
How can I do this?
When you are using a for each loop, you are unable to change the word, so use a for loop:
Dim i As Integer
For i = 1 To ActiveDocument.Words.Count Step 1
ActiveDocument.Words(i) = StrReverse(ActiveDocument.Words(i)) & " "
Next i
Sub ReverseSelectedWords()
Dim i As Integer
Dim oWords As Words
Dim oWord As Range
Set oWords = Selection.Range.Words
For i = 1 To oWords.Count Step 1
Set oWord = oWords(i)
''Make sure the word range doesn't include a space
Do While oWord.Characters.Last.text = " "
Call oWord.MoveEnd(WdUnits.wdCharacter, -1)
Loop
Debug.Print "'" & oWord.text & "'"
oWord.text = StrReverse(oWord.text)
Next i
End Sub

Word VBA Range from Words Object

Context: I'm writing a Word VBA macro that loops through each word in a document, identifies the acronyms, and creates an acronym list as a new document. The next step is to identify whether the acronym is in parentheses (meaning it's likely spelled out) on its first occurrence. So, I'd like to expand the range to find out whether the characters on either side of the word are "(" and ")".
Issue: I can't figure out how to assign the range of the word to a range variable that I can then expand. Using "rngWord = ActiveDocument.Words(k)" (where k is the counter variable) gets the error #91, Object Variable or With block variable not set. So presumably there's a method or property for Words that I'm missing. Based on Microsoft's VBA reference, though, the members of the Words collection are already ranges, so I'm stumped on why I can't assign one to a range variable.
Dim intArrayCount As Integer
Dim booAcroMatchesArray As Boolean
Dim intNextAcro As Integer
Dim strAcros(1000) As String
Dim strContext(1000) As String
Dim booAcroDefined(1000) As Boolean
Dim strTestMessage As String
i = 1
booAcroMatchesArray = False
intNextAcro = 1
For k = 1 To ActiveDocument.Words.Count
strWord = ActiveDocument.Words(k).Text
rngWord = ActiveDocument.Words(k) //The line that's missing something
MsgBox strWord
rngWord.Expand Unit:=wdCharacter
strWordPlus = rngWord
MsgBox strWordPlus
strWord = Trim(strWord)
If strWord = UCase(strWord) And Len(strWord) >= 2 And IsLetter(Left(strWord, 1)) = True Then
'MsgBox ("Word = " & strWord & " and Length = " & Len(strWord))
For intArrayCount = 1 To 1000
If strWord = strAcros(intArrayCount) Then booAcroMatchesArray = True
Next intArrayCount
'MsgBox ("Word = " & strWord & " Match = " & booAcroMatchesArray)
If booAcroMatchesArray = False Then
strAcros(intNextAcro) = strWord
intNextAcro = intNextAcro + 1
End If
booAcroMatchesArray = False
End If
Next k
Object variables need to be assigned using Set. Instead of:
rngWord = ActiveDocument.Words(k)
use
Set rngWord = ActiveDocument.Words(k)
This small sample worked correctly:
Sub WordRangeTest()
Dim rngWord As Range
Set rngWord = ActiveDocument.Words(1)
MsgBox (rngWord.Text)
End Sub