Subscript not in Range MS Access - vba

I'm needing to get fields names from a query and put them into a dynamic array. I believe I have found the proper code this, however I get a "Subscript not in Range" error.
Code so far:
Dim qdf As QueryDef
Dim fld As Field
Dim o As Integer
Dim fieldCount as Integer
fieldCount = CurrentDb.QueryDefs("qryctAverage").Fields.Count
Set qdf = db.QueryDefs("qryctAverage")
Dim n As Integer
n = fieldCount
ReDim colHeaders(0 To n - 1)
For o = 0 To n - 1
colHeader(o) = qdf.Fields(o).Name
Next o
Line of error: colHeader(o) = qdf.Fields(o).Name
I'm fairly new to VBA so I appreciate the patience and time put in to helping out! Thanks in advance
Note: All answer I've found have applied to non-dynamic arrays.
EDIT:
I now get "Sub or Function not defined" after removing Dim colHeader() as String
Error Line: colHeader(o) = qdf.Fields(o).Name

Dim colHeader() As String
ReDim colHeaders(0 To n - 1)
You've declared colHeader, but resized colHeaders. Option Explicit can't pick that up, because the ReDim statement is perfectly valid as a declaration, too.
But then, colHeader isn't dimensioned, so index o is necessarily out of bounds:
colHeader(o) = qdf.Fields(o).Name
Change it to
colHeaders(o) = qfd.Fields(o).Name
I'd remove the Dim colHeader() As String declaration, and add Option Explicit at the top of the module if it's missing.

Another way would be to use a For Each block.
This code builds a single string of field names separated by an |.
Split is then used to turn the field string into an array of field names which is passed back to the calling procedure.
Option Explicit
Sub Test()
Dim colHeaders As Variant
colHeaders = FieldNames("qryctAverage")
Debug.Assert False 'Pause code so you can look at colHeaders.
End Sub
Public Function FieldNames(QueryName As String) As Variant
Dim qdf As DAO.QueryDef
Dim fld As DAO.Field
Dim fldNames As String
Set qdf = CurrentDb.QueryDefs(QueryName)
For Each fld In qdf.Fields
fldNames = fldNames & fld.Name & "|"
Next fld
fldNames = Left(fldNames, Len(fldNames) - 1)
FieldNames = Split(fldNames, "|")
End Function

Related

Dlookup passing incorrect value

I am using DLookup
Dim FormName As String
Dim OldBBusinessterm As Long
Dim field As Long
Dim NewBusinessTerm As String
Dim NewRecord As DAO.Database
Dim rstUpdate As DAO.Recordset
oldbusinessterm = Me!BusinessTermID.OldValue 'equal 5194 in test
NewBusinessTerm = Me!BusinessTermID.Value ' equal 5195 in test
BusinessTerm = DLookup("[businesstermdesc]", "tblbusinessterm", " [businesstermid] =" & oldbusinessterm)
' here is the issue - [businesstermid] = 5195 - it should be setting the value from Oldbusinessterm but it is populating it from New businessterm
Set NewRecord = CurrentDb
Set rstUpdate = NewRecord.OpenRecordset("TblFieldTermLink")
rstUpdate.AddNew
rstUpdate("GTSBusinessTerm").Value = BusinessTerm
rstUpdate("BusinessTermID").Value = Me!BusinessTermID.Value
rstUpdate.Update
My problem is that it is reading the businesstermID from my open form, instead of doing the dlookup using the old value.
Any ideas as to why
When you are adding a new record to table TblFieldTermLink you are using the new BusinessTermID value. If you want the Old value you should modify your code to reflect that:
rstUpdate.AddNew
rstUpdate("GTSBusinessTerm").Value = BusinessTerm 'String Text describing old BusinessTermID
rstUpdate("BusinessTermID").Value = oldbusinessterm 'Long Value of old BusinessTermID
rstUpdate.Update
I also notice that you are not using the variable you declared to hold the old business term id:
Dim OldBBusinessterm As Long
oldbusinessterm = Me!BusinessTermID.OldValue 'equal 5194 in test
Notice the spelling of the two. Which is why capitalization did not follow. Perhaps you want to correct the Dim statement:
Dim OldBusinessTerm As Long

Calling excel's function.ets from Access VBA

