Reference a cell by its column name - excel

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)

Related

Creating a mixed reference named range with VBA

I'm trying to write some VBA that can create a named range if it matches a header. I've managed to achieve this but hit a snag that I can't seem to get to work.
I need the named range to be mixed reference only locking in the column. I've got this to work also but when trying to combine everything it doesn't carry the mixed ref.
Examples -
Sub test()
i = 1
Do Until Cells(1, i) = "Created Date"
i = i + 1
Loop
NR1 = Cells(1, i).Offset(1, 0).Address(False, True)
ActiveWorkbook.Names.Add Name:="Created_Date", RefersTo:=NR1
ActiveWorkbook.Names("Created_Date").Comment = ""
End Sub
The above will set the named range with the required mixed ref but obviously no sheet name -
so my thought was simply use activesheet.range(NR1) like so -
Sub test()
i = 1
Do Until Cells(1, i) = "Created Date"
i = i + 1
Loop
NR1 = Cells(1, i).Offset(1, 0).Address(False, True)
ActiveWorkbook.Names.Add Name:="Created_Date", RefersTo:=ActiveSheet.Range(NR1)
ActiveWorkbook.Names("Created_Date").Comment = ""
End Sub
This does create the named range on the sheet but the references go back to being locked to one cell!
any ideas?
You can use the External argument in the Range.Address property to make the Workbook and Sheet name part of the returned address.
If you put an address into the RefersTo argument, it just takes the string as its value instead of going to the cells or range that the address is pointing at. To fix this interaction, you can add an = in front of the address to make the String into a formula which Excel will evaluate.
So with the following changes, your code should do what you want:
NR1 = Cells(1, i).Offset(1, 0).Address(False, True, External:=True)
ActiveWorkbook.Names.Add Name:="Created_Date", RefersTo:="=" & NR1

VBA - How to output a variable to a particular row and column in excel?

This code should find the correct cell (in the column corresponding to it's 'length' and the next empty row) in which to output a variable.
I'm getting the error message:
method range of object _worksheet failed
on lines 13 onward containing "outputcolumn"
In the MsgBox lines, the correct column and row number are being displayed, so I am not sure why it is not happy with my outputcolumn in particular.
Private Sub OutputRowAndColumn()
'Choose correct column: Find the length column and name this outputcolumn
Dim cell As Range, outputcolumn As Integer
Set cell = Range("FindLength").Find(Range("Length").Value, LookIn:=xlValues)
If Not cell Is Nothing Then
outputcolumn = cell.Column
End If
MsgBox "Output column is number " & outputcolumn & "."
'Choose correct row: If the cell to the left of "cell" is empty then this is the first row of output otherwise find next empty cell down
If Sheet1.Range(outputcolumn & "4").Offset(0, 1).Value = "" Then
outputrow = 4 ''' error msg '''
ElseIf Sheet1.Range(outputcolumn & "5").Offset(0, 1).Value = "" Then
outputrow = 5
Else
outputrow = Sheet1.Range(outputcolumn & "4").Offset(0, 1).End(xlDown).Row + 1
End If
MsgBox "Output row is number " & outputrow & "."
'Copy values 1, 2 and 3 from sheet 2 to sheet 1
Sheet1.Range(outputcolumn & outputrow).Offset(0, 1).Value = Sheet2.Range("Value1").Value ''' error msg '''
Sheet1.Range(outputcolumn & outputrow).Offset(0, 2).Value = Sheet2.Range("Value2").Value
Sheet1.Range(outputcolumn & outputrow).Offset(0, 3).Value = Sheet2.Range("Value3").Value
End Sub
outputcolumn is a numeric value (you defined it as Integer, but you always should define variables holding row or column numbers as long to avoid overflow errors).
So let's say outputcolumn gets the number 2 (column B). You write Sheet1.Range(outputcolumn & "4"). To access a range by it's address, You would have to write something like Range("B4"), but what you write is Range(2 & "4"), which means Range("24"), and that is an invalid address for a Range.
You could try to translate the column number 2 to a B, but there is an easier way to access a cell when you know the row and column number: Simply use the cells-property:
If Sheet1.Cells(4, outputcolumn).Offset(0, 1).Value = "" Then
' (or)
If Sheet1.Cells(4, outputcolumn+1).Value = "" Then
Just note that the order of the parameters is row, column.
"outputcolumn" is numeric in your case and when using .Range(), it needs to be a proper alphanumeric cell reference like "C5", not all numeric.
I haven't tried it directly but changing this ...
If Not cell Is Nothing Then
outputcolumn = cell.Column
End If
... to this ...
If Not cell Is Nothing Then
outputcolumn = Split(cell.Address, "$")(1)
End If
... will go a long way to helping you.

Trying to access named tables with VBA to lookup a value

I am trying to find a value in a named table (i.e. a listobject) but failing miserably.
I have a page containing a table of the same name as the page. So far I have 3 working lines to find the word "Area" in the description column and return the row number. This is:
Function CheckArea(ByVal My_Table As String)
Set Table = Worksheets(My_Table).ListObjects(My_Table)
Descriptions = Table.ListColumns("Description").Range
found_cell = Application.Match("Area", Descriptions, 0)
Once I have found the row, I want to access the information in the same row but a different column "Schem typ" and return the value contained. I cannot find how to access a cell within the table by a named column and a referenced row. If should be simple but I cannot find any examples out there. Can anyone help?
Table.ListColumns("Schem Typ").Range.Cells(found_Cell)
Try this
Dim MyTable As ListObject
Set MyTable = ThisWorkbook.ActiveSheet.ListObjects("Tabla_1")
Dim ZZ As Long
Dim MySearch As String
MySearch = "caca" 'change this whatever you want
On Error GoTo Errores:
ZZ = Application.WorksheetFunction.Match(MySearch, MyTable.ListColumns(1).Range, 0) - 1 'minus 1 to exclude header. Change 1 for number of column where you are searching
Debug.Print MyTable.DataBodyRange.Cells(ZZ, 2).Value 'Change 2 for number of column you want to obtain
Clear_All:
Set MyTable = Nothing
Exit Sub
Errores:
If Err.Number = 1004 Then
MsgBox MySearch & " not found"
GoTo Clear_All
Else
MsgBox Err.Description
End If
The code will use Match Function to find the value. IF not found, it will pop up a message (not the best error handling I must admit).
Try to adapt it to your needs.

Excel - Match item http string and display result

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

Dynamically read in Column

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.

Resources