Word VBA Array not behaving as expected - arrays

I'm iterating through a list of keywords to define terms in a document but only certain keywords will get picked up.
For instance, with Array("Agreement", "deed", "AGREEMENT", "letter agreement", "letter", "Undertaking"), "Agreement" and "letter" get picked up just fine, but letter agreement and Undertaking do not.
I've tried rearranging the order of the array but that does nothing.
I'm guessing there's something fundamental about arrays I'm misunderstanding. I'm more familiar with python and am going for list functionality.
Full code is below. Any pointers would be very much appreciated.
Function getagree() As String
Dim aggrlist As Variant
aggrlist = Array("Agreement", "NDA", "deed", "AGREEMENT", "letter
agreement", "letter", "Undertaking", "Confidentiality Undertaking",
"agreement")
Set myRange = ActiveDocument.Content
With myRange.Find
For Each aggr In aggrlist
.ClearFormatting
.Text = aggr
.MatchWholeWord = True
.MatchCase = True
.Execute Forward:=True
If .Found = True Then
getagree = aggr
End If
Next
End With
End Function

Try using an underscore (_) to break your string into multiple lines...
aggrlist = Array("Agreement", "NDA", "deed", "AGREEMENT", _
"letter agreement", "letter", "Undertaking", _
"Confidentiality Undertaking", "agreement")

Related

VBA Access parsing JSON nested array

