I am developing a routine to calculate the proper futures contract front month
If i have a array of integers denoting month numbers ie, "1,4,7,10,12"
and I have a variable integer that is 2.
How do i test the variable against the array and change the variable to the next highest available in the array if the variable itself wasn't in the array? ie in this case the variable's value of 2 would become 4.
I've tried various ways but am stuck now
If datenum >= (targetdayofmonth + adjdays) Then
currentmonth = currentmonth + 1
Dim currmonthname As String = MonthName(currentmonth, True)
For x As Integer = 0 To contractmonths.Count - 1
If GetMonthNumberfromShortMonthName(contractmonths(x)) = currentmonth Then
currmonthname = currmonthname
Else
End If
Next
Else
Dim currmonthname As String = MonthName(currentmonth, True)
End If
So based on Tim's comments I've updated the code to;
Dim contractmonthNos As New List(Of Int32)
For Each childnode As XmlNode In From childnode1 As XmlNode In root Where childnode1.SelectSingleNode("futures/symbol/Code").InnerText = commodcode
'get the available contract months for this contract
Dim contractmonthnodes As XmlNode = childnode.SelectSingleNode("ContractMonths")
contractmonthNos.AddRange(From subnode As XmlNode In contractmonthnodes Select GetMonthNumberfromShortMonthName(subnode.Name))
Next
If datenum >= (targetdayofmonth + adjdays) Then
currentmonth = currentmonth + 1
Dim currmonthname As String = MonthName(currentmonth, True)
Else
Dim nextmonth = From month As Integer In contractmonthNos Where month > currentmonth
If nextmonth.Any() Then
currentmonth = nextmonth.First()
End If
Dim currmonthname As String = MonthName(currentmonth, True)
End If
but I am getting a VS2012 squiggly under nextmonth in the If Then Else warning of "Possible multiple enumeration of IEnumerable"
I think this is what you want:
Dim intVar = 2
Dim months = { 1,4,7,10,12 }
Dim higherMonths = months.Where(Function(month) month > intVar).ToArray()
If higherMonths.Any() Then
intVar = higherMonths.First()
End If
If you don't want the next available month in the array but the nearest you have to sort before:
Dim higherMonths = months.Where(Function(m) m> intVar).
OrderBy(Function(m) m).
ToArray()
If higherMonths.Any() Then
intVar = higherMonths.First()
End If
Something like
Module Module1
Sub Main()
' N.B. this needs to the array to be sorted.
Dim a() As Integer = {1, 4, 7, 10, 12}
Dim toFind As Integer = 2
Dim foundAt As Integer = -1
For i = 0 To a.Length() - 1
If a(i) >= toFind Then
foundAt = i
Exit For
End If
Next
If foundAt >= 0 Then
Console.WriteLine(String.Format("Looked for {0}, found {1}.", toFind, a(foundAt)))
Else
Console.WriteLine(String.Format("Did not find {0} or higher.", toFind))
End If
Console.ReadLine()
End Sub
End Module
Or you might want to look at using the Array.BinarySearch Method.
Related
I currently have a routine that searches up through the directory where a file is saved and finds a folder called "$Fabrication Data".
I am working on a new addition that will be subbed into my existing code to allow for some human error, aka slight misspelling/misformating if that folder name.
I would like to examine each folder in the 'Path' directory (but not its sub folders). Currently it returns a match:
Path\SubFolder$Fabrication Data$
instead if the path I want:
Path$ Fabrication Data
Bonus question... I am currently returning any folder that is above a .8 match, how can I return the closest match if there are multiple folders above .8 match?
Dim Path As String = "N:\Stuff\More Stuff\More More Stuff\Project Folder"
For Each d In System.IO.Directory.GetDirectories(Path)
For Each sDir In System.IO.Directory.GetDirectories(d)
Dim sdirInfo As New System.IO.DirectoryInfo(sDir)
Dim similarity As Single = GetSimilarity(sdirInfo.Name, "$Fabrication Data")
If similarity > .8 Then
sFDPath = Path & "\" & sdirInfo.Name
MsgBox(sFDPath)
Else
End If
Next
Next
End Sub
Public Function GetSimilarity(string1 As String, string2 As String) As Single
Dim dis As Single = ComputeDistance(string1, string2)
Dim maxLen As Single = string1.Length
If maxLen < string2.Length Then
maxLen = string2.Length
End If
If maxLen = 0.0F Then
Return 1.0F
Else
Return 1.0F - dis / maxLen
End If
End Function
Private Function ComputeDistance(s As String, t As String) As Integer
Dim n As Integer = s.Length
Dim m As Integer = t.Length
Dim distance As Integer(,) = New Integer(n, m) {}
' matrix
Dim cost As Integer = 0
If n = 0 Then
Return m
End If
If m = 0 Then
Return n
End If
'init1
Dim i As Integer = 0
While i <= n
distance(i, 0) = System.Math.Min(System.Threading.Interlocked.Increment(i), i - 1)
End While
Dim j As Integer = 0
While j <= m
distance(0, j) = System.Math.Min(System.Threading.Interlocked.Increment(j), j - 1)
End While
'find min distance
For i = 1 To n
For j = 1 To m
cost = (If(t.Substring(j - 1, 1) = s.Substring(i - 1, 1), 0, 1))
distance(i, j) = Math.Min(distance(i - 1, j) + 1, Math.Min(distance(i, j - 1) + 1, distance(i - 1, j - 1) + cost))
Next
Next
Return distance(n, m)
End Function ```
You can track the ratings for each folder using a simple class like this:
Public Class FolderRating
Public Rating As Single
Public Folder As String
Public Sub New(folder As String, rating As Single)
Me.Folder = folder
Me.Rating = rating
End Sub
End Class
Then, create a List:
Dim ratings As New List(Of FolderRating)
In your loop, when you find a rating that is above 0.8, add it to the list:
If similarity > 0.8 Then
Dim sFDPath As String = Path & "\" & sdirInfo.Name
ratings.Add(New FolderRating(sFDPath, similarity))
End If
Finally, sort the list:
ratings.Sort(Function(x, y) x.Rating.CompareTo(y.Rating))
You can then take the last value in the list and it will be your most similar folder, if any:
Dim bestMatch As FolderRating = ratings.LastOrDefault
Main point of this function is to return the most common movie genre.
Function MoviesByGenre(genreRng As Range) As String
Dim genreList(1 To 4) As String
Dim current As Integer
current = 1
For i = 1 To genreRng.count
Dim found As Integer
found = 0
For j = 1 To current
If genreList(j) = genreRng.Cells(i) Then
found = 1
Exit For
End If
Next j
If found = 0 Then
genreList(current) = genreRng.Cells(i)
current = current + 1
End If
Next i
Dim genreCount(1 To 4) As Integer
For i = 1 To 4
Dim count As Integer
count = 0
For j = 1 To genreRng.count
If genreRng.Cells(j) = genreList(i) Then
count = count + 1
End If
Next j
genreCount(i) = count
Next i
MoviesByGenre = FindMax(genreCount, genreList)
End Function
Now my FindMax function looks like this:
Function FindMax(valueArray, nameArray) As String
Dim max As Double
max = valueArray(LBound(valueArray))
For i = LBound(valueArray) + 1 To UBound(valueArray)
If valueArray(i) > valueArray(max) Then
max = i
End If
Next i
FindMax = nameArray(max)
End Function
FindMax appears to work well in other areas, but depending on the range I use for MoviesByGenre, it may or may not work. (sometimes it'll give me #VALUE!, other times it'll give me the actual most common movie genre, and i'm not sure why.) I'm using Excel 2016 for MacOS.
Do you mean something like that
Sub Test()
Dim a As Variant
a = Range("A1:A7").Value
MsgBox FindMax(a)
End Sub
Function FindMax(valueArray) As String
Dim max As Double
Dim i As Long
max = valueArray(LBound(valueArray), 1)
For i = LBound(valueArray) + 1 To UBound(valueArray)
If valueArray(i, 1) > max Then
max = valueArray(i, 1)
End If
Next i
FindMax = max
End Function
I would like to format my listbox so that the output becomes something like this.
This is method, not in the main form tho:
Public Function GetSeatInfoStrings(ByVal choice As DisplayOptions,
ByRef strSeatInfoStrings As String()) As Integer
Dim count As Integer = GetNumOfSeats(choice)
If (count <= 0) Then
Return 0
End If
strSeatInfoStrings = New String(count - 1) {}
Dim StrReservation As String = ""
strSeatInfoStrings = New String(count - 1) {}
Dim i As Integer = 0 'counter for return array
'is the element corresponding with the index empty
For index As Integer = 0 To m_totNumOfSeats - 1
Dim strName As String = ""
Dim reserved As Boolean = Not String.IsNullOrEmpty(m_nameList(index))
'if the criteria below are not met, skip to add info in the array
If (choice = DisplayOptions.AllSeats) Or
(reserved And choice = DisplayOptions.ReservedSeats) Or
((Not reserved) And (choice = DisplayOptions.VacantSeats)) Then
If (reserved) Then
StrReservation = "Reserved"
strName = m_nameList(index)
Else
StrReservation = "Vacant"
strName = "..........."
End If
strSeatInfoStrings(i) = String.Format("{0,4} {1,-8} {2, -20} {3,10:f2}",
index + 1, StrReservation, strName, m_priceList(index))
i += 1
End If
Next
Return count
End Function
I don't know how to format the listbox as the strSeatInfoStrings(i) in the main form.
My listbox
This is what I've done
Private Sub UpdateGUI()
'Clear the listbox and make it ready for new data.
ReservationList.Items.Clear()
'size of array is determined in the callee method
Dim seatInfoStrings As String() = Nothing
Dim calcOption As DisplayOptions = DirectCast(cmbDisplayOptions.SelectedIndex, DisplayOptions)
Dim count As Integer = m_seatMngr.GetSeatInfoStrings(calcOption, seatInfoStrings)
If count > 0 Then
ReservationList.Items.AddRange(seatInfoStrings)
Else
ReservationList.Items.Add("Nothing to display!")
End If
Found the error! I forgot to call the UpdateGUI() in the IntializeGUI().
So I am trying to run the following sub where I wish to split the string iden at the second occurrence of "_" But what I get instead is an array with the following elements "1-SWFEED-4.6.14", "10", "3_C" but what I want is an array with elements "1-SWFEED-4.6.14_10", "3_C". What am I doing wrong?
Sub check_split()
Dim iden As String
Dim element As Variant
iden = "1-SWFEED-4.6.14_10_3_C"
For Each element In Split(iden, "_", 3)
MsgBox element
Next element
End Sub
I also tried using the limit as UBound(split(iden, "_")) but it doesn't work either.
Came up with this sub which does what I need (Thanks #Maco for the comment)
Sub check_split()
Dim iden As String
Dim element As Variant
Dim indexCounter As Integer
Dim concIden As String
iden = "1-SWFEED-4.6.14_10_3_C"
indexCounter = 0
For Each element In Split(iden, "_")
If indexCounter < UBound(Split(iden, "_")) - 1 Then
If Not indexCounter + 1 = UBound(Split(iden, "_")) - 1 Then
concIden = concIden + element + "_"
Else
concIden = concIden + element
End If
End If
indexCounter = indexCounter + 1
Next element
MsgBox concIden
End Sub
How about this?
Sub check_split()
Dim iden As String, splitLocation As Integer, firstPart As String, secondPart As String
iden = "1-SWFEED-4.6.14_10_3_C"
splitLocation = WorksheetFunction.Find("_", iden, WorksheetFunction.Find("_", iden, 1) + 1)
firstPart = VBA.Left$(iden, splitLocation - 1) //prints 1-SWFEED-4.6.14_10
secondPart = VBA.Right$(iden, Len(iden) - splitLocation) // prints 3_C
End Sub
I have string "ololo123".
I need get position of first digit - 1.
How to set mask of search ?
Here is a lightweight and fast method that avoids regex/reference additions, thus helping with overhead and transportability should that be an advantage.
Public Function GetNumLoc(xValue As String) As Integer
For GetNumLoc = 1 To Len(xValue)
If Mid(xValue, GetNumLoc, 1) Like "#" Then Exit Function
Next
GetNumLoc = 0
End Function
Something like this should do the trick for you:
Public Function GetPositionOfFirstNumericCharacter(ByVal s As String) As Integer
For i = 1 To Len(s)
Dim currentCharacter As String
currentCharacter = Mid(s, i, 1)
If IsNumeric(currentCharacter) = True Then
GetPositionOfFirstNumericCharacter = i
Exit Function
End If
Next i
End Function
You can then call it like this:
Dim iPosition as Integer
iPosition = GetPositionOfFirstNumericCharacter("ololo123")
Not sure on your environment, but this worked in Excel 2010
'Added reference for Microsoft VBScript Regular Expressions 5.5
Const myString As String = "ololo123"
Dim regex As New RegExp
Dim regmatch As MatchCollection
regex.Pattern = "\d"
Set regmatch = regex.Execute(myString)
MsgBox (regmatch.Item(0).FirstIndex) ' Outputs 5
I actually have that function:
Public Function GetNumericPosition(ByVal s As String) As Integer
Dim result As Integer
Dim i As Integer
Dim ii As Integer
result = -1
ii = Len(s)
For i = 1 To ii
If IsNumeric(Mid$(s, i, 1)) Then
result = i
Exit For
End If
Next
GetNumericPosition = result
End Function
You could try regex, and then you'd have two problems. My VBAfu is not up to snuff, but I'll give it a go:
Function FirstDigit(strData As String) As Integer
Dim RE As Object REMatches As Object
Set RE = CreateObject("vbscript.regexp")
With RE
.Pattern = "[0-9]"
End With
Set REMatches = RE.Execute(strData)
FirstDigit = REMatches(0).FirstIndex
End Function
Then you just call it with FirstDigit("ololo123").
If speed is an issue, this will run a bit faster than Robs (noi Rob):
Public Sub Example()
Const myString As String = "ololo123"
Dim position As Long
position = GetFirstNumeric(myString)
If position > 0 Then
MsgBox "Found numeric at postion " & position & "."
Else
MsgBox "Numeric not found."
End If
End Sub
Public Function GetFirstNumeric(ByVal value As String) As Long
Dim i As Long
Dim bytValue() As Byte
Dim lngRtnVal As Long
bytValue = value
For i = 0 To UBound(bytValue) Step 2
Select Case bytValue(i)
Case vbKey0 To vbKey9
If bytValue(i + 1) = 0 Then
lngRtnVal = (i \ 2) + 1
Exit For
End If
End Select
Next
GetFirstNumeric = lngRtnVal
End Function
An improved version of spere's answer (can't edit his answer), which works for any pattern
Private Function GetNumLoc(textValue As String, pattern As String) As Integer
For GetNumLoc = 1 To (Len(textValue) - Len(pattern) + 1)
If Mid(textValue, GetNumLoc, Len(pattern)) Like pattern Then Exit Function
Next
GetNumLoc = 0
End Function
To get the pattern value you can use this:
Private Function GetTextByPattern(textValue As String, pattern As String) As String
Dim NumLoc As Integer
For NumLoc = 1 To (Len(textValue) - Len(pattern) + 1)
If Mid(textValue, NumLoc, Len(pattern)) Like pattern Then
GetTextByPattern = Mid(textValue, NumLoc, Len(pattern))
Exit Function
End If
Next
GetTextByPattern = ""
End Function
Example use:
dim bill as String
bill = "BILLNUMBER 2202/1132/1 PT2200136"
Debug.Print GetNumLoc(bill , "PT#######")
'Printed result:
'24
Debug.Print GetTextByPattern(bill , "PT#######")
'Printed result:
'PT2200136