I apologize if this is a dumb question but I've been unable to find an answer to my current issue. I am attempting to create a userform which searches spreadsheet data based on input in the combobox for username, a start date, and an end date.
There is one user column and all other columns correspond to dates. The data is a daily workload productivity number (e.g., 87 widgets). The goal is to be able to enter inputs and copy the data from the raw data spreadsheet to a results spreadsheet, showing information for one user during that date range.
I keep getting an error ("Compile error: Sub or Function not defined") regarding my attempt to use the function to define columns for the appropriate dates. I'd appreciate any input. Thanks.
'Function changes column number to column name
Public Function fnColumnToLetter_CellAdressReplace(ByVal ColumnNumber As Integer)
fnColumnToLetter_CellAdressReplace = Replace(Replace(Cells(1, ColumnNumber).Address, "1", ""), "$", "")
End Function
Private Sub SearchButton1_Click()
'Dim variables
Dim sheet As Worksheet
Dim name1 As String
Dim date1 As Date
Dim date2 As Date
Dim STARTcol As String
Dim ENDcol As String
Dim pharmcol1 As String
Dim pharmcol2 As String
Set sheet = ActiveWorkbook.Sheets("Data")
Set Results = ActiveWorkbook.Sheets("Results")
'Variables for entries input into userform
name1 = userform1.NameBox1.Value
date1 = userform1.DateBox1.Value
date2 = userform1.DateBox2.Value
'Define row based on pharmacist name
rowno = Application.WorksheetFunction.Match(name1, Range("A:A"), 0)
pharmrow = "a" + rowno
'Find first column from start date
STARTcol = Application.WorksheetFunction.Match(date1, Range("A1:AZZ1"), 0)
'Find last column from end date
ENDcol = Application.WorksheetFunction.Match(date2, Range("A1:AZZ1"), 0)
'Call function to replace column number to column name
pharmcol1 = fnColumnToLetter_CellAddressReplace(STARTcol) + rowno
pharmcol2 = fnColumnToLetter_CellAddressReplace(ENDcol) + rowno
'Copy table array to RESULTS worksheet
sheet.Range("pharmcol1:pharmcol2").Copy Destination = Results.Range("A1")
End Sub
Compile error: Sub or Function not defined
Whenever I see this error I usually look for grammatical errors between the function/sub and the call. In your case you have spelled Address differently between the two.
Public Function fnColumnToLetter_CellAdressReplace(ByVal ColumnNumber As Integer)
pharmcol1 = fnColumnToLetter_CellAddressReplace(STARTcol) + rowno
pharmcol2 = fnColumnToLetter_CellAddressReplace(ENDcol) + rowno
Hope this helps.
Related
I have a word table. I wrote a macro to get values from the table. When it runs I get a runtime error 13. When I debug and watch the value of parsing string it looks like this "2019-04-03 There is only one quote in the string. I think that is the case I couldn't convert that string into a date format. Can you help me to fix this?
The code
Sub Macro2()
Dim NumRows As Integer
Dim startDate As String
Dim days As String
Dim endDate As String
If Not Selection.Information(wdWithInTable) Then
Exit Sub
End If
NumRows = Selection.Tables(1).Rows.Count
'Loop to select each row in the current table
For J = 2 To NumRows
'Loop to select each cell in the current row
startDate = Selection.Tables(1).Rows(J).Cells(5).Range.Text
days = Selection.Tables(1).Rows(J).Cells(6).Range.Text
FormatDate = CDate(ends)
endDate = DateAdd("d", days, FormatDate)
Selection.Tables(1).Rows(J).Cells(7).Range.Text = endDate
Next J
End Sub
The table
Here's the minimal change I found that works for me when tested in Word 2013.
General points:
I added Option Explicit so that the computer would help me find errors. In this case, the variables J and FormatDate were used but not Dimed, and ends was used but never initialized (I changed it to startDate).
The Range.Text in a table cell includes whitespace and the end-of-table marker (ยค). That is why CDate was giving an error.
For the dates, I used Left() to take only the left ten characters, since you seem to always be using yyyy-mm-dd-format dates.
For the counts of days, since those can be of any length, I used Range.Words(1).Text to keep only the first Word (as MS Word defines it), which is the number.
I also added the CLng() call in the parameter to DateAdd, since DateAdd wants a number* rather than a string.
For production use, I would also recommend using Selection only in one place, and doing Dim workTable as Table: Set workTable = Selection.Tables(1). That will simplify your code.
Code
<=== marks changed lines
Option Explicit ' <==
Sub Macro2()
Dim NumRows As Integer
Dim startDate As String
Dim days As String
Dim endDate As String
If Not Selection.Information(wdWithInTable) Then
Exit Sub
End If
NumRows = Selection.Tables(1).Rows.Count
'Loop to select each row in the current table
Dim J As Long ' <==
For J = 2 To NumRows
'Loop to select each cell in the current row
startDate = Selection.Tables(1).Rows(J).Cells(5).Range.Text
startDate = Left(startDate, 10) ' <== Remove the space and table mark
days = Selection.Tables(1).Rows(J).Cells(6).Range.Words(1).Text ' <===
Dim FormatDate As Date ' <==
FormatDate = CDate(startDate) ' <== not `ends`
endDate = DateAdd("d", CLng(days), FormatDate) ' <=== clng
Selection.Tables(1).Rows(J).Cells(7).Range.Text = endDate
Next J
End Sub
* DateAdd actually takes a Double, but VBA can promote Long to Double. I chose CLng since it looks like you are only using integer day spans. If not, use CDbl instead.
Try:
Sub Demo()
Dim r As Long
With Selection
If Not .Information(wdWithInTable) Then Exit Sub
With .Tables(1)
For r = 2 To .Rows.Count
.Cell(r, 7).Range.Text = _
Format(DateAdd("d", Split(.Cell(r, 6).Range.Text, vbCr)(0), CDate(Split(.Cell(r, 5).Range.Text, vbCr)(0))), "YYYY-MM-DD")
Next r
End With
End With
End Sub
I am trying to generate random number between the list like start year is 1950, and end year is 1960, the formula I'm trying is
=VLOOKUP(RANDBETWEEN($D$3,$D$4),'SP500 Returns2'!$A$3:$F$103,2,0)
I also tried this formula
=if($D$3 < $D$4, $D$3 + Randombetween(1,10),"")
but it gives me only the value up to the condition true and show me only 2 or 3 values but I need complete value between 1950-1960 but with different sequence every time I open the workbook or when the sheet is changed.
I have also a button of random generate that will populate the column B only from the value column A, I want a formula / function / VBA code that could generate a series between the selective numbers as I discuss above.
Here is overlook of my sheet.
How to add formula in Column A that should generate range between the selection of years? I also tried this formula
=Randombetween($D$3,$D$4) but it return duplicate values.
you could use this sub:
Sub GenerateRandomYearsBetween()
Dim myYear As Long, i As Long
Dim years As New Collection
For myYear = Range("D3") To Range("D4")
years.Add myYear
Next
For myYear = 1 To years.Count
i = Int((years.Count) * Rnd + 1)
Range("A8").Offset(myYear).Value = years(i)
years.Remove i
Next
End Sub
which you may call form the relevant event handler
OR you could use its corresponding Function():
Function GenerateRandomYearsBetween(startYear As Variant, endyear As Variant) As Variant
Application.Volatile
Dim myYear As Long, i As Long
Dim initialYears As New Collection
ReDim finalYears(1 To endyear - startYear + 1) As Long
For myYear = startYear To endyear
initialYears.Add myYear
Next
For myYear = 1 To initialYears.Count
i = Int((initialYears.Count) * Rnd + 1)
finalYears(myYear) = initialYears(i)
initialYears.Remove i
Next
GenerateRandomYearsBetween = finalYears
End Function
which you would place as "array formula" in the proper number of cells (i.e. as many cells as the number of years between the referenced ones) as
=GenerateRandomYearsBetween($D$3;$D$4)
I've written some VBA code with two matches and index formula. I need to pick the unique value from a sheet and compare it with the other sheet. However it is not working. I get some errors. (unable to get the match property of the worksheetfunction class vba - this is the error)
Here is my code :
Sub Post_Attendance()
Sheets("DB").Activate
'On Error Resume Next
Dim myvalue As String
Dim mydate As String
Dim mypost As String
(the date value entered in a cell)
Dim Dt As String
Dt = Range("C7").Value
(the unique id entered in a cell)
Dim empid As String
empid = Range("C8").Value
(activating another worksheet , from a cell value)
Dim strWsName As String
strWsName = Sheets("DB").Range("A7")
Sheets(Left(strWsName, 3)).Select
(match function to find the row and columns number for indexing)
mydate = WorksheetFunction.Match(Dt, Range("B1:Q1"), 0)
myvalue = WorksheetFunction.Match(empid, Range("A5:A500"), 0)
mypost = WorksheetFunction.Index(Range("B2:Q6"), myvalue, mydate)
End Sub
First off, WorksheetFunction.Match never returns a string; it either returns a number (a long integer) or an error. It is not the value from the match, it is the row or column number where the match was found.
Next, you cannot catch an #N/A error from no match with WorksheetFunction.Match but you can catch it with Application.Match into a variant.
Real dates are numbers, not strings. The raw underlying value is another long integer; e.g. a positive whole number with no decimal portion. If you had time or a datetime then you would have a decimal portion.
Resolve and reference your parent worksheet properly; do not rely upon Select or Activate.
The number returned from MATCH is the position within the range of cells searched. You are looking for a row number from row 5 to row 500 then using that to find a row within row 2 to 6; any match above row 9 (match returning 6 or above) in the original is going to be out-of-range.
If the empid values are numbers then deal with numbers; you cannot find a match to a true number from text-that-looks-like-a-number; e.g. 99 <> "99". I'm going to assume that empid should be alphanumeric and not a true number but given the errors with the previous variable assignments, it is up to you to determine the correct assignment.
Here is my best guess at an error controlled sub procedure (given that you have shown no sample data).
Option Explicit
Sub Post_Attendance()
'On Error Resume Next
Dim myvalueRow As Variant, mydateCol As Variant, dt As Long, empid As String, mypost As Variant
dt = Worksheets("DB").Range("C7").Value2
empid = Worksheets("DB").Range("C8").Value2
With Worksheets(Left(Worksheets("DB").Range("A7").Value2, 3))
'locate the column for the date
mydateCol = Application.Match(dt, .Range("B1:Q1"), 0)
If IsError(mydateCol) Then _
mydateCol = Application.Match(CStr(Worksheets("DB").Range("C7").Value2), .Range("B1:Q1"), 0)
If IsError(mydateCol) Then
Debug.Print "dt not found in row 1"
Exit Sub
End If
'locate the row for the value
myvalueRow = Application.Match(empid, .Columns("A"), 0)
If IsError(myvalueRow) Then
Debug.Print "empid not found in column A"
Exit Sub
End If
mypost = Application.Index(.Range("B:Q"), myvalueRow, mydateCol)
End With
End Sub
I have a template with a set number of columns (170) and title headers (row 1 cell name's). This is always the same, until users add columns in between (they're instructed not to change headers). The idea is to make it tamperproof as far as the adding of columns is involved.
I'd like to make variables to hold some of the headers (with the capacity to hold all) and check these with the template to find out the column number (in a loop I reckon). It's probably wisest to make a function to call upon it?
Dim ColHeader1Str as string 'literal row 1, column 1 value (which is always
'the same string and position in the template)
Dim iColHeader1 as integer 'holds the (to be set) value of the column number
Set ColHeader1Str = "ColHeader1"
Now I'd like a loop where it loops trough all the columns (last column = 200) and checks to see what the column number is that matches the ColHeader1Str and store this in the iColHeader1
So something like:
Function find_columnNmbr
Dim i As Integer
For i = 1 To 200 Step 1
If 'ColHeader1Str matches actual column header name
'set found integer as iColHeader1 and so forth
Exit For
End If
Next
End Function`
I know I'm missing a few steps and I'm hoping you guys can help me out.
Update: The template has set column headers. When users interact with it a result could be that columns shift position, or they add more. I have a workbook that needs to load data out of the user's altered template.
I.E. The template has columns 1, 2, 3, 4 and the names are column1, column 2 etc. A user ads a random column so now there are 5. The loop needs to loop through the names of the column headers and identify the column number of the original template columns 1, 2 etc based on a string variable with the original names, which I've hard coded beforehand. These are public constants.
What function LookForHeaders do: input a string, then search for the string in usersheet.range(1:1). If it is found, return the column number of that cell, otherwise it returns 0.
Private Function LookForHeaders(ByVal headerName As String) As Long
Dim rng As Range
Dim userSheet As WorkSheet
Set userSheet = 'userSheet here'
On Error GoTo NotFound
LookForHeaders = userSheet.Range("1:1").Find(headerName).Column
Exit Function
NotFound:
LookForHeaders = 0
End Function
Private Sub Test()
Dim rng As Range
Dim template As WorkSheet
Set template = 'template here'
For Each rng In template.Range(Cells(1,1), Cells(1,200))
iColHeader1 = LookForHeaders(rng.Value)
'Do something with iColHeader1
Next rng
End Sub
Not sure what your looking for but here is example
Option Explicit
Public Sub Example()
Dim LastCol As Long
Dim i As Long
LastCol = ActiveSheet.UsedRange.Columns(ActiveSheet.UsedRange.Columns.Count).Column
For i = 1 To LastCol
If Cells(i) = "Name" Then
Debug.Print Cells(i).Address
End If
Next
End Sub
I'm attempting to write a simple VBA macro that will take the active cell's column and the user's input to add a range of cells on a single row together. The range is calculated by adding the integer the user inputs to the active column and that is the end column. The problem is that it gives me a "Compile Error: Invalid Qualifier" when I run it, and gets angry at the 'total' line.
Here is my code. I'm just starting in VBA, but it can't be that hard....right?
Sub Food()
Dim first As Variant
Dim last As Integer
Dim days As Integer
Dim month As Range
Dim total As Double
first = ActiveCell.Column
days = InputBox("Days in the month?")
last = first + days
Set month.Value = Range(Cells(first, 4), Cells(last, 4))
total.Value = WorksheetFunction.Sum(month)
Worksheets(1).Cells(1, 13).Value = total
End Sub
Looks like a syntax error. Try dropping the .value on month and total.
Sub Food()
Dim first As Variant
Dim last As Integer
Dim days As Integer
Dim month As Range
Dim total As Double
first = ActiveCell.Column
days = InputBox("Days in the month?")
last = first + days
Set month = Range(Cells(first, 4), Cells(last, 4))
total = WorksheetFunction.Sum(month)
Worksheets(1).Cells(1, 13).Value = total
End Sub
If you want to get the value of a range into a variable, you put the .value with the range instead of the variable. For example:
x = cells(1,2).value
The .value property returns the value in a range object. So it doesn't make sense to use it on a function and will cause excel to throw and error if you try. It also doesn't make sense to try to set the month variable as a value instead of a range since that would just make it an array. If you want month to be an array you would want to set it as a variant instead of a range.
For more information on the .value property, see this link:
https://msdn.microsoft.com/en-us/vba/excel-vba/articles/range-value-property-excel