I'm trying to call excel's FORECAST.ETS from VBA in my access project but it seems like no matter what I do I get this error:
"VBA Error 1004 Invalid number of arguments."
Here's what I'm doing -
'**********************************************
Public Sub testFCsof()
Dim testrfXL As Object
Dim testrfNowDate As Date
Dim testrfempSQLStr As String
Dim testrfempSQLRS As DAO.Recordset
Dim testrfRecNo As Integer
Dim testrfDateARRAY() As Date
Dim testrfPointsARRAY() As Double
Dim testrfFDFCAST As Double
Dim fdtestempID As Long
On Error GoTo Err_testrfNBA
Set todaysDB = CurrentDb()
fdtestempID = 382
testrfFDFCAST = 1000000
testrfempSQLStr = "SELECT NBAFANempPER.eventTime, NBAFANempPER.FDpoints " & _
"FROM NBAFANempPER WHERE ((NBAFANempPER.empID)= " & fdtestempID & ") ORDER BY NBAFANempPER.eventTime;"
Set testrfempSQLRS = todaysDB.OpenRecordset(testrfempSQLStr, dbOpenDynaset, dbSeeChanges, dbReadOnly)
If Not (testrfempSQLRS.BOF And testrfempSQLRS.EOF) Then 'only do this if we have records
testrfempSQLRS.MoveLast
ReDim testrfDateARRAY(testrfempSQLRS.RecordCount - 1)
ReDim testrfPointsARRAY(testrfempSQLRS.RecordCount - 1)
testrfempSQLRS.MoveFirst
testrfRecNo = 0
Do While Not testrfempSQLRS.EOF
testrfDateARRAY(testrfRecNo) = CDate(dateHeadFunk(CDate(testrfempSQLRS!eventTime)))
testrfPointsARRAY(testrfRecNo) = CDbl(testrfempSQLRS!FDpoints)
testrfRecNo = testrfRecNo + 1
testrfempSQLRS.MoveNext
Loop
Set testrfXL = CreateObject("Excel.Application")
testrfNowDate = Now()
testrfFDFCAST = testrfXL.WorksheetFunction.Forecast.ets(Arg1:=testrfNowDate, Arg2:=testrfPointsARRAY, Arg3:=testrfDateARRAY, Arg4:=0, Arg5:=1, Arg6:=5)
Set testrfXL = Nothing
End If
Exit_testrfNBA:
Erase testrfPointsARRAY
Erase testrfDateARRAY
testrfNowDate = Empty
testrfempSQLStr = ""
If Not testrfempSQLRS Is Nothing Then
testrfempSQLRS.Close
Set testrfempSQLRS = Nothing
End If
Exit Sub
Err_testrfNBA:
MsgBox "Got a sucky forecast number back.."
generic.TestODBCErr
Resume Exit_testrfNBA
End Sub
'**********************************************
The arrays fill up just fine, both the same size.
I can call other Excel functions without a problem.
Can't figure out what the problem could be. I've tried this with and without the "Arg=" tags, with and without the last three optional arguments, tried wrapping the arrays with Array(myArray), even set the Arrays to Variant.
At least in Excel VBA, the function name is Forecast_ETS, not Forecast.ETS.

Excel vba -- Object required error at Sub line

So I am getting an error at the beginning of my code, an error I didn't use to get last time I opened and edited my VBA code. Any ideas? Here is part of it. When I try to step through the code, I get the error: "Object required" and my sub line (first line) is highlighted. Any ideas how I can fix this?
Sub ManagerCashflow()
'---------------------------Declare all the variables---------------------------
'------Define object names------
'Dim i As Integer
'Dim c As Integer
Dim AUM_Cash_Projections_folder_pathname As String
Dim AUM_Cash_Projections_FOLDER_YEARMONTH_pathname As String
Dim AUM_Cash_Projections_filename_DATE As String
Dim AUMCshf_wb As Workbook
Dim MngrCshF_wb As Workbook
'Dim CshF_lr As Integer
'Dim PE_r As Integer
'Dim lstmanager_r As Integer
'------Set/call the objects to a destination------
'Worksheets
'Manager Cashflow
Set MngrCshF_wb = ThisWorkbook
Set MCF_Current_ws = MngrCshF_wb.Sheets("Sheet1")
'AUM Cash Projections
Set AUM_Cash_Projections_folder_pathname = "https://iportal.casey.org/Risk Management/CFP Reporting/AUM Cash Projection"
Set AUM_Cash_Projections_FOLDER_YEARMONTH_pathname = Right(MCF_Current_ws.Cells(2, 1).Value, 7)
Set AUM_Cash_Projections_filenamedate = MCF_Current_ws.Cells(2, 1).Value
Set AUMCshf_wb = Workbooks.Open(AUM_Cash_Projections_folder_pathname + "/" + AUM_Cash_Projections_FOLDER_YEARMONTH_pathname + "/" + AUM_Cash_Projections_filenamedate)
Set CshF_ws = AUMCshf_wb.Sheets("CashFlow + Projections")
'Master Data with all of the current managers
Set CurrAssets_ws = AUMCshf_wb.Sheets("Master Data")
'... a bunch of other code that works....
End Sub
Not sure why it didn't happen before. You don't need to use set to assign a value to a string.
AUM_Cash_Projections_folder_pathname = "https://iportal.casey.org/Risk Management/CFP Reporting/AUM Cash Projection"
AUM_Cash_Projections_FOLDER_YEARMONTH_pathname = Right(MCF_Current_ws.Cells(2, 1).Value, 7)
AUM_Cash_Projections_filenamedate = MCF_Current_ws.Cells(2, 1).Value
You also need to declare MCF_Current_ws and your other worksheets. It won't tell you unless you have "Option Explicit" at the top of your code, but it's good to do.
Dim MCF_Current_ws as Excel.Worksheet