I am using VBA Access to get data from Google Books for a library database. The code is based on that given in this stackoverflow question.
I am struggling for the right code to allow for a varying number of authors as the information is in a nested array. I would like all of the author names to appear in one TextBox.
I tried:
Form_AddAmendItems.AuthorTextBox.Value = Join(subitem("authors"), ",")
from the link above but that fails to find any result.
I think I need to use UBound and LBound to count the number of authors and then loop through and add each one. But I haven't been able to find an example of how to do that.
Currently as a workaround I can populate the AuthorTextBox with the names of up to 3 authors, which is enough for my needs. But if there are less than 3 authors the error handler message pops up because it hasn't been able to find the requested data.
I am using the VBA-JSON Converter from here.
This is the JSON I would like to parse (from here)
{
"kind": "books#volumes",
"totalItems": 1,
"items": [
{
"kind": "books#volume",
"id": "BT2CAz-EjvcC",
"etag": "6Z7JqyUtyJU",
"selfLink": "https://www.googleapis.com/books/v1/volumes/BT2CAz-EjvcC",
"volumeInfo": {
"title": "Collins Gem German Dictionary",
"subtitle": "German-English, English-German",
"authors": [
"Veronika Calderwood-Schnorr",
"Ute Nicol",
"Peter Terrell"
]
And this is my VBA code:
Private Sub FindBookDetailsButton_Click()
'Error handle for Null Strings
If IsNull(Me.ISBNTextBox) = True Then
MsgBox "Item ID not specified.", vbExclamation + vbOKOnly, "Error"
Exit Sub
End If
'Error message if there is no match
On Error GoTo ErrMsg
Dim http As Object, JSON As Object, i As Integer, subitem As Object
Dim ISBN As String
ISBN = CStr(Me.ISBNTextBox.Value)
Set http = CreateObject("MSXML2.XMLHTTP")
http.Open "GET", "https://www.googleapis.com/books/v1/volumes?q=isbn:" & ISBN, False
http.send
Set JSON = ParseJSON(http.responseText)
For Each item In JSON("items")
Set subitem = item("volumeInfo")
Form_AddAmendItems.TitleTextBox.Value = subitem("title")
Form_AddAmendItems.AuthorTextBox.Value = subitem("authors")(1)
Form_AddAmendItems.PublisherTextBox.Value = subitem("publisher")
'For multiple authors
Set subitem = item("volumeInfo")
If subitem.Exists("authors") Then
For Each item2 In subitem("authors")
Form_AddAmendItems.AuthorTextBox.Value = subitem("authors")(1) & ", " & subitem("authors")(2)
Next
For Each item3 In subitem("authors")
Form_AddAmendItems.AuthorTextBox.Value = subitem("authors")(1) & ", " & subitem("authors")(2) & ", " & subitem("authors")(3)
Next
End If
Next
'To end with success
MsgBox ("Process complete"), vbInformation
Exit Sub
'To end with an error message
ErrMsg:
MsgBox ("No match obtained"), vbCritical
End Sub
An array or collection or dictionary can be looped without knowing that object's limits. So if subitem("authors") is one of those object types, your code could be something like (essentially the code shown in accepted answer for the SO link in your question):
Set subitem = item("volumeInfo")
Form_AddAmendItems.TitleTextBox.Value = subitem("title")
Form_AddAmendItems.PublisherTextBox.Value = subitem("publisher")
If subitem.Exists("authors") Then
For Each item2 In subitem("authors")
sA = sA & item2 & ","
Next
sA = Left(sA, Len(sA) - 1) 'remove trailing comma
If sA <> "" Then Form_AddAmendItems.AuthorTextBox.Value = sA
End If
I discovered that elements in the ISBN download aren't always the same. As an example, with ISBN 0-575-05577-4 the publisher is not provided even though publishedDate is. Might use an If Then condition for each element you want to explicitly address. As a side note, found it interesting that the co-author for that book was not included.

How could I do the sum of all values of a nested hash?

I have a nested hash like this
Aranea={
"Aranéomorphes"=>{
"Agelenidae"=>[80,1327],
"Amaurobiidae"=>[49,270],
"Ammoxenidae"=>[4,18],
"Anapidae"=>[58,233],
"Anyphaenidae"=>[56,572],
"Araneidae"=>[175,3074],
"Archaeidae"=>[5,90],
"Arkydiae"=>[2,38],
"Austrochilidae"=>[3,10],
"Caponiidae"=>[18,119],
"Cheiracanthiidae"=>[12,353],
"Cithaeronidae"=>[2,8],
"Clubionidae"=>[16,639],
"Corinnidae"=>[68,489],
"Ctenidae"=>[48,519],......
For each key (spiders families), the array represents [number of genders, number of species].
Iwould like to get the sum of all first elements....i.e all the genders in total....
I tried different things without success like :
genre = []
#total = genre.transpose.map {|x| x.reduce(:+)}
Or....
def sum_deeply(h)
h.values.inject(0) { |m, v|
m + (Hash === v[0] ? sum_deeply(v[0]) : v[0].to_i)
}
end
puts sum_deeply(Aranea)
But none does work for with transpose I get a no implicit conversion error...
Could anyone enligthen me on this ? Thanks
!!! Update.... 08.07.2020... solution found with
families = Aranea
num_genders = families.flat_map do |_family_name, species_hash|
num_genders, _num_species = species_hash.values.transpose
num_genders
Thanks to Kache for his help on this.
This should do what you want:
families = Aranea
num_genders = families.flat_map do |_family_name, species_hash|
num_genders, _num_species = species_hash.values.transpose
num_genders
end
num_genders.inject(:+)
Just a tip: splitting out the "data extraction" and "data processing" (i.e. accessing the num_genders value vs summing them) will make your code easier to follow.
I don't think there'll be any part of the above that you won't understand, but if there is, just let me know what parts you'd like to have explained.

Array.include? myVariable not working as expected

I am coding a Ruby 1.9 script and I'm running into some issues using the .include? method with an array.
This is my whole code block:
planTypes = ['C','R','S'];
invalidPlan = true;
myPlan = '';
while invalidPlan do
print "Enter the plan type (C-Commercial, R-Residential, S-Student): ";
myPlan = gets().upcase;
if planTypes.include? myPlan
invalidPlan = false;
end
end
For troubleshooting purposes I added print statements:
while invalidPlan do
print "Enter the plan type (C-Commercial, R-Residential, S-Student): ";
myPlan = gets().upcase;
puts myPlan; # What is my input value? S
puts planTypes.include? myPlan # What is the boolean return? False
puts planTypes.include? "S" # What happens when hard coded? True
if planTypes.include? myPlan
puts "My plan is found!"; # Do I make it inside the if clause? Nope
invalidPlan = false;
end
end
Since I was getting the correct result with a hard-coded string, I tried "#{myPlan}" and myPlan.to_s. However I still get a false result.
I'm new to Ruby scripting, so I'm guessing I'm missing something obvious, but after reviewing similar question here and here, as well as checking the Ruby Doc, I'm at a loss as to way it's not acting correctly.
The result of gets includes a newline (\n), which you can see if you print myPlan.inspect:
Enter the plan type (C-Commercial, R-Residential, S-Student): C
"C\n"
Add strip to clean out the unwanted whitespace:
myPlan = gets().upcase.strip;
Enter the plan type (C-Commercial, R-Residential, S-Student): C
"C"

Convert.ChangeType() Returns incorrect value

I've got a class that parses a CNC file, but I'm having difficulties with trailing "words" on each line of the file.
My code parses all leading "words" until it reaches the final word. It's most noticeable when parsing "Z" values or other Double type values. I've debugged it enough to notice that it successfully parses the numerical value just as it does with "X" and "Y" values, but it doesn't seem to successfully convert it to double. Is there an issue with a character I'm missing or something?
Here's my code:
If IO.File.Exists("Some GCode File.eia") Then
Dim sr As New IO.StreamReader("Some GCode File.eia")
Dim i As Integer = 0
'Read text file
Do While Not sr.EndOfStream
'Get the words in the line
Dim words() As String = sr.ReadLine.Split(" ")
'iterate through each word
For i = 0 To words.Length - 1 Step 1
'iterate through each "registered" keyword. Handled earlier in program
For Each cmd As String In _registeredCmds.Keys
'if current word resembles keyword then process
If words(i) Like cmd & "*" Then
_commands.Add(i, _registeredCmds(cmd))
'Double check availability of a Type to convert to
If Not IsNothing(_commands(i).DataType) Then
'Verify enum ScopeType exists
If Not IsNothing(_commands(i).Scope) Then
'If ScopeType is modal then just set it to True. I'll fix later
If _commands(i).Scope = ScopeType.Modal Then
_commands(i).DataValue = True
Else
'Catch errors in conversion
Try
'Get the value of the gcode command by removing the "registered" keyword from the string
Dim strTemp As String = words(i).Remove(0, words(i).IndexOf(_commands(i).Key) + _commands(i).Key.Length)
'Save the parsed value into an Object type in another class
_commands(i).DataValue = Convert.ChangeType(strTemp, _commands(i).DataType)
Catch ex As Exception
'Log(vbTab & "Error:" & ex.Message)
End Try
End If
Else
'Log(vbTab & "Command scope is null")
End If
Else
'Log(vbTab & "Command datatype is null")
End If
Continue For
End If
Next
Next
i += 1
Loop
Else
Throw New ApplicationException("FilePath provided does not exist! FilePath Provided:'Some GCode File.eia'")
End If
Here's an example of the GCode:
N2930 X-.2187 Y-1.2378 Z-.0135
N2940 X-.2195 Y-1.2434 Z-.0121
N2950 X-.2187 Y-1.249 Z-.0108
N2960 X-.2164 Y-1.2542 Z-.0096
N2970 X-.2125 Y-1.2585 Z-.0086
N2980 X-.207 Y-1.2613 Z-.0079
N2990 X-.2 Y-1.2624 Z-.0076
N3000 X0.
N3010 X12.
N3020 X24.
N3030 X24.2
N3040 X24.2072 Y-1.2635 Z-.0075
N3050 X24.2127 Y-1.2665 Z-.0071
N3060 X24.2167 Y-1.2709 Z-.0064
N3070 X24.2191 Y-1.2763 Z-.0057
N3080 X24.2199 Y-1.2821 Z-.0048
N3090 X24.2191 Y-1.2879 Z-.004
N3100 X24.2167 Y-1.2933 Z-.0032
N3110 X24.2127 Y-1.2977 Z-.0026
N3120 X24.2072 Y-1.3007 Z-.0021
N3130 X24.2 Y-1.3018 Z-.002
N3140 X24.
N3150 X12.
N3160 X0.
N3170 X-.2
N3180 X-.2074 Y-1.3029 Z-.0019
N3190 X-.2131 Y-1.306 Z-.0018
N3200 X-.2172 Y-1.3106 Z-.0016
N3210 X-.2196 Y-1.3161 Z-.0013
N3220 X-.2204 Y-1.3222 Z-.001
N3230 X-.2196 Y-1.3282 Z-.0007
N3240 X-.2172 Y-1.3338 Z-.0004
N3250 X-.2131 Y-1.3384 Z-.0002
N3260 X-.2074 Y-1.3415 Z-.0001
N3270 X-.2 Y-1.3426 Z0.
N3280 X0.
N3290 X12.
N3300 X24.
N3310 X24.2
N3320 G0 Z.1
N3330 Z1.0
N3340 G91 G28 Z0.0
N3350 G90
With regard to the sample CNC code above, you'll notice that X and Y commands with a trailing Z command parse correctly.
EDIT
Per comment, here is a breakdown of _commands()
_commands = SortedList(Of Integer, Command)
Command is a class with the following properties:
Scope as Enum ScopeType
Name as String
Key as String
DataType as Type
DataValue as Object
EDIT: Solution!
Figured out what was wrong. The arrays that make up the construction of the classes were essentially being passed a reference to the "registered" array of objects from the Command class. Therefore every time I parsed the value out of the "word" each line, I was overwriting the DataValue in the Command object.
The solution was to declare a new 'Command' object with every parse and append it to the proper array.
Here's my short hand:
...
For I = 0 To words.Length - 1 Step 1
'iterate through each "registered" keyword. Handled earlier in program
For Each cmd as String in _registeredCmds.Keys
'if current word resembles keyword then process
If words(I) Like cmd & "*" Then
'NEW!!! Declare unassigned Command object
Dim com As Command
' ****** New elongated logic double checking existence of values.....
If _registeredCmds.Keys.Scope = ScopeType.Modal Then
'assign Command object to previously declared variable com
com = New Command()'There's technically passing arguments now to ensure items are transferred
Else
'Parse and pass DataValue from this word
com = New Command()'There's technically passing arguments now to ensure items are transferred
End If
'New sub to add Command object to local array
Add(com)
Continue For
End If
Next
Next
...

VB Script writing to MultiString reg key

I'm writing a script that looks at the current home page of IE. if it is something other than our intranet I grab that value and merge it in to the secondary pages reg key.
Now I have figured out how merge it in to an array(assuming that there are some secondary pages... if there are no big deal). What I am running in to is that there seems to be an extra line when I finally merge it. It's driving me nuts. Any thoughts? Here is the function. There is more tot he script but this is the part that is painful. Thanks
Function AppendSecondary(StrComputer)
objReg.GetstringValue HKEY_CURRENT_USER, strKeyPath, ValueName, strValueMain
objReg.SetStringValue HKEY_CURRENT_USER, strKeyPath, ValueName, strValueMyMTD
set ws = WScript.CreateObject("Wscript.Shell")
strKeyPath=WS.RegRead(strKeyPathPath & ValueNameSecondary)
if vartype(strKeyPath)= vbArray + vbVariant then
arStrings = strKeyPath
else
arStrings = split(strKeyPath,chr(0))
redim preserve arStrings(ubound(arStrings)-3)
end If
redim preserve arStrings(ubound(arStrings)+1)
arstrings(ubound(arStrings))= strvaluemain
arstrings1 = join(arStrings,VBCRLF)
arstringsnew = Array(arstrings1)
objReg.SetMultiStringValue HKEY_CURRENT_USER, strKeyPath, ValueNameSecondary, arstringsnew
End Function
Check the last element of each array to make sure it's not a null string ("") or a non-printing character like Chr(10) or Chr(13) or vbCR, vbLF or vbCRLF.
Interesting question.
Just out of curiosity, why do you merge an array, then rebuild it as an array later on?
arstrings1 = join(arStrings,VBCRLF) 'merge
arstringsnew = Array(arstrings1) 'reassemble
Regardless, I think your split on "chr(0)" is creating this issue and a simple revision too the join command will suffice.
arstrings1 = trim(join(arstrings,vbcrlf))
of if not the case, a quick loop'd'loop
dim nArray() : Redim nArray(0)
for each str in arstrings
if len(str)>0 then
nArray(ubound(nArray)) = str
redim preserve nArray(ubound(nArray)+1)
end if
next
arrstringsnew = nArray

Resources