VBA: Loop for string - excel

I am currently looking in a string for a particular value and creating a new string from it. Was wondering if there is any efficient code to do it?
Example:
I have string as:
ISSCD = "ISSUE1; ISSUE2; ISSUE3; ISSUE1; ISSUE3; ISSUE10; ISSUE12; ISSUE2; ISSUE18; ISSUE18; ISSUE1;
but want string as:
NEWISSCD = "ISSUE1; ISSUE2; ISSUE3; ISSUE10; ISSUE12; ISSUE18; "
Here is the code I am using:
Sub test()
Dim ISSCD, NEWISSCD as String
NEWISSCD = ""
If InStr(ISSCD, "ISSUE1;") > 0 Then NEWISSCD = NEWISSCD & "ISSUE1; "
If InStr(ISSCD, "ISSUE2;") > 0 Then NEWISSCD = NEWISSCD & "ISSUE2; "
'...
If InStr(ISSCD, "ISSUE50;") > 0 Then NEWISSCD = NEWISSCD & "ISSUE50; "
End Sub

You can use a dictionary for this purpose. By using dictionary, you can also count have many times your ISSUE# occurs in your original list.
Please see this:
Sub test()
Dim ISSCD()
Dim i As Long
Dim dict As Object
Dim key As Variant
Set dict = CreateObject("Scripting.Dictionary")
ISSCD = Array("ISSUE1", "ISSUE2", "ISSUE3", "ISSUE1", "ISSUE3", "ISSUE10", "ISSUE12", "ISSUE2", "ISSUE18", "ISSUE18", "ISSUE1")
For Each Item In ISSCD
If dict.Exists(Item) Then
dict(Item) = dict(Item) + 1
Else
dict.Add Item, 1
End If
Next Item
For Each key In dict.Keys
Debug.Print key, dict(key)
Next key
End Sub

Related

Create a string array where the index “value” is a string

Already looked in the forum but was not able to find anything similar.
I would like to create a string array where the index “value” is a string.
For instance:
MyArray(“abc”)=1
MyArray(“def”)=2
MyArray(“ghi”)=3
Is there a way to do this in VBA or can I achieve the same in a different way?
A Dictionary Introduction
A Simple Example
Sub DictIntroduction()
Dim InitText As Variant, InitNumbers As Variant
InitText = Array("abc", "def", "ghi")
InitNumbers = Array(1, 2, 3)
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
Dim i As Long
For i = 0 To UBound(InitText)
dict.Add InitText(i), InitNumbers(i)
Next
Dim Key As Variant
For Each Key In dict.Keys
Debug.Print Key, dict(Key)
Next
Debug.Print dict("abc")
Debug.Print dict("def")
Debug.Print dict("ghi")
End Sub
To find out more about the dictionary visit the following links:
Excel VBA Dictionary - A Complete Guide (Article)
Excel VBA Dictionary (YouTube Playlist)
Sub main()
Set myArray = CreateObject("Scripting.Dictionary")
myArray("abc") = 1
myArray("def") = 2
myArray("ghi") = 3
Debug.Print myArray("abc")
For Each tempKey In myArray
Debug.Print tempKey, myArray(tempKey)
Next
End Sub

Using Collections in Access VBA