Please suggest reg express for every nth occurrence of a character in a string in vba

At the very outset, let me confess. I am not a developer nor do I have any technical background. However, I have learned a bit of coding. I want to a regular expression for finding every nth position of a character. For example, google, yahoo, rediff, facebook, cnn, pinterest, gmail. I want to find every third occurrence of the comma in the string and replace it with a semicolon. This is for a excel vba macro I am working on. For now, I am trying to loop through it and then replace. If the data is large, the macro fails. Would appreciate your reply. Thanks a ton in advance.
Here is what I am doing:
Option Explicit
Sub reg()
Dim regx As RegExp
Set regx = New RegExp
Dim allMatches As Object
Dim contents As String
Dim contents2 As String
contents = "hello, wow, true, false, why, try, cry, haha"
contents = "contents1" & contents
regx.pattern = ",{4}"
regx.Global = True
regx.IgnoreCase = False
Set allMatches = regx.Execute(contents)
contents2 = regx.Replace(contents, ";")
MsgBox contents2
End Sub
I get the data from all selected cells. Join it. Add semicolon (an indicator for the row end) at every fourth comma found. Please suggest if there is a better way to do it as I am new to this:
Here is what I have done currently by looping through array. I want to avoid this.
Function insertColon(sInputString As String) As Variant
Dim data3 As String
Dim sFind As String
Dim sReplacewith As String
Dim result As String
'Dim i As Integer
Dim Counter As Integer
sFind = ","
sReplacewith = ";"
data3 = sInputString
' MsgBox = data3
' Dim J As Integer
Application.Volatile
FindN = 0
'Dim i As Integer
' i = 1
Counter = 4
' MsgBox Len(data3)
While InStr(Counter, sInputString, sFind)
FindN = InStr(Counter, sInputString, sFind)
data3 = Application.Substitute(data3, sFind, sReplacewith, Counter)
Counter = Counter + 3
' MsgBox "loop" & i
'
' i = i + 1
Wend
If I understood you properly then all your code could be summarized to a few lines
Dim sText As String
sText = "hello, wow, true, false, why, try, cry, haha, huh, muh"
For p = 3 To Len(sText) Step 2
sText = WorksheetFunction.Substitute(sText, ",", ";", p)
Next p
MsgBox sText
'Output
'hello, wow, true; false, why, try; cry, haha, huh; muh
Test it and let me know whether it fails. If not don't forget to accept the answer.

Excel VBA Type Mismatch (13)

