Is there to a way to improve the below? I would like to know if an array-like statement can replace the below.
I just want to like remove the multiple "And Not"s so if you can suggest something to optimize the below then I would appreciate it.
Set ws_raw = ThisWorkbook.Worksheets("Raw")
Set ws_master_tracker = ThisWorkbook.Worksheets("Master Tracker")
' more assignments here...
For Each ws In ThisWorkbook.Worksheets
If Not ws Is ws_raw _
And Not ws Is ws_master_tracker _
And Not ws Is ws_title_page _
And Not ws Is ws_sample _
And Not ws Is ws_closing _
And Not ws Is ws_ref _
And Not ws Is ws_pdf_template _
And Not ws.Visible = xlSheetHidden Then
project_name = ws.Range("E3").Value
int_last_row_of_ws = 46
For int_current_row_of_ws = 11 To int_last_row_of_ws
cell_value = ws.Cells(int_current_row_of_ws, 3).Value
With rng_raw
.AutoFilter 1, project_name
End With
Set rng_filtered_raw = ws_raw.Range("J3", ws_raw.Cells(int_last_row_of_raw, int_last_col_of_raw)).SpecialCells(xlCellTypeVisible)
Select Case cell_value
Case Is = "Task Creation!"
module_to_look_for = "Task Creation"
' twenty more cases
' Others that are manually typed
Case Else
module_to_look_for = "MANUAL"
End Select
If Not rng_filtered_raw Is Nothing Then
If module_to_look_for = "MANUAL" Then
' Do nothing
' Highlight cell, etc.
Else
look_up_result = Application.WorksheetFunction.VLookup(module_to_look_for, rng_filtered_raw, 3, False)
If look_up_result = "" Then
ws.Cells(int_current_row_of_ws, 56).Value = "Blank Date!"
Else
ws.Cells(int_current_row_of_ws, 56).Value = look_up_result
End If
End If
End If
Next int_current_row_of_ws
End If
Next ws
Something like:
Dim some_array_variable As Array
Set some_array_variable = (ws_master_tracker, ws_title_page, .....)
If Not ws Is In some_array_variable Then
' some code...
Please help, I am new in VBA.
It depends on the type of variable you are using. This tests for primary colors:
Sub MultiAND()
kolor = "mauve"
If Not kolor = "red" And Not kolor = "blue" And Not kolor = "yellow" Then
MsgBox "secondary"
Else
MsgBox "primary"
End If
End Sub
because the variables are strings, the chain of AND NOT can be replaced by:
Sub StringMeAlong()
kolor = " mauve "
s = " red blue yellow "
If InStr(s, kolor) = 0 Then
MsgBox "secondary"
Else
MsgBox "primary"
End If
End Sub
Here is a simple prime number test for numbers less than 100:
Sub IsItPrime()
s = "|1|2|3|5|7|11|13|17|19|23|29|31|37|41|43|47|53|59|61|67|71|73|79|83|89|97|"
N = 23
v = "|" & CStr(N) & "|"
If InStr(s, v) = 0 Then
MsgBox "not prime"
Else
MsgBox "Prime"
End If
End Sub
If this was in a worksheet formula, you could use the MATCH() function with array constants.
You could use the filter method of an array to check existance:
dim arrSheets() as string
arrSheets = Split("sheetNameOne,sheetNameTwo,sheetNameThree", ",")
if ubound(filter(arrsheets,"sheetNameOne"))>-1 then debug.print "Sheet is in list"
Edit: extended answer as requested...
I used sheet names for brevity rather than worksheet objects as you just have to list the worksheet names in a comma separated string. To use the actual objects as you have done in your code you'd need to assign them to an array like so;
Dim arrSheets(3) as string
arrSheets(0) = ws_master_tracker.name
arrSheets(1) = ws_title_page.name
arrSheets(2) = ws_sample.name
Rather than as a string;
arrSheets = Split("Master Tracker,Title Page,Sample Sheet", ",")
The Split method takes a string and splits it into the elements of an array based on a delimiter, in the above example a comma. Think of Text-To-Columns in Excel.
The Ubound property describes how many elements are in an array - three in this example. It is zero based i.e. 1 instance will return 0, 2 instances = 1, 3 = 2 and so on.
The Filter method returns a new array containing only the elements that match the specified criteria, in this case a specific worksheet name. As Tim Williams commented the method will include substrings, so if you have a sheet called 'my sheet' and one called 'some other sheet' filtering for the word 'sheet' will return both.
Putting them together: Split creates the array from the string, filtering returns an array containing the string(s) requested, Ubound tells you how many times that string is in the filtered array. If its not present Ubound will return -1, otherwise a number greater than -1.
In your original code this will tell you if a sheet name is one of the names you want to omit. You'd then need to check if that sheet is hidden separately.
if ubound(filter(arrsheets,"sheetNameOne"))=-1 then ' the sheet is not in the list
if sheets("sheetNameOne").Visible = xlSheetHidden Then ' is it hidden?
Related
I've got a workbook containing a Summary sheet and 200 numbered sheets that the user fills in one after the other.
The following macro checks about 125 cell values on every numbered sheet, and fills in the Summary, one line per numbered sheet.
If a numbered sheet hasnt been used yet, the macro fills in every column from column D to column DV with the minus sign "-" and goes on to check every numbered sheet one after the other till there's no more to check.
Is there a way to set it so that if an arbitrary number (let's say 10 lines) of the newly generated lines contain only the minus sign "-" from D to DV (Iw,4 to Iw, 126), then the macro would reach its end as it means all the remaining numbered sheets aren't used yet?
Sub SummaryMacro()
Dim Sh As Worksheet
Range("B2:L1000").ClearContents
Iw = 2 ' Index Write
For Each Sh In ActiveWorkbook.Sheets
If Sh.Name = "Summary" Then GoTo EndConsolidation
Cells(Iw, 1).Select
With Selection
.Hyperlinks.Add Anchor:=Selection, Address:="", SubAddress:="'" & Sh.Name & "'" & "!" & "A1", TextToDisplay:="Go to"
End With
Cells(Iw, 2) = Sh.Name
If Sh.Range("D8") = "" Then
Cells(Iw, 3) = "-"
Else
Cells(Iw, 3) = Sh.Range("D8")
End If
'Here the rest of the process (Iw, 4 till Iw, 125)
'The process also includes a few variations:
'Something like 20 of those with various text
If Sh.CheckBoxes("Check Box 1").Value = 1 Then Cells(Iw, 40) = "Declared" Else Cells(Iw, 40) = "-"
'Something like 30 of those with various text
If Sh.Range("H33") = "Issued" Then
Cells(Iw, 42) = "-"
Else
Cells(Iw, 42) = Sh.Range("H33")
End If
'But all in all they are mostly like that
If Sh.Range("C134") = "" Then
Cells(Iw, 126) = "-"
Else
Cells(Iw, 126) = Sh.Range("C134")
End If
Iw = Iw + 1
EndConsolidation:
Next Sh
End Sub
Try adding this code to your For loop at the end:
If (WorksheetFunction.CountIf(Range("D" & Iw & ":DV" & Iw), "-") = 123) Then
Cntr = Cntr + 1 'Blank sheet found
Else
Cntr = 0 'Not blank - Restart counter
End If
If (Cntr = 10) Then Exit For
This counts the number of - in your row and if it equals 123 (D-DV) then it increments the counter otherwise it clears the counter. When Cntr reaches 10 it exits the loop.
HTH
Add this code before your For loop ends
Dim counter As Integer
Dim previousRowBlank As Boolean
counter = 0
previousRowBlank = True
'count if all the 123 cells contain - string
If (WorksheetFunction.CountIf(Sheets("Summary").Range("D" & Iw & ":DV" & Iw), "-") = 123) Then
If (counter = 0) Then
counter = counter + 1
previousRowBlank = True
Else
If (previousRowBlank = True) Then
counter = counter + 1
End If
End If
Else
previousRowBlank = False
counter = 0
End If
'assuming you want to exit when 10 consecutive rows are blank
If (counter = 10) Then
Exit Sub
End If
When I have something like this I Dim a Boolean variable (perhaps call it isPopulated) which only gets switched to true when one of the cells has a value to act on. Then for your case after 10 (or however many you choose) lines, insert an If isPopulated = False Then Exit For to skip the remaining sheets.
EDIT; another idea I just had for you - if all the cells you're checking are supposed to have numeric values then you could use the below;
If Not WorksheetFunction.Concat(Range("D8"), Range("C134"), etc) Like "*#*" Then
'Code here to skip this and remaining sheets.
Obviously you'd need to add the relevant ranges inside the concat() brackets. What that will do is join the contents of those cells together, then check the result for any numbers "*#*" (you could also check for any letters using "*?*"). That gives you a one-code-line answer to the basic question 'is this sheet populated or not'.
I'm sure it's a bad idea to terminate the macro prematurely, based on such an imprecise criterion as the number of "empty" sheets in series. If data starts again on the 11th, 15th or 30th sheet, then you will not process it, you will lose it.
Your macro is not very complex, it shouldn't take longer than a few seconds. For modern Excel, 25K cells are very few
Your code can be shortened a little, simplified. After all, you know all the addresses of the cells that you need to check on each sheet, you enter them in the macro code sequentially, right? Write them on one line separated by commas and put them in a constant.
After that, the whole code will become much shorter:
Sub SummaryMacro()
Const REQUIRED_CELLS_ADDRESS As String = "D8,...<all other source cells>...,B6"
Const SUM_SHEETNAME As String = "Summary"
Dim ws As Worksheet
Dim wsSum As Worksheet
Dim rCell As Range
Dim oTargetCell As Range
Dim oSumCell As Range
Dim aAddress As Variant
Dim i As Integer
aAddress = Split(REQUIRED_CELLS_ADDRESS, ",")
Set wsSum = ActiveWorkbook.Worksheets(SUM_SHEETNAME)
wsSum.UsedRange.Offset(1, 0).ClearContents
Set oTargetCell = wsSum.Range("A1")
For Each ws In ActiveWorkbook.Worksheets
If ws.Name <> SUM_SHEETNAME Then
Set oTargetCell = oTargetCell.Offset(1, 0)
wsSum.Hyperlinks.Add Anchor:=oTargetCell, Address:="", SubAddress:="'" & ws.Name & "'" & "!" & "A1", TextToDisplay:="Go to"
oTargetCell.Resize(1, 123).Value = "-"
Set oSumCell = oTargetCell.Offset(0, 1)
oSumCell.Value = ws.Name
For i = LBound(aAddress) To UBound(aAddress)
Set rCell = ws.Range(aAddress(i))
Set oSumCell = oSumCell.Offset(0, 1)
If Not IsEmpty(rCell) Then oSumCell.Value2 = rCell.Value2
Next i
End If
Next ws
End Sub
Update Everyone knows that working with an array in RAM is much faster than working with sheet cells. Therefore, the outer loop - iterating over the sheets of the book - remains the same, but we change the code inside the loop in this way:
Sub SummaryMacro()
Const SUM_SHEETNAME As String = "Summary"
Dim ws As Worksheet
Dim wsSum As Worksheet
Dim oTargetCell As Range
Dim aResData As Variant
aAddress = Split(REQUIRED_CELLS_ADDRESS, ",")
Set wsSum = ActiveWorkbook.Worksheets(SUM_SHEETNAME)
wsSum.UsedRange.Offset(1, 0).ClearContents
Set oTargetCell = wsSum.Range("A1")
For Each ws In ActiveWorkbook.Worksheets
If ws.Name <> SUM_SHEETNAME Then
Set oTargetCell = oTargetCell.Offset(1, 0)
wsSum.Hyperlinks.Add Anchor:=oTargetCell, Address:="", SubAddress:="'" & ws.Name & "'" & "!" & "A1", TextToDisplay:="Go to " & ws.Name
aResData = validateData(ws.Range("A1:L140").Value2) ' Or "D8:C134" or any other
oTargetCell.Offset(0, 1).Resize(1, UBound(aResData) + 1).Value = aResData
End If
Next ws
End Sub
The main trick is hidden in this line aResData = validateData(ws.Range("A1:L140").Value2)
We call our function and pass it as a parameter an array of cell values from the entire next sheet. Further work on analysis and processing will be carried out with the elements of this array. However, this is not the whole trick.
The validateData() function is very simple and looks like this:
Function validateData(aD As Variant) As Variant
validateData = validateValues(aD(1, 5), aD(2, 8), aD(3, 1), aD(2, 11), _
........ , _
aD(111, 3), aD(112, 8), aD(123, 9), aD(126, 10))
End Function
In other words, we select from the entire large array of aD (the name is deliberately made short, because in this function it will have to be repeated 123 times) only those values that need to be analyzed and we pass on to the next function. Despite the seeming simplicity, this is the most time-consuming part - you need to select from the sheet all cells "D8", "C134", etc. and write down their coordinates (row, column) as numbers aD(4,8), aD(3,134), etc.
Perhaps can help in this the mode R1C1 of displaying the sheet. Or some kind of helper function that will be used when preparing the code (not when executing - we agreed that we will no longer access cells for get values or for .Row and .Column properties!)
What will the validateData() function get? A long one-dimensional array aData(0 To 122) of cell values in the listed order. That is, as many values as there are cells to be filled in the Summary row for this sheet.
The last trick is the process of processing values. It would seem that we have gained nothing from all these transformations. But you claim that there are three groups of checks - for an empty value, for a boolean value (checkbox) and for text lines. This is how it is handled:
Function validateValues(ParamArray aData() As Variant) As Variant
Dim i As Variant
Dim aResult As Variant
ReDim aResult(LBound(aData) To UBound(aData))
For i = LBound(aData) To UBound(aData)
Select Case i
Case 1, 5, 7, 9 ' Checking cells empty / value
aResult(i) = IIf(aData(i) = "", "-", aData(i))
Case 4, 6, 10 ' Checking cells boolean True / "not True" (False or blank)
aResult(i) = IIf(aData(i), "Declared", "-")
Case 0, 3, 8 ' Checking cells string "Issued" / other
aResult(i) = IIf(aData(i) = "Issued", "-", aData(i))
Case 2, 91, 118 ' Checking cells string "Pending" / other
aResult(i) = IIf(aData(i) = "Issued", "-", aData(i))
Case Else ' In a real macro, this line is not needed, it will never be executed because all the cells of the array are already listed above, this is useful only for debugging while all conditions will be written
aResult(i) = "-"
Debug.Print "Cell #" & i & " not processed yet"
End Select
Next i
validateValues = aResult
End Function
And now - again, in just one call! - we write a whole row of results:
oTargetCell.Offset(0, 1).Resize(1, UBound(aResData) + 1).Value = aResData
I am sure that these tricks will reduce the time it takes to form the summary sheet many times over. Please try this and let me know if it gets better?
I have two workbooks, and both shave a list of ALMOST the same items. One of the list has a few extra spaces at the end of its list and it's throwing me completely off.
Public Sub test() 'Imports data into M&R spreadsheet
Dim wbMnR As Workbook
Dim wbMatch As Workbook
Set wbMnR = Workbooks("MnRs.xlsx")
Set wbMatch = Workbooks("Match.xlsm")
Dim myRow As Integer
For i = 1 To 10
myRow = WorksheetFunction.Match(wbMatch.Worksheets(1).Range("a" & CStr(i)), wbMnR.Worksheets(1).Range("A:A"), 0)
Debug.Print myRow
Next i
End Sub
The item list in copy is
"R-01"
"R-02"
"R-03"
"R-04"
the item list in paste is
"R-01 "
"R-03"
"R-02"
"R-04 "
These are just examples I made up and for various reasons I can't input my actual data. I cannot sort my list in the MnR worksheet though since the workbook I was given contains some merge cells and various data which separates specific sections. With the way Match works, I know that using a perfect match of "0" will not work because of the extra space, but using a "1" or "-1" will not work either because my list cannot be sorted.
Try this Select Case statement.
With wbMatch.Worksheets(1)
For i = 1 To 10
myRow = 0
Select Case False
Case IsError(Application.Match(.Range("a" & i), wbMnR.Worksheets(1).Range("A:A"), 0))
myRow = Application.Match(.Range("a" & i), wbMnR.Worksheets(1).Range("A:A"), 0)
Case IsError(Application.Match(.Range("a" & i) & Chr(32), wbMnR.Worksheets(1).Range("A:A"), 0))
myRow = Application.Match(.Range("a" & i) & Chr(32), wbMnR.Worksheets(1).Range("A:A"), 0)
Case Else
'nothing found
End Select
Debug.Print myRow
Next i
End With
If you run into further trouble, that Select Case will be easier to expand upon. To make this more efficient, the most common matches should be at the top of the Case statements.
I have column H that contains long GET requests on sheet 1 such as:
H
GET /profiles/text/23493495_3492/g93id93kd
GET /edit/result/393493/te3903k4d
I would like to have a second sheet with the following type of list in columns A and B:
A B
23493495 identifier1
3903k4 realid2
g93id realid3
Ultimately, I would like a function that will search sheet 1 column H for any of the values in sheet 2 column A. Most of the time there is no separator so I need it to search for strings within the GET string. Once a value in sheet 2 column A is matched with a value in sheet 1 column H, I would like the function to take the corresponding text in sheet 2 column B and print it in sheet 1 column I. There may be multiple matches in a cell, so that would need to be taken into account. So if using the example above:
In H1, there would be a match of 23493495 and g93id within the string. I would like sheet 1 column I to display:
I
identifier1, realid3
I initially started with the below code where I had to specify the list but it doesn't use a second sheet or print the corresponding text of the match. So I would rather have something that meets my needs above, but below is an example of what I have tried so far:
=ListSearchB(J2, "23493495 g93id")
With this module I found that I modified a little:
Function ListSearchB(text As String, wordlist As String, Optional caseSensitive As Boolean = False)
Dim strMatches As String
Dim res As Variant
Dim arrWords() As String
arrWords = Split(wordlist)
On Error Resume Next
Err.Clear
For Each word In arrWords
If caseSensitive = False Then
res = InStr(LCase(text), LCase(word))
Else
res = InStr(text, word)
End If
If res > 0 Then
strMatches = strMatches & word
End If
Next word
If Len(strMatches) <> 0 Then
strMatches = Right(strMatches, Len(strMatches))
End If
ListSearchB = strMatches
End Function
That gives me:
23493495g93id in column I, and I wasn't sure how to separate the two with a comma.
In general though, I would prefer, to use some way to pull the list from sheet 2 and display the value in column I as specified initially.
Give this a try - just adjust the sheet names where commented before running
Sub your_sub()
Dim sGet As Worksheet
Dim sIDs As Worksheet
Dim rget As Range
Dim rIds As Range
'ADJUST SHEET NAME
With Worksheets("GET")
Set rget = Range(.Range("H1"), .Range("h" & .Rows.count).End(xlUp))
End With
'ADJUST SHEET NAME
With Worksheets("IDs")
Set rIds = Range(.Range("A1"), .Range("A" & .Rows.count).End(xlUp))
End With
mys = vbNullString
i = 1
For Each cget In rget
For Each cIds In rIds
If InStr(cget.Value, cIds) <> 0 Then
mys = mys & ", " & cIds.Offset(0, 1).Value
End If
Next cIds
If mys <> vbNullString Then
mys = Right(mys, Len(mys) - 2)
'ADJUST SHEET NAME
Worksheets("GET").Range("I" & i).Value = mys
End If
i = i + 1
mys = vbNullString
Next cget
End Sub
I have a problem. I spent hours designing a form which works just great with all your feedback. Today, everything went wrong. The reason for this is simple. A few new columns got added and, obviously, the data my form is reading in is now wrong.
Thus I was thinking of trying the following...
Rather than using the column number as below
TK = Cells(ActiveCell.Row, "S").Value 'everything in the form refers to the active row
I could possibly use the column headings in Row 1.
Is that possible ? This way the spreadsheet can have columns added up to as many as a user would like and the form would dynamically scan for the right heading and get the column number that way.
My thought is, on opening the form, read in all the headings, pick out the ones I need and assign them to a variable. Then I use my normal code and substitute the variable into the column section.
It sounds easy, but I have no idea how to do this.
Use the versatile Find to give you a quick method of detecting where your header is - or if it is missing
Find details here
In the code below I have specified that the search must return
an exact match (xlWhole)
a case sensitive match (False)
The match can be a partial match (xlPart) if you were looking to match say Game out of Game X
code
Const strFind = "Game"
Sub GetEm()
Dim rng1 As Range
Set rng1 = ActiveSheet.Rows(1).Find(strFind, , xlValues, xlWhole, , , False)
If Not rng1 Is Nothing Then
MsgBox "Your column is " & rng1.Column
Else
MsgBox strFind & " not found", vbCritical
End If
End Sub
Why use a loop? There's no need to.
Dim col as variant
Col = application.match("my header", rows(1), 0)
If iserror(col) then
'not found
Else
TK = cells(activecell.row, col)
End if
For this purpose I usually use a function which runs through the headers (in the first row of a sheet) and returns the number of the column which contains the value I have searched for.
Public Function FindColumn(HeaderName As String, Sht As String) As Long
Dim ColFound As Boolean
Dim StartingPoint As Range
ColFound = False
Set StartingPoint = Sheets(Sht).Range("A1")
Do While StartingPoint.Value <> ""
If UCase(Trim(StartingPoint.Value)) = UCase(Trim(HeaderName)) Then
FindColumn = StartingPoint.Column
ColFound = True
Exit Do
Else
Set StartingPoint = StartingPoint.Offset(0, 1)
End If
Loop
If Not ColFound Then FindColumn = 0
End Function
Example:
If the first row of your sheet named "Timeline" contains headers like e.g. "Date" (A1), "Time" (B1), "Value" (C1) then calling FindColumn("Time", "Timeline") returns 2, since "Time" is the second column in sheet "Timeline"
Hope this may help you a little.
Your thought is a good one. Reading in column headers to calculate addresses is one way to avoid hard coding - e.g.
Sub Test()
Dim R As Range
Set R = ActiveSheet.[A1]
Debug.Print ColNo(R, "Col1Hdr")
End Sub
Function ColNo(HdrRange As Range, ColName As String) As Integer
' 1st column with empty header is returned if string not found
ColNo = 1
Do While HdrRange(1, ColNo) <> ""
If HdrRange(1, ColNo) = ColName Then Exit Do
ColNo = ColNo + 1
Loop
End Function
Another way I frequently use - and I must admit I prefer it over the above, is to define Enum's for all my tables in a seperate "definition" module, e.g.
Public Enum T_VPN ' sheet VPN
NofHRows = 3 ' number of header rows
NofCols = 35 ' number of columns
MaxData = 203 ' last row validated
GroupNo = 1
CtyCode = 2
Country = 3
MRegion = 4
PRegion = 5
City = 6
SiteType = 7
' ....
End Enum
and use it like
Sub Test1()
Debug.Print ActiveSheet(T_VPN.NofHRows, T_VPN.Country)
End Sub
As you can see, the usage is simpler. Allthough this is again "some kind" of hardcoding, having all definition in one place reduces maintenance significantly.
I want to update the contents of a cell in a workbook. My code looks a little like this:
ProductionWorkBook.Sheets("Production Schedule").Cells(StartRow, 1).Value = EstJobName(i)
The cells are referenced using Cells(StartRow, 1) Where StartRow was a pre-declared and pre-defined integer variable that specifies the row and "1" denotes the column.
EDIT:
Now, I want to change this code to reference the columns by the column HEADERS instead.
For example, the header of a column is: "Fab Hours Date", how do I reference that?
Yes, you can simply use the letter name for the column in quotes:
Cells(StartRow, "A")
Edited to answer your further question:
to look for a specific column name, try this:
columnNamesRow = 1 ' or whichever row your names are in
nameToSearch = "Fab Hours" ' or whatever name you want to search for
columnToUse = 0
lastUsedColumn = Worksheets("Foo").Cells(1, Worksheets("Foo").Columns.Count).End(xlToLeft).Column
For col = 1 To lastUsedColumn
If Worksheets("Foo").Cells(columnNamesRow, col).Value = nameToSearch Then
columnToUse = col
End If
Next col
If columnToUse > 0 Then
' found the column you wanted, do your thing here using "columnToUse" as the column index
End If
Here are two different functions to get what you want. To use them, you'd have to put them in your code.
Function ColumnNumberByHeader(text As String, Optional headerRange As Range) As Long
Dim foundRange As Range
If (headerRange Is Nothing) Then
Set headerRange = Range("1:1")
End If
Set foundRange = headerRange.Find(text)
If (foundRange Is Nothing) Then
MsgBox "Could not find column that matches header: " & text, vbCritical, "Header Not Found"
ColumnNumberByHeader = 0
Else
ColumnNumberByHeader = foundRange.Column
End If
End Function
Function ColumnNumberByHeader2(text As String, Optional headerRange As Range) As Long
If (headerRange Is Nothing) Then
Set headerRange = Range("1:1")
End If
On Error Resume Next
ColumnNumberByHeader2 = WorksheetFunction.Match(text, headerRange, False)
If Err.Number <> 0 Then
MsgBox "Could not find column that matches header: " & text, vbCritical, "Header Not Found"
ColumnNumberByHeader2 = 0
End If
On Error GoTo 0
End Function
Example Calls:
ColumnNumberByHeader ("Extn")
ColumnNumberByHeader("1718", Range("2:2"))
Or in your case:
ProductionWorkBook.Sheets("Production Schedule"). _
Cells(StartRow, ColumnNumberByHeader("Fab Hours Date")).Value = EstJobName(i)
ProductionWorkBook.Sheets("Production Schedule").Range("A" & StartRow).Value = EstJobName(i)
Unless you mean the column is a named range you defined?
ProductionWorkBook.Sheets("Production Schedule").Range("E"& StartRow).Value = ...
will do the job.
Though keep in mind that using hard coded references like the column letter will risk that the macro breaks when the sheet is edited (e.g. a column is inserted). It's therefore better to use a named range and Offset to access:
ProductionWorkBook.Sheets("Production Schedule").Range("StartCell").Offset(StartRow-1).Value
Now you only need to provide the name StartCellto your fist cell (make sure that it's a local name in the Name Manager)