I have this following subroutine In Access VBA:
Sub SampleReadCurve()
Dim rs As Recordset
Dim iRow As Long, iField As Long
Dim strSQL As String
Dim CurveID As Long
Dim MarkRunID As Long
Dim MaxOfMarkAsofDate As Date
Dim userdate As String
CurveID = 15
Dim I As Integer
Dim x As Date
userdate = InputBox("Please Enter the Date (mm/dd/yyyy)")
x = userdate
For I = 0 To 150
MaxOfMarkAsofDate = x - I
strSQL = "SELECT * FROM VolatilityOutput WHERE CurveID=" & CurveID & " AND MaxOfMarkAsofDate=#" & MaxOfMarkAsofDate & "# ORDER BY MaxOfMarkasOfDate, MaturityDate"
Set rs = CurrentDb.OpenRecordset(strSQL, Type:=dbOpenDynaset, Options:=dbSeeChanges)
If rs.RecordCount <> 0 Then
rs.MoveFirst
rs.MoveLast
Dim BucketTermAmt As Long
Dim BucketTermUnit As String
Dim BucketDate As Date
Dim MarkAsOfDate As Date
Dim InterpRate As Double
BucketTermAmt = 3
BucketTermUnit = "m"
BucketDate = DateAdd(BucketTermUnit, BucketTermAmt, MaxOfMarkAsofDate)
InterpRate = CurveInterpolateRecordset(rs, BucketDate)
Debug.Print BucketDate, InterpRate
End If
Next I
End Function
This subroutine put outs a 2x2 list of 76 numbers, consisting of a date and an associated rate. I want to store this list of values as a Collection, so that I can use it as an input in another function. Is it possible to use collections to do this? What would be the appropriate syntax?
I agree with comments that a data dictionary is probably the way to go. The reason is that with a dictionary you can actually loop through the keys if needed. You will need to add a reference to the Microsoft Scripting Runtime. Here is a brief example:
Public Function a()
Dim dRates As New Scripting.Dictionary
Dim key As Variant
dRates.Add #1/1/2016#, 1
dRates.Add #2/1/2016#, 1.5
dRates.Add #3/1/2016#, 2
'You can either access the rate directly with the key:
Debug.Print dRates(#2/1/2016#)
'Or you can loop through the keys/values
For Each key in dRates.Keys
Debug.Print key & " - " & dRates(key)
Next
'Or, you can pass the entire collection to a function
Call b(dRates)
End Function
Public Function b(d As Scripting.Dictionary)
For Each key In d.Keys
Debug.Print key & " - " & d(key)
Next
End Function
This will provide the following output:
1.5
1/1/2016 - 1
2/1/2016 - 1.5
3/1/2016 - 2
1/1/2016 - 1
2/1/2016 - 1.5
3/1/2016 - 2

Detect the renaming or deletion of worksheets

Is there a way to detect when a user
renames, or
deletes a worksheet?
I want to run some code if one of these events happens.
what I have tried
My tool uses a lot of event handlers so one thing I thought of was looping through all the sheetnames during each Worksheet_Change, but I don't think that is the best approach.
This approach goes under the ThisWorkbook module.
Public shArray1 As Variant
Public shArray2 As Variant
Private Sub Workbook_SheetDeactivate(ByVal Sh As Object)
Dim lngCnt As Long
Dim strMsg As String
Dim strSht
Dim vErr
Dim strOut As String
'get all sheet names efficiently in a 1D array
ActiveWorkbook.Names.Add "shtNames", "=RIGHT(GET.WORKBOOK(1),LEN(GET.WORKBOOK(1))-FIND(""]"",GET.WORKBOOK(1)))"
shArray2 = Application.Transpose([INDEX(shtNames,)])
strSht = Application.Transpose(Application.Index(shArray2, , 1))
'exit here if first time code is run
If IsEmpty(shArray1) Then
shArray1 = shArray2
Exit Sub
End If
`check each sheet name still exists as is
For lngCnt = 1 To UBound(shArray1)
vErr = Application.Match(shArray1(lngCnt, 1), strSht, 0)
If IsError(vErr) Then
strOut = strOut & shArray1(lngCnt, 1) & vbNewLine
vErr = Empty
End If
Next
shArray1 = Application.Transpose([INDEX(shtNames,)])
If Len(strOut) > 0 Then MsgBox strOut, vbCritical, "These sheets are gone or renamed"
End Sub

LotusScript getting data from a view

I am new to lotus script, and I'm trying to get data from a view and save it into a string. But each time I do that I get the error that Initialize Object variable not set on line 36. In my domino designer Line 36 is right under ItemNames(6).
I tried to use the code from my friend and I get the same error, while his works without a problem.
Please help I'm desperate to make this work.
Sub Initialize
On Error GoTo ERRSUB
Dim nSession As New NotesSession
Dim nDb As NotesDatabase
Dim nDoc As NotesDocument
Dim view As NotesView
Dim nitem As NotesItem
Dim strRecord As String
Dim DataString As String
Dim nList List As String
Dim ListCount As Integer
Dim FirstLine As String
Dim counter As Integer
counter = 0
Dim ItemNames(6) As String
ItemNames(0) = "Date"
ItemNames(1) = "Name"
ItemNames(2) = "Name of buyer"
ItemNames(3) = "Naziv of project"
ItemNames(4) = "value"
ItemNames(5) = "source"
ItemNames(6) = "status"
Set nDb = nSession.Currentdatabase
Set view = nDb.Getview("X_view_1")
Set ndoc = view.Getfirstdocument()
Do Until (ndoc Is nothing)
ForAll item In ItemNames
Set nitem = ndoc.Getfirstitem(item)
DataString = nitem.Values & ";"
counter = counter + 1
End ForAll
DataString = DataString & Chr(13)
Set ndoc = view.Getnextdocument(ndoc)
Loop
GoTo DONE
DONE:
MessageBox counter
Exit Sub
ERRSUB:
Call logger("Error",nSession.currentagent.name,"Initialize","","")
GoTo done
End Sub
Line 36 is DataString = nitem.Values & ";". The error is that nitem is not set properly. Probably the item is not available in a certain document. Test for nitem isn't Nothing.
Change your ForAll loop to
ForAll item In ItemNames
Set nitem = ndoc.Getfirstitem(item)
If Not nitem Is Nothing then
DataString = DataString & nitem.Text
End If
DataString = DataString & ";"
counter = counter + 1
End ForAll
I would writ it something like this below.
Among the things I notice in your code:
* You use GoTo in your error handler, should be a Resume instead.
* You have "GoTo DONE" when the code would get there anyway, that is not needed.
* You have several variables declared that you don't use.
* You don't use much error checking, Knut's suggestion is a good one.
Here is my suggestion, this is how I would export a view:
Sub Initialize
Dim session As New NotesSession
Dim db As NotesDatabase
Dim view As NotesView
Dim col As NotesViewEntryCollection
Dim entry As NotesViewEntry
Dim DataString As String
Dim cnt List As Long
On Error GoTo errHandler
Set db = session.Currentdatabase
Set view = db.Getview("X_view_1")
Set col = view.AllEntries
'*** Set counters
cnt("total") = col.Count
cnt("processed") = 0
'*** Loop though all view entries, much faster that documents
Set entry = col.GetFirstEntry()
Do Until entry Is Nothing
'*** Update status bar every 100 documents
If cnt("processed") Mod 100 = 0 Then
Print "Processed " & cnt("processed") & " of " & cnt("total") & " documents."
End If
'*** Read view columns and add to string
ForAll cv In entry.ColumnValues
DataString = cv & ";"
End ForAll
'*** Add line break to string
DataString = DataString & Chr(13)
'*** Update counter and get next entry in view collection
cnt("processed") = cnt("processed") + 1
Set entry = col.GetNextEntry(entry)
Loop
exitSub:
MsgBox "Processed " & cnt("processed") & " of " & cnt("total") & " documents.",,"Finished"
Exit Sub
errHandler:
Call logger("Error",session.CurrentAgent.Name,"Initialize","","")
Resume exitSub
End Sub
Another way to do it would be to read the the value directly from the NotesDocument:
DataString = doc.GetItemValue(item)(0) & ";"
Of course, this will only read the first value of any multi-value fields, but you can fix that like this:
DataString = Join(doc.GetItemValue(item),"~") & ";"
This will put a ~ between each value if there are more than one, then you can process that the way you like.

VB6: Splitling with multi-multicharactered delimiters?

I have a problem with the split function I have currently. I am able to either split with 1 delimited only (split()) or split with many single characters (custom()). Is there a way to split this? Keep in mind that these delimiters are not in order.
"MY!!DATA##IS!!LOCATED##HERE!!IN!!BETWEEN##THE##ATS!!AND!!MARKS"
I need your help to get the following result
"MY" , "DATA" , "IS" , "LOCATED" , "HERE" , "IN" , "BETWEEN","THE", "ATS" , "AND", "MARKS"
thanks
Create a new VB6 EXE project and add a button to the form you will be given, and use the following code for the Button1_Click event:
Private Sub Command1_Click()
Dim myText As String
Dim myArray() As String
Dim InBetweenAWord As Boolean
Dim tmpString As String
Dim CurrentCount As Integer
CurrentCount = 0
myText = "MY!!DATA##IS!!LOCATED##HERE!!IN!!BETWEEN##THE##ATS!!AND!!MARKS"
For i = 1 To Len(myText)
If (Mid(myText, i, 1) = "#" Or Mid(myText, i, 1) = "!") And InBetweenAWord = True Then
CurrentCount = CurrentCount + 1
ReDim Preserve myArray(CurrentCount)
myArray(CurrentCount) = tmpString
tmpString = ""
InBetweenAWord = False
Else
If (Mid(myText, i, 1) <> "#" And Mid(myText, i, 1) <> "!") Then
tmpString = tmpString & Mid(myText, i, 1)
InBetweenAWord = True
End If
End If
Next
For i = 1 To CurrentCount
MsgBox myArray(i) 'This will iterate through all of your words
Next
End Sub
Notice that once the first For-Next loop is finished, the [myArray] will contain all of your words without the un-desired characters, so you can use them anywhere you like. I just displayed them as MsgBox to the user to make sure my code worked.
Character handling is really awkward in VB6. I would prefer using built-in functions like this
Private Function MultiSplit(ByVal sText As String, vDelims As Variant) As Variant
Const LNG_PRIVATE As Long = &HE1B6 '-- U+E000 to U+F8FF - Private Use Area (PUA)
Dim vElem As Variant
For Each vElem In vDelims
sText = Replace(sText, vElem, ChrW$(LNG_PRIVATE))
Next
MultiSplit = Split(sText, ChrW$(LNG_PRIVATE))
End Function
Use MultiSplit like this
Private Sub Command1_Click()
Dim vElem As Variant
For Each vElem In MultiSplit("MY!!DATA##IS!!LOCATED##HERE!!IN!!BETWEEN##THE##ATS!!AND!!MARKS", Array("!!", "##"))
Debug.Print vElem
Next
End Sub

Resources