I am getting a type mismatch error in VBA and I am not sure why.
The purpose of this macro is to go through a column in an Excel spreadsheet and add all the emails to an array. After each email is added to the first array, it's also supposed to added to a second array but split into two pieces at the # symbol in order to separate name from domain. Like so: person#gmail.com to person and gmail.com.
The problem that I'm getting is that when it gets to the point where it's supposed to split the email, it throws a Type Mismatch error.
Specifically this part:
strDomain = Split(strText, "#")
Here is the complete code:
Sub addContactListEmails()
Dim strEmailList() As String 'Array of emails
Dim blDimensioned As Boolean 'Is the array dimensioned?
Dim strText As String 'To temporarily hold names
Dim lngPosition As Long 'Counting
Dim strDomainList() As String
Dim strDomain As String
Dim dlDimensioned As Boolean
Dim strEmailDomain As String
Dim i As Integer
Dim countRows As Long
'countRows = Columns("E:E").SpecialCells(xlVisible).Rows.Count
countRows = Range("E:E").CurrentRegion.Rows.Count
MsgBox "The number of rows is " & countRows
'The array has not yet been dimensioned:
blDimensioned = False
Dim counter As Long
Do While counter < countRows
counter = counter + 1
' Set the string to the content of the cell
strText = Cells(counter, 5).Value
If strText <> "" Then
'Has the array been dimensioned?
If blDimensioned = True Then
'Yes, so extend the array one element large than its current upper bound.
'Without the "Preserve" keyword below, the previous elements in our array would be erased with the resizing
ReDim Preserve strEmailList(0 To UBound(strEmailList) + 1) As String
Else
'No, so dimension it and flag it as dimensioned.
ReDim strEmailList(0 To 0) As String
blDimensioned = True
End If
'Add the email to the last element in the array.
strEmailList(UBound(strEmailList)) = strText
'Also add the email to the separation array
strDomain = Split(strText, "#")
If strDomain <> "" Then
If dlDimensioned = True Then
ReDim Preserve strDomainList(0 To UBound(strDomainList) + 1) As String
Else
ReDim strDomainList(0 To 0) As String
dlDimensioned = True
End If
strDomainList(UBound(strDomainList)) = strDomain
End If
End If
Loop
'Display email addresses, TESTING ONLY!
For lngPosition = LBound(strEmailList) To UBound(strEmailList)
MsgBox strEmailList(lngPosition)
Next lngPosition
For i = LBound(strDomainList) To UBound(strDomainList)
MsgBox strDomainList(strDomain)
Next
'Erase array
'Erase strEmailList
End Sub
ReDiming arrays is a big hassle. Welcome to the world of collections and Dictionarys. Collection objects are always accessible. Dictionaries require a reference to Microsoft Scripting Runtime (Tools>References>scroll down to find that text and check the box> OK). They dynamically change size for you, you can add, remove items very easily compared to arrays, and Dictionaries especially allow you to organize your data in more logical ways.
In the below code I used a dictionary there the key is the domain (obtained with the split function). Each value for a key is a collection of email addresses with that domain.
Put a break point on End Sub and look at the contents of each of these objects in your locals window. I think you'll see they make more sense and are easier in general.
Option Explicit
Function AllEmails() As Dictionary
Dim emailListCollection As Collection
Set emailListCollection = New Collection 'you're going to like collections way better than arrays
Dim DomainEmailDictionary As Dictionary
Set DomainEmailDictionary = New Dictionary 'key value pairing. key is the domain. value is a collection of emails in that domain
Dim emailParts() As String
Dim countRows As Long
Dim EmailAddress As String
Dim strDomain As String
'countRows = Columns("E:E").SpecialCells(xlVisible).Rows.Count
Dim sht As Worksheet 'always declare your sheets!
Set sht = Sheets("Sheet1")
countRows = sht.Range("E2").End(xlDown).Row
Dim counter As Long
Do While counter < countRows
counter = counter + 1
EmailAddress = Trim(sht.Cells(counter, 5))
If EmailAddress <> "" Then
emailParts = Split(EmailAddress, "#")
If UBound(emailParts) > 0 Then
strDomain = emailParts(1)
End If
If Not DomainEmailDictionary.Exists(strDomain) Then
'if you have not already encountered this domain
DomainEmailDictionary.Add strDomain, New Collection
End If
'Add the email to the dictionary of emails organized by domain
DomainEmailDictionary(strDomain).Add EmailAddress
'Add the email to the collection of only addresses
emailListCollection.Add EmailAddress
End If
Loop
Set AllEmails = DomainEmailDictionary
End Function
and use it with
Sub RemoveUnwantedEmails()
Dim allemailsDic As Dictionary, doNotCallSheet As Worksheet, emailsSheet As Worksheet
Set doNotCallSheet = Sheets("DoNotCallList")
Set emailsSheet = Sheets("Sheet1")
Set allemailsDic = AllEmails
Dim domain As Variant, EmailAddress As Variant
Dim foundDoNotCallDomains As Range, emailAddressesToRemove As Range
For Each domain In allemailsDic.Keys
Set foundDoNotCallDomains = doNotCallSheet.Range("A:A").Find(domain)
If Not foundDoNotCallDomains Is Nothing Then
Debug.Print "domain found"
'do your removal
For Each EmailAddress In allemailsDic(domain)
Set emailAddressesToRemove = emailsSheet.Range("E:E").Find(EmailAddress)
If Not emailAddressesToRemove Is Nothing Then
emailAddressesToRemove = ""
End If
Next EmailAddress
End If
Next domain
End Sub
strDomain must store array of the split text, therefore,
Dim strDomain As Variant
Afterwards, strDomain should be referenced by index, if operations with certain fragments will be made:
If strDomain(i) <> "" Then
The split function returns an array of strings based on the provided separator.
In your if you are sure that the original string is an email, with just one "#" in it then you can safely use the below code:
strDomain = Split(strText, "#")(1)
This will get you the part after "#" which is what you are looking for.
Split returns an array:
Dim mailComp() As String
[...]
mailComp = Split(strText, "#")
strDomain = mailComp(1)
Try strDomain = Split(strText,"#")(1) to get the right hand side of the split where (0) would be the left. And of course works with more than 2 splits as well. You could dim you string variable as an array strDomain() and then Split(strText,"#") will place all the seperated text into the array.