Modifying an Excel VBA Script to Work in Word - bash

I have the following VBA code:
Sub test2()
Dim w1 As Worksheet
Dim w2 As Worksheet
Dim k As Long
Dim c As Range
Dim d As Range
Dim strFA As String
Set w1 = Sheets("Sheet1")
Set w2 = Sheets("Sheet2")
w2.Cells.Clear
k = 1
With w1.Range("A:A")
Set c = .Cells.Find("FirstThing", After:=.Cells(.Cells.Count), lookat:=xlWhole)
strFA = ""
While Not c Is Nothing And strFA <> c.Address
If strFA = "" Then strFA = c.Address
If IsError(Application.Match(c.Offset(0, 1).value, w2.Range("A:A"), False)) Then
Set d = .Cells.Find("SecondThing", c, , xlWhole)
w2.Range("A" & k).value = c.Offset(1, 0).value
w2.Range("B" & k).value = d.Offset(0, 1).value
k = k + 1
End If
Set c = .Cells.Find("FirstThing", After:=c, lookat:=xlWhole)
Wend
End With
End Sub
The code works essentially like this:
Look through Sheet1 for a certain phrase.
Once the phrase is found, place the value from the cell one row over in Sheet2
Search for a second phrase.
Place the value from the cell one row over in the cell beside the other value in Sheet2
Repeat
Now. I have the same data that, don't ask me why, is in .doc files. I'd like to create something similar to this code that will go through and look for the first phrase, and place the next n characters in an Excel sheet, and then look for the second phrase and place the next m characters in the row beside the cell housing the previous n characters.
I'm not sure whether it's better to do this with a bash script or whether it's possible to do this with VBA, so I've attached both as tags.

Your question seems to be: "I'm not sure whether it's better to do this with a bash script or whether it's possible to do this with VBA"
The answer to that is: You'd need VBA, especially since this is a *.doc file - docx would be a different matter.
In order to figure out what that is, start by trying to do the task manually in Word. More specifically, how to use Word's "Find" functionality. When you get that figured out, record those actions in a macro to get the starting point for your syntax. The code on the Excel side for writing the data across will essentially stay the same.
You'll also need to decide where the code should reside: in Word or in Excel. That will mean researching how to run the other application from within the one you choose - lots of examples here on SO and on the Internet...

Related

How can I get the position of a CAD part via VBscript?

I want to write a VBScript, which outputs the position of a CAD part (x,y,z). The Input should be the partnumber. This is what I tried.
Sub CATMain()
dim pos(11)
for n = 1 to CATIA.Documents.Count
set Dokument = CATIA.Documents.Item(n)
for i = 1 to Dokument.product.products.Count
set InstDokument = Dokument.product.products.item(i)
If InstDokument = "my_part" Then
msgbox InstDokument.Name
InstDokument.Position.GetComponents pos
msgbox "Origin Point: X= " &pos(9) &" Y= " &pos(10) &" Z= " &pos(11)
End If
next
next
End Sub
I got an error in line 8 column 2. The object does not have the property or method.: Dokument.product
How can I solve that?
There are several problems with your code.
At the root is probably this:
set Dokument = CATIA.Documents.Item(n)
The CATIA documents collection will contain many documents which do not have windows and which the CATIA application maintains for it's own various internal purposes. So it is not assured that CATIA.Documents.Item(n) actually contains a CATProduct.
In most cases one is interested in the current active document, and it is retrieved like this:
Set Dokument = CATIA.ActiveDocument
Otherwise you can test for it
Set Dokument = CATIA.Documents.Item(n)
if typename(Dokument) = "ProductDocument" Then ...
Even after that you have problems. You are comparing the Document object to a string... and other things. Also without recursion you may never find your target instance if it is deeper then at the first level. It may also be possible that search may be a better way to find your instance then "Reading the tree".
Once you have corrected all your infrastructure errors your method for getting the transformation matrix is basically correct.

