I'm trying to search a string for a set of values and return the value ONLY if the string matches exactly with one of the set.
My original expression was as so:
title = "MrS"
setTitles = {"Miss", "Mr", "Mrs", "Dr", "Ms"}
title = (title:gsub("%w",string.lower)):gsub("^l", string.upper)
if string.match(title, setTitles) ~= nil then title = title else title = "XX" end
I then realised I needed some way of cycling through the values so got to here:
title = "MrS"
setTitles = {"Miss", "Mr", "Mrs", "Dr", "Ms"}
title = (title:gsub("%w",string.lower)):gsub("^%l", string.upper)
for i = 1, 5 do
if string.match(title, setTitles[i]) ~= nil
then title = title
else title = "XX"
end
end
Except that just returns "XX" every time.
I know it's probably quite simple and obvious, but I can't seem to find a solution and would really appreciate a hand!
Here is why your code is not working. Your first iteration of the loop uses Mrs and checks if it matches Miss, and it does not so it changes title to XX and thus no following checks can ever match.
You cant change title until you have checked all your possible values first.
By adjusting your code to use a matchFound variable to determine if the change is needed you can fix this problem:
local matchFound = false
for i = 1, 5 do
if string.match(title, setTitles[i]) ~= nil then
matchFound = true
break
end
end
if matchFound == false then
title = "XX"
end
print(title)
Additionally your code can give false matches for Mr rather then Mrs this is because Mr will match inside Mrs or any string that starts with Mr. To change this you can adjust your call of string.match to:
string.match(title, "^".. setTitles[i] .. "$")
This forces string.match to insure that the first and last chars of the pattern are also the first and last chars of the string passed to it.
As a suggestion, Rather then using string.match, make your setTitles a proper set like:
local setTitles = {["Miss"] = true, ["Mr"] = true, ["Mrs"] = true, ["Dr"] = true, ["Ms"] = true}
Then your check becomes:
title = setTitles[title] and title or "XX"
Lua resource on Sets:
https://www.lua.org/pil/11.5.html
http://lua-users.org/wiki/SetOperations
You shouldn't change the title variable in the for loop.
You can try this code:
--title = "MrS"
title = "MrX"
setTitles = {"Miss", "Mr", "Mrs", "Dr", "Ms"}
title = title:gsub("%w", string.lower) -- mrs
title = title:gsub("^%l", string.upper) -- Mrs
ismatch = false
for i = 1, 5 do
print(title, setTitles[i])
if tostring(title) == tostring(setTitles[i]) then
ismatch = true
print("matched")
return
end
end
if ismatch then title = title else title = "XX" end
print(title)
Hope this helps.
Related
i have this subroutine that i use to make labels. for some reason i cant just make another subroutine where instead of "CGI_SAMPLE_LABEL" it uses "YCI_SAMPLE_LABEL" because of other subroutines. any suggestions on how i can add this so that it chooses either one or the other. if tried using WHERE OR but that didn't work. i edited some of the tags because LIMS basic language is also like smalltalk.
LabelType = "CGI_SAMPLE_LABEL"
pTableNameStr = "SAMPLE"
pLabelNameStr = "CGI_SAMPLE_LABEL"
'Breakpoint(aReason)
NumSamples = Ubound(selectedObjects, 1)
FOR X = 1 TO NumSamples
SampleNumber = selectedObjects[x]
pKeyNameArr[1] = SampleNumber
pNumLabelsInt = 1
pLabelNameStr = "CGI_SAMPLE_LABEL"
pReasonStr = "Auto Label Generation"
pActivityStr = "Label printed for sample logged event"
GOSUB FN_LABEL_PRINT_ALL
NEXT 'Sample
RETURN
I have through an API fetched my data as an XML, and I wish to cycle through nodes (there are several of the same type) and add them to certain fields/a table.
Example from the XML-file:
<HistRating
xmlns="">
<EndrAr>2020</EndrAr>
<EndrMnd>7</EndrMnd>
<Rating>A</Rating>
</HistRating>
<HistRating
xmlns="">
<EndrAr>2019</EndrAr>
<EndrMnd>6</EndrMnd>
<Rating>A</Rating>
</HistRating>
I have tried the following format (at this point the XML I need is in a string in xmlDoc xmlDoc = CreateObject("MSXML2.DOMDocument.6.0"). Fully aware that this is not a really "sexy" way to write it, but I'm new at this game:
Set nodeXML = xmlDoc.getElementsByTagName("EndrAr")
Range("G1").Value = nodeXML(1).Text
Range("H1").Value = nodeXML(2).Text
Range("I1").Value = nodeXML(3).Text
Set nodeXML = xmlDoc.getElementsByTagName("EndrMnd")
Range("G2").Value = nodeXML(1).Text
Range("H2").Value = nodeXML(2).Text
Range("I2").Value = nodeXML(3).Text
Set nodeXML = xmlDoc.getElementsByTagName("Rating")
Range("G3").Value = nodeXML(1).Text
Range("H3").Value = nodeXML(2).Text
Range("I3").Value = nodeXML(3).Text
This works great as long as all three items are there. Unfortunately that is not given. If it is a new company i.e. (3) wont exist (there is one line per year above), and I would like to either set the cell to Blank or No value or something.
The result from when I run the above code:
But if I try to add a line 4 to test what happens if value does not exists I get the following (for obvious reasons)
What I would love some help with is:
Can I by some "magic" add a ifmissing (tried it, but could not get it to work)?
Other ways to add a if variable is not found, input following into cell
Or are there a complete different way I should have solved this?
This is to add accounting data from last X available years (where X is ie 4, or less if not 4 is available) from 30 nodes.
You could use an Error trapping Function. Note in the code below we choose not to use the returned boolean.
Dim myTest as String
.
.
TryReadingXmlNode nodeXML,1, myText
Range("G1").Value = myText
.
.
Public Function TryReadingXmlNode(ByVal ipNode as object, ByVal ipIndex as Long, ByRef opText as string) as boolean
On Error Resume Next
opText=ipNode.Item(ipIndex).Text
TryReadingXmlNode=Len(opText)>0
If err.number>0 then opText="NoValue"
on Error Goto 0
End Function
Start by querying all of the HistRating elements, then loop over that collection:
Const MAX_YEARS As Long = 4
Dim ratings, rating, c As Range, i as Long
Set c= Range("A1")
Set ratings = xmlDoc.getElementsByTagName("HistRating")
For Each rating in ratings
c.offset(0, i) = rating.getElementsByTagName("EndrAr")(0).Text
c.offset(1, i) = rating.getElementsByTagName("EndrMnd")(0).Text
c.offset(2, i) = rating.getElementsByTagName("Rating")(0).Text
i = i + 1
If i >= MAX_YEARS Then Exit For 'exit if processed enough nodes
Next rating
I have a simple 2 column array and all I want to do is use Vlookup to find the matching value in Column 1 and return the value out of column 2. I keep getting #N/A which implies that my "answer" isn't there. I thought I had done everything correctly, but I can't seem to find the answer despite reading MANY #N/A questions. This is my code:-
Dim y as Variant
Dim Misc_Pay(1 to 16,1 to 2 ) as Variant.
' Populate the Misc_Pay array
Misc_Pay(1,2) = "Cancer,3"
Misc_Pay(2,2) = "Clerical,Last of Month"
Misc_Pay(3,2) = "Halifax,14"
Misc_Pay(4,2) = "Reward,1"
'and so on down to
Misc_Pay(16,2) = "Last Line,End of File"
'My Vlook up code is:-
y = (Application.Vlookup("Reward",Misc_Pay,2,False)
I've then been printing "y" out so I can see what value it is picking up, except it doesn't appear to be picking anything up, but e.g. "Reward" is patently there.
If I can get this line to work then the full line code will be:-
If(iserror(Application.Vlookup("Reward,Misc_Bay,2,False) then 'do something' Else 'do something else'
To start, the code you posted won't compile, due to the period at the end on line 2, and the extra ( on the last line.
If you're trying to do a lookup in a 2D array, you need to set values for both the key and lookup columns (in this case, columns 1 and 2 respectively). Currently, you're just assigning the entire string with the comma to the second column, so there's nothing to look up in the first column.
I think what you want is this:
Dim y As Variant
Dim Misc_Pay(1 To 16, 1 To 2) As Variant
Misc_Pay(1, 1) = "Cancer"
Misc_Pay(1, 2) = "3"
Misc_Pay(2, 1) = "Clerical"
Misc_Pay(2, 2) = "Last of Month"
Misc_Pay(3, 1) = "Halifax"
Misc_Pay(3, 2) = "14"
Misc_Pay(4, 1) = "Reward"
Misc_Pay(4, 2) = "1"
Misc_Pay(16, 1) = "Last Line"
Misc_Pay(16, 2) = "End of File"
y = Application.VLookup("Reward", Misc_Pay, 2, False)
I'm using Matlab R2014b (that's why I cannot use strings, but only char vectors). Working inside a class, I have to take data from a table variable, format it following my needs, and then insert it into a GUI table (an instance of uitable, to be exact):
function UpdateTable(this)
siz = size(mydata);
tab = cell(siz);
tab(:,1) = num2cell(this.Data.ID);
tab(:,2) = cellstr(datestr(this.Data.Date,'dd/mm/yyyy'));
tab(:,3) = arrayfun(#(x){MyClass.TypeDef1{x,1}},this.Data.Type1);
tab(:,4) = arrayfun(#(x){MyClass.TypeDef2{x,1}},this.Data.Type2);
tab(:,5) = arrayfun(#(x){MyClass.FormatNumber(x)},this.Data.Value);
this.UITable.Data = tab;
end
Where:
properties (Access = private, Constant)
TypeDef1 = {
'A1' 'Name A1';
'B1' 'Name B1';
'C1' 'Name C1';
'D1' 'Name D1';
...
}
TypeDef2 = {
'A2' 'Name A2';
'B2' 'Name B2';
'C2' 'Name C2';
'D2' 'Name D2';
...
}
end
methods (Access = private, Static)
function str = FormatNumber(num)
persistent df;
if (isempty(df))
dfs = java.text.DecimalFormatSymbols();
dfs.setDecimalSeparator(',');
dfs.setGroupingSeparator('.');
df = java.text.DecimalFormat();
df.setDecimalFormatSymbols(dfs);
df.setMaximumFractionDigits(2);
df.setMinimumFractionDigits(2);
end
str = char(df.format(num));
end
end
Everything is working fine. Now I would like to right justify the strings to be inserted in columns 1 and 5, to improve the table readability. I found the Matlab function that suits my needs, strjust. Reading the documentation, I saw that it can be used with cell arrays of char vectors, so I modified part of my UpdateTable code as follows:
tab(:,1) = cellstr(num2str(this.Data.ID));
tab(:,5) = strjust(arrayfun(#(x){MyClass.FormatNumber(x)},this.Data.Value));
TThe second one produces no changes (strings are still not justified). Should the strings already contain enough whitespace to be all the same length?
Ok, I solved the problem by myself using the following code:
function UpdateTable(this)
siz = size(this.Data);
los = arrayfun(#(x){MyClass.FormatNumber(x)},this.Data.Value);
los_lens = cellfun(#(x)numel(x),los);
pad = cellfun(#blanks,num2cell(max(los_lens) - los_lens),'UniformOutput',false);
tab = cell(siz);
tab(:,1) = cellstr(num2str(this.Data.ID));
tab(:,2) = cellstr(datestr(this.Data.Date,'dd/mm/yyyy'));
tab(:,3) = arrayfun(#(x){MyClass.TypeDef1{x,1}},this.Data.Type1);
tab(:,4) = arrayfun(#(x){MyClass.TypeDef2{x,1}},this.Data.Type2);
tab(:,5) = cellstr(strcat(pad,los));
this.UITable.Data = tab;
end
It's probably not the most elegant solution, but it works. Starting from Matlab 2016, the padding can be performed using the built-in pad function.
OK Starting all over again and hopefully a bit more straight forward.
Var = 24
string = "var"
x = string
This results in
x = "var" NOT x = 24
Is it possible to retrieve the value of var, by using the string stored in string
Thank you
Aaron
I don't believe you are able to do that. As a solution you can use a collection to mimic this behaviour:
Sub TestCollection()
Dim Values As New Collection
Values.Add "24", "Var"
Values.Add "Alexander", "Name"
MsgBox Values("Var") & " " & Values("Name")
End Sub