Optimize performance of Removing Hidden Rows in VBA

I am using the following code to remove hidden/filtered lines after applying autofilters to a big sheet in VBA (big means roughly 30,000 rows):
Sub RemoveHiddenRows()
Dim oRow As Range, rng As Range
Dim myRows As Range
With Sheets("Sheet3")
Set myRows = Intersect(.Range("A:A").EntireRow, .UsedRange)
If myRows Is Nothing Then Exit Sub
End With
For Each oRow In myRows.Columns(1).Cells
If oRow.EntireRow.Hidden Then
If rng Is Nothing Then
Set rng = oRow
Else
Set rng = Union(rng, oRow)
End If
End If
Next
If Not rng Is Nothing Then rng.EntireRow.Delete
End Sub
The code comes from here: Delete Hidden/Invisible Rows after Autofilter Excel VBA
Moreover I read this thread: Speeding Up Code that Removes Hidden Rows on a Sheet
The situation: I have applied 5 different filters to a table consisting of 12 columns, therefore a lot of rows are filtered out (hidden) after the process. When I try to delete those, the code above takes a very long time. In my case I don't know if Excel was still working, so I had to force an exit. That leads to the following question:
Is there any other way than looping through all the hidden rows and deleting them?
An idea which came to my mind was to copy only the remaining unfiltered (that is non-hidden) content to a new sheet and afterwards delete the old sheet, which contains the full information. If so, how can that be done?
I don't think you need to involve another worksheet. Simply copy the rows below the existing Range.CurrentRegion property and then remove the filter and delete the original data.
Sub RemoveHiddenRows()
With Sheets("Sheet10")
With .Cells(1, 1).CurrentRegion
With .Offset(1, 0).Resize(.Rows.Count - 1, .Columns.Count)
If CBool(Application.Subtotal(103, .Columns(1))) Then
.Cells.Copy Destination:=.Cells(.Rows.Count + 1, 1)
End If
.AutoFilter
.Cells(1, 1).Resize(.Rows.Count, 1).EntireRow.Delete
End With
End With
End With
End Sub
You may also receive some good, focused help on this subject by posting on Code Review (Excel).
You can improve performance significantly with a function like this:
Option Explicit
Public Sub deleteHiddenRows(ByRef ws As Worksheet)
Dim rngData As Range, rngVisible As Range, rngHidden As Range
With ws
Set rngData = .UsedRange
With rngData
Set rngVisible = .SpecialCells(xlCellTypeVisible)
Set rngHidden = .Columns(1)
End With
End With
If Not (rngVisible Is Nothing) Then
ws.AutoFilterMode = False
' invert hidden / visible
rngHidden.Rows.Hidden = False
rngVisible.Rows.Hidden = True
' delete hidden and show visible
rngData.SpecialCells(xlCellTypeVisible).Delete
rngVisible.Rows.Hidden = False
End If
End Sub
I tested it on a file with 2 filters applied to it
The function was adapted from the code in this suggestion

Sorting multidimensional array and displaying it

Here's part of my code.
<%
Dim lineData,fso,filea,fileb,filec
s=request.querystring("query")
set fso = Server.CreateObject("Scripting.FileSystemObject")
a(0,0)=0
a(1,0)=" - Entries in File A"
set filea = fso.OpenTextFile(Server.MapPath("FileA.txt"), 1, true)
do until lone.AtEndOfStream
lineData = lcase(filea.ReadLine())
if instr(lineData,s)>0 then
a(0,0)=a(0,0)+1
end if
Loop
a(0,1)=0
a(1,1)=" - Entries in File B"
set fileb = fso.OpenTextFile(Server.MapPath("FileB.txt"), 1, true)
do until mile.AtEndOfStream
lineData = lcase(fileb.ReadLine())
if instr(lineData,s)>0 then
a(0,1)=a(0,1)+1
end if
Loop
a(0,2)=0
a(1,2)=" - Entries in File C"
set filec = fso.OpenTextFile(Server.MapPath("FileC.txt"), 1, true)
do until payne.AtEndOfStream
lineData = lcase(filec.ReadLine())
if instr(lineData,s)>0 then
a(0,2)=a(0,2)+1
end if
Loop
%>
The code essentially looks for the number of entries in a text file. What I need is it to be sorted such that the file with the most number of entries comes first.
Suppose there are 10 entries in FileA, 12 in FileB and 7 in FileC. I'd like the output to be displayed like this:
12 - Entries in File B
10 - Entries in File A
7 - Entries in File C
I'm guessing it won't be too complicated since response.write(a(0,i)&a(1,i)) will work. I just need help with the loop or any sorting method if there is one.
Any help I can get in here will be much appreciated.
This will be a "neo-answer" that should help you get to where you want to go, both in the short- and long-term.
1) First, a suggestion for further reading to help you address this sort of problem in a more general way -- and to help you develop your "chops" as you go. You can Google the term "bubble sort" and get a whole host of interesting and mostly helpful input, but here's a link you probably will find most directly helpful, from a brief series of articles on sorting from the 4 Guys from Rolla site, which back in the day was THE place for quality writing on ASP:
https://web.archive.org/web/20211020153403/https://www.4guysfromrolla.com/webtech/011601-1.shtml
You will see that there is a link to an introductory article at the top of this one that covers one-dimensional array sorting, and I recommend it as well. For one, it introduces another sort method, QuickSort, and having multiple tools in your toolbox is almost never a bad idea. (As you will discover, bubble sorting is often the easiest to envision and implement, but because its performance is essentially linear based on the number of items being sorted, can become a performance problem on larger datasets.) Go ahead, check it out; I'll wait 'til you get back...
2) OK, to give you a more concrete approach to address your specific situation here, if the number of files you're reviewing isn't going to be too large, you can do a sort of "final pass" sort to present your results in the desired order.
First, you'll want to introduce a simple global counting variable up toward the top of your code:
dim intMaxEntries
intMaxEntries = 0
Then, at the end of each of your file-parsing runs, you'll want to check the number of entries against intMaxEntries and update intMaxEntries if the number of entries just read in is greater.
if a(0, 1) > intMaxEntries then
intMaxEntries = a(0, 1)
end if
You'll do right after each file reading loop, so the comparison in the above snippet would be done for a(0, 1), a(1, 1), and a(2, 1). More on that repetitive logic at the end.
After you've done all the file reads, intMaxEntries will have the maximum number of entries you've found in one of the files. Then, you can just step down from that value and print out entry counts in the correct order when they match your countdown:
dim i, j
for i = intMaxEntries to 0 step -1
for j = 0 to ubound(a) 'By default gives the upper bound of the 1st dimen.
if a(j, 1) = i then
Response.Write i & a(j, 2) & "<BR>"
end if
next j
next i
This is more than a bit of a hack, and I would encourage you to opt instead for doing a proper sort of your array so that you have something more generalizably useful, but it will work to get you where you want to go, especially if the number of files -- or the maximum number of entries -- isn't too large. You could also clean up my example by introducing the possibility of breaking out of the loops when all the files are accounted for, but I'll let you figure out if that's necessary.
3) You may have just simplified the codebase to get the concept across more cleanly (for which I applaud you if true), but just in case, I would encourage you to look at ways to modularize your work by building your file reading functionality as a function that is simply called with the file and string comparison information needed. (Also, probably an artifact of your snipping, but the "lone", "mile" and "Payne" references in there don't make sense; assuming those are the FSOs you are instantiating and have just forgotten to change them to fileA, fileB and fileC.)
Hope that helps a bit,
Bret
#bret
Someone else came through.
Here's a code that worked perfectly.
Would this be an example of "bubble sort"?
for k=23 to 0 Step-1
for j=0 to k
if (a(0,j)<a(0,j+1)) then
t1=a(0,j+1)
t2=a(1,j+1)
a(0,j+1)=a(0,j)
a(1,j+1)=a(1,j)
a(0,j)=t1
a(1,j)=t2
end If
next
next
for i=0 to 24
if a(0,i)>0 then
response.write (a(0,i)&a(1,i)&"<br>")
end if
next
set objFSO = Server.CreateObject("Scripting.FileSystemObject")
set objFolder = objFSO.GetFolder(server.mappath("Files"))
set objfiles = objFolder.Files
Function filesearch(name)
set searchname = objFSO.OpenTextFile(server.mappath(filename),1, true)
do until searchname.AtEndOfStream
lineData = lcase(searchname.ReadLine())
if instr(lineData,s)>0 then
instances = instances + 1
end if
Loop
End Function
For Each objFile in objFolder.Files
filesearch(objFile)
Response.Write filename & "<br>" & instances & "<br>" & "<br>"
Next
Set objFolder = Nothing
Set objFSO = Nothing
There are a few rough edges but what really bothers me now is the sorting. Where do I keep the bubble sort code?
EDIT:
I've got it work perfect with the following code.
For Each objFile in objFolder.Files
filesearch(objFile)
i = i + 1
a(0,i) = instances
a(1,i) = filename
Next
I was also wondering if there's anyway I could also write the total number of instances. I was able to do it before with:
for i=0 to 43
entries=entries+a(0,i)
next
I cant seem to make it work now.
EDIT:
Works now with:
for i = 0 to n
entries = entries + a(0,i)
next

Lotusscript : How to sort the field values (an array of words) by their frequency

I would like to sort the field values (strings) by their frequency in lotusscript.
Has anyone an idea to solve this?
Thanks a lot.
Personally I would avoid LotusScript if you can help it. You are going to run into limitations that cannot be worked around.
Regardless of which route you do take, from a performance point of view it is better to have the View indexes do the work.
So you would create a view. The first column would be as follows.
Column Value: The field you want to check.
Sort: Ascending
Type: Categorized
After this you can access the data using the NotesViewNavigator. The related method call is getNextCategory. This will give you a view entry object which you can call ChildCount on to get totals.
For example (Disclaimer: Code written from memory, not guaranteed to run):
Dim sess As New NotesSession
Dim db As NotesDatabase
Dim vw As NotesView
Dim nav as NotesViewNavigator
Dim entryA As NotesViewEntry
Dim entryB As NotesViewEntry
Set db = sess.CurrentDatabase
Set vw = db.GetView("testView")
vw.AutoUpdate = False
Set nav = vw.CreateViewNav
Set entryA = nav.GetFirst
while entryA not Nothing
Set entryB = nav.GetNextCategory(entryA)
if entryB not nothing then
' Do your processing.
' entryB.childCount will give total.
end if
set EntryA = EntryB
Wend
view.AutoUpdate = True
This way the heavy lifting (string sorting, counting) is handled by the View index. So you only need to process the final results.
To answer the op's (old) question directly, the way to do this in LotusScript is both simple and easy:
dim para as string
dim words as variant
dim fq list as long
'get the text to freq-count
para = doc.text '(or $ from somewhere)
'tidy up para by removing/replacing characters you don't want
para = replace(para, split(". , : ; - [ ] ()"), "")
words = split(para) 'or split(words, "delim") - default is space
forall w in words
if iselement(words(w)) then
fq(w) = fq(w) + 1
Else
fq(w) = 1
End forall
'you now have a count of each individual word in the FQ list
'to get the words out and the word frequencies (not sorted):
forall x in fq
print listtag(x) = x
End forall
Thats it. No issue with LotusScript - quick and easy (and lists can be massive). To get a sorted list, you would have to move to an array and do a sort on it or move to a field and let #sort do the job somehow.

Sorting data by groups in Excel

So, I've looked around and tried to solve this on my own. This isn't an absolutely crucial question currently, I just want to know if it could be done.
So let's say I've got a list with some data that looks like
Date Location
01/24/14 H-12
01/25/14 BB-44
01/30/14 G-12
01/29/14 7A-55
01/28/14 NN-15
01/24/14 GG-47
What I want is to be able to sort the data by Location, but I don't want it to be the general way, otherwise I'll end up with 7A-55, BB-44, G-12, H-12, NN-15. I want the data to be sorted so that double letters and single letters are sorted together. E.G. it should be G-12, H-12, BB-44, NN-15, 7A-55 once everything has been sorted.
I've tried creating a custom list sort, but it doesn't work. the way intended. The custom list I tried was A-Z, AA-ZZ, 7A (items were listed out, but for saving space I wrote them like that).
Like I said, this isn't a particularly huge deal if it can't be done, it just would have made it a little easier.
Edit 1 Here is what I would like to be the output
Date Location
01/30/12 G-12
01/24/14 H-12
01/25/14 BB-44
01/24/14 GG-47
01/28/14 NN-15
01/29/14 7A-55
Edit
All of these worked in the regards i wanted to, although if I had to choose a favorite it would be the base 36 number conversion one. That was some real out-of-the-box thinking and the math geek in me appreciated it. Thanks everyone!
Well it works, but is a bit complex, so rather just for fun:
This UDF returns a value that can be used as sort key. It transforms the code into a four-digit base 36-number, i.e. using A-Z and 0-9 as symbols (like hex uses 0-9 and A-F). To get at your desired output, I literally put the symbols in this order, letters first (so "A" = 0 and "0" = 26).
(The missing 'digits' are filled up with zeros, which are in this case "A"s)
It works ;)
Public Function Base36Transform(r As Range) As Long
Dim s As String, c As String
Dim v
Dim i As Integer
Dim rv As Long
v = Split(r.Text, "-")
s1 = v(0)
s2 = v(1)
s = Right("A" & s1, 2) & Right("A" & s2, 2)
rv = 0
For i = 1 To Len(s)
c = Mid(s, Len(s) - i + 1, 1)
If c Like "#" Then
rv = rv + (Val(c) + 26) * (36 ^ (i - 1))
Else
' c is like "[A-Z]"
rv = rv + (Asc(c) - Asc("A")) * (36 ^ (i - 1))
End If
Next
Base36Transform = rv
End Function
Sorting is often a very creative process. VBA can ease up the process, but a little extension of the data will work just as well.
See my results:
The way I did it is by getting the length of each string, just to be safe. This is gotten by simply going =LEN(B2), dragged down.
Then I check if it starts with 7. If it does, assign 1, otherwise keep at 0. I used this formula: =(LEFT(B2,1)="7")*1, dragged down.
Now, my custom sort is this:
Now I might have gotten some things wrong here, or I might even have done overkill by going the Length column. However, the logic is pretty much what you're aiming for.
Hope this helps in a way! Let us know. :)
I am a little lazy here and assuming your data sits in Column A,B. You mightneed to adjust your range or the starting point of your list. But here's the code:
Sub sortttttt()
Dim rng As Range
Dim i As Integer
Range("B2").Activate
Do While Not IsEmpty(ActiveCell)
ActiveCell.Value = Len(ActiveCell.Value) & ActiveCell.Value
ActiveCell.Offset(1, 0).Activate
Loop
Set rng = Range("A1:B6")
rng.Sort Key1:=Range("B2"), Order1:=xlAscending, Header:=xlYes
Range("B2").Activate
Do While Not IsEmpty(ActiveCell)
ActiveCell.Value = Right(ActiveCell.Value, Len(ActiveCell.Value) - 1)
ActiveCell.Offset(1, 0).Activate
Loop
End Sub
Assuming your data is in columns B:C with labels in Row1 and no intervening blank rows, add a column with:
=IF(ISNUMBER(VALUE(LEFT(C2))),3,IF(FIND("-",C2)>2,2,1))
in D1 copied down to suit and sort ascending Location within sort ascending of the added column.

Resources