I was working on an Excel project with multiple Tabs.
One of the worksheets has a column called "Names". My users usually move the columns every now and then. So to extract the correct column I have used this particular VBA code.
HEADER = Sheets("WORKSHEET").Range("A1:Z1").Address
SourceDataColumn = Application.WorksheetFunction.Match("Name", HEADER, 0)
SourceColumnLetter = Split(Cells(1, SourceDataColumn).Address(True, False), "$")(0)
Example: In this case, the output would be [ SourceColumnLetter = C ] containing the "Names"
It works fine, I was just wondering if there is a way to set the 'SourceColumnLetter' as a global variable so that I don't have to use the same whole block of code again and again.
Any help would be appreciated.
Thanks in Advance.
Make a function GetColumnNumberByTitle that finds the column number for you. You can then easily access it by its number.
You can use GetColumnNumberByTitle("Name") in any of your procedures now to get the column by its title. Note that it returns 0 if it does not find the "Name".
See example below:
Option Explicit
Public Sub test()
Dim ColNo As Long
ColNo = GetColumnNumberByTitle("Name") 'get the column number by its title in row 1
If ColNo <> 0 Then
'access the column by its number
Debug.Print ThisWorkbook.Worksheets("WORKSHEET").Cells(1, ColNo)
'this should return the column name
Debug.Print ThisWorkbook.Worksheets("WORKSHEET").Cells(2, ColNo)
'this should return the columns first value
Else
MsgBox "Column Name was not found."
End If
End Sub
Public Function GetColumnNumberByTitle(ByVal ColumnTitle As String) As Long
On Error Resume Next 'next line errors if title is not found
GetColumnNumberByTitle = Application.WorksheetFunction.Match(ColumnTitle, ThisWorkbook.Worksheets("WORKSHEET").Rows(1), 0)
On Error GoTo 0 're-enable error reporting!
'if title was not found this function returns 0
End Function
Another approach could be using formatted tables (ListObjects):
And you can then easily access a column by its title with the following code:
Worksheets("Sheet1").ListObjects("Table1").ListColumns("Name").Range.Select
or to select a specific row
Worksheets("Sheet1").ListObjects("Table1").ListColumns("Name").Range(3).Select
'selects row 3 in column "Name"
Any variable (even Global, Public etc.) must receive somehow a value...
If columns are inserted or deleted its reference is changed even if it has been defined on, let us say, Workbook_Open event.
I would suggest you to make it a named range. Select it, go to the left formula bar side (Name Box), write the name you need (HD, for instance) and press Enter.
Than, you can use it like:
SourceColumnLetter = Split(Range("HD").Address(True, False), "$")(0)
or better try using Cells instead of Range, doing it in the next way (without the column letter):
Cells(x, range("HD").Column)
without any preliminary calculation...
Related
I have an excel with predefined column headers.The problem is these column headers can be at any position for every iteration that is only column headers are fixed not their position(index). So, I need to get the column index based on the column name for further processing.
I found another way to access the column as described in the question, so I though I should change my first answer accordingly. But I thought maybe that answer works for other cases.
Here is a quicker way to get hold of a column containing the value:
Sub Macro1()
ColumnByValue("age").Offset(5, 0).Value = "17"
End Sub
Function ColumnByValue(col As String) As Range
Set ColumnByValue = Range("1:1").Find(col)
End Function
Use .Find() to locate the cell containing the column you are looking for.
I wrote the following example to add a value at a certain column, on a specified row number. You might want to change the code so that it adds the value to the bottom of the existing values instead. For simplicity, the code below will find the column, go down to the specified row and change the value to the value specified by the caller.
Sub Macro1()
Dim result As Boolean
result = add_value_to_column("age", atRow:=3, newValue:=17)
'was that successful?
If result = True Then
MsgBox "value was added"
Else
MsgBox "value NOT added. maybe column does not exist"
End If
End Sub
Function add_value_to_column(col_name As String, _
atRow As Integer, _
newValue As String) As Boolean
Dim c As Range
'Assuming the header row is the first row at the top (1:1)
With Range("1:1")
'try to find the column specified in the function parameters
Set c = .Find(col_name, LookIn:=xlValues)
'If the column was found then it will not be nothing
'NOT NOTHING = THING :)
If Not c Is Nothing Then
'access the row specified in the parameter, at the column,
'and set the value for that location
Cells(atRow, c.Column).Value = newValue
'Optioal: inform the caller that the value was set
add_value_to_column = True
Else
'optional: inform the caller that column was not found
add_value_to_column = False
End If
End With
End Function
Here is an image showing a sample data set. Running the code above has added the value 17 to the age column for the row number 3
I am writing and Excel VBA if statement, but I can't figure out why it is not working.
I have 1 Sheet called "Data" and I want to check if some variables in column I are the same as in my ActiveSheet row 2, column B (which is number 2). I used the following code which ends automatically because it is not working. Anybody an idea?
Example:
Sub test()
If Sheets("Data").Range("I:I") = ActiveSheet(2, 2) Then
MsgBox ("Yes")
Else
MsgBox ("No")
End If
End Sub
You should create a loop in column I if you want to validate every item in that column; use a flag to bail out as soon as you find a mismatching value, so as to avoid looping through all cells once you already know the outcome:
Dim x as long, result As Boolean
result = True
For x = 1 to 100 'let's say up to row 100
If Worksheets("Data").Range("I" & x).value <> ActiveSheet.Cells(2, 2).value Then
result = False
End If
If Not result Then Exit For
Next x
If result Then
MsgBox "Yes"
Else
MsgBox "No"
End If
You compare a whole column (i.e. 1048576 values) with a single value which obviously does not work. Furthermore if you want to access a specific cell you have to use the Cells-collection of your worksheet i.e. ActiveSheet.Cells(2,2)
If you want to compare each cell in column I individually, use a Loop. If you only want to know if the search-value exists somewhere within the column, you can use the Range.Find method.
Good morning,
I am in yet another rut and need some help. I have created a user form that allows a user to delete an entire rows worth of data on a second sheet (rawdata). Everything works fine using the code below, however the combo box ONLY shows the row number. I am in desperate need of changing the column so it will show the project names of the rows that need to be deleted.
Example:
Row: Project
1 Alpha
2 Beta
I would like the combo box to show Alfa and Beta and have the user be able to select the row they would like to delete based on that criteria.
The code below unhides and then hides the sheet that I want this deletion to occur on. This was done with purpose.
Private Sub ComboBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
Dim lRw As Long
ActiveWorkbook.Sheets("RAWDATA").Visible = xlSheetVisible
'get the row number. add 1 because ListIndex starts at zero
lRw = Me.ComboBox1.ListIndex + 1
ActiveWorkbook.Sheets("RAWDATA").Select
Cells(lRw, 1).EntireRow.Delete
ActiveWorkbook.Sheets("RAWDATA").Visible = xlSheetHidden
End Sub
Private Sub CommandButton1_Click()
End Sub
Private Sub UserForm_Initialize()
'assumes data starts in A1 and has a header row
Me.ComboBox1.List = ActiveWorkbook.Sheets("RAWDATA").Cells(1, 1).CurrentRegion.Offset(1).Value
End Sub
Thanks for the help!
Change .Cells(1, 1) to .Cells(1, 2)
The Cells() method gives the code co-ordinates to a specific range using the row and the column number like so:
Cells(r, c)
so in your original code, the .Cells(1, 1) points to "A1" and then uses .CurrentRegion to get all cells within the region of A1.
By replacing the column number and using .Cells(1, 2) we tell it to look at "B1" instead - therefore shifting the column over to the right.
EDIT:
You could apply this logic to the Offset(r, c) function to shift the returned value over by 1 column - so:
.Cells(1, 1).CurrentRegion.Offset(1, 1)
This will more than likely be the culprit as the .Cells() method will point to a specific cell, but the .CurrentRegion() method will return the same result regardless unless we offset it.
Ok lets see if I can make this as easy to understand as possible. I'm working on an inspection workbook. This book is composed of many sheets each with its own name. I have been working with a few people to figure out ways to flag and copy "BLANKornovalue" cells but can not get the keyword idea to work.
Here is a short example of a sheet.
Name = Initiating devices
`$`Column A = adrress
`$`Column B = Type
`$`Column C = location
`$`Column D = Part#
`$`Column E = (RESULTS) dropdown choices
The only column I'm looking at at the moment is "E" "Results" Results can have many different choices from a drop down box, (FAIL, DAMAGED, LOW VOLTS, LOW AMPS, ets) I'm adding to it as i get feedback before the final copy. When an inspector clicks on the dropdown box and selects the words listed above that row would then be copied to the first available space on "FAILED" sheet. First available space would be A6 (due to a title graphic)
Then if the repairs are made the inspector or service tech would be able to change column "E" on the "FAILED" sheets page to another dropdown box. It would have choices like (PASS, REPLACED, REPAIRED, etc) When that was selected the same device and column in the "INITIATING DEVICES" would be updated and the item would be removed from the "FAILED" sheet all items would shift up leaving no spaces. This should happen after the device has been selected if possible.
I'm hoping that once i get a working example I will be able to adapt the code to work with several sheets, so that different values would place the items on different sheets. But that is yet to come.
Example
Column 6 (RESULTS) keyword = FAIL, or Fail or fail or Damaged, or low volts, or low amps, would copy all rows with this value to "FAIL" sheet
keyword = message change copy the row to a sheet called "Message changes" etc
Any help would be great and thanks in advance.
This should get you started. Its simple really if you know a little VBA. But I concur with ErikE that you should use Access or somthing similar
Option Compare Text
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range
Dim row As Long
Dim newRowId As Long
If ActiveSheet.Name = "Blad1" Then
For Each rng In Target.Cells
Select Case rng.Value
Case "FAIL"
row = rng.row
Rows(row).Cut
newRowId = findFirstAvailableRow(8, 1, "Blad2")
ThisWorkbook.Sheets("Blad2").Rows(newRowId).Insert Shift:=xlDown
Case Else
End Select
Next
End If
End Sub
Private Function findFirstAvailableRow(iStartIndex As Long, SearchColumnIndex As Long, workSheetName As String) As Integer
Dim i As Long
For i = iStartIndex To 32000
If ThisWorkbook.Sheets(workSheetName).Cells(i, SearchColumnIndex).Value = "" Then
findFirstAvailableRow = i
Exit For
End If
Next
End Function
Just copy it into your sheet and change the sheet names (I've got the swedish version and EVERY bloody thing is regionalized in Excel)
I have a list in Excel and I need to format rows based on the value in the cell 2 of that row. This is how data looks like
No. | Name | Other data | Other data 2 | Date | Date 2 |
For example, if Name=John Tery => color row as Red, if Name=Mary Jane => color row as Pink, etc.
I tried using conditional formatting, but I did not know how to make this work. I have very little experience with such tasks in Excel.
Can anyone help?
PS. all name are two-word names
if there are only a few names to handle, each conditional-format formula would look like this
=$B2="John Tery"
you need to have selected the affected rows from the top row down (so current active cell is in the 2nd row, not in the last row)
absolute reference to column $B means that for all cells in different columns, column B will be tested
relative reference to row 2 means that for cell in different rows, its own row will be tested (e.g. for cell A42, the formula will test value of $B42)
equality operator = will return either TRUE or FALSE (or an error if any of the arguments are errors) and it has the same use as inside IF conditions...
Edit Rereading the question, I saw that the entire row is to be coloured not just the name. I also decided that if a recognised name is replaced by an unrecognised name, the colour should be removed from the row. The original code has been replaced to address these issues.
I decided I did not care about the answers to my questions because the solution below seems the easiest for any scenerio I could identify.
First you need some method of identifying that "John Tery" is to be coloured red and "Mary Jane" is to be coloured pink. I decided the easiest approach was to have a worksheet NameColour which listed the names coloured as required. So the routine knows "John Tery" is to be red because it is red in this list. I have added a few more names to your list. The routine does not care how many words are in a name.
The code below must go in ThisWorkbook. This routine is triggered whenever a cell is changed. The variables MonitorColNum and MonitorSheetName tell the routine which sheet and column to monitor. Any other cell changes are ignored. If it finds a match, it copies the standard form of the name from NameColour (delete this statement from the code if not required) and colours the cell as required. If it does not find a match, it adds the name to NameColour for later specification of its colour.
Hope this helps.
Option Explicit
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Changed As Range)
Dim CellCrnt As Variant
Dim ColLast As Long
Dim Found As Boolean
Dim MonitorColNum As Long
Dim MonitorSheetName As String
Dim RowNCCrnt As Long
MonitorSheetName = "Sheet2"
MonitorColNum = 2
' So changes to monitored cells do not trigger this routine
Application.EnableEvents = False
If Sh.Name = MonitorSheetName Then
' Use last value in heading row to determine range to colour
ColLast = Sh.Cells(1, Columns.Count).End(xlToLeft).Column
For Each CellCrnt In Changed
If CellCrnt.Column = MonitorColNum Then
With Worksheets("NameColour")
RowNCCrnt = 1
Found = False
Do While .Cells(RowNCCrnt, 1).Value <> ""
If LCase(.Cells(RowNCCrnt, 1).Value) = LCase(CellCrnt.Value) Then
' Ensure standard case
CellCrnt.Value = .Cells(RowNCCrnt, 1).Value
' Set required colour to name
'CellCrnt.Interior.Color = .Cells(RowNCCrnt, 1).Interior.Color
' Set required colour to row
Sh.Range(Sh.Cells(CellCrnt.Row, 1), _
Sh.Cells(CellCrnt.Row, ColLast)).Interior.Color = _
.Cells(RowNCCrnt, 1).Interior.Color
Found = True
Exit Do
End If
RowNCCrnt = RowNCCrnt + 1
Loop
If Not Found Then
' Name not found. Add to list so its colour can be specified later
.Cells(RowNCCrnt, 1).Value = CellCrnt.Value
' Clear any existing colour
Sh.Range(Sh.Cells(CellCrnt.Row, 1), _
Sh.Cells(CellCrnt.Row, ColLast)).Interior.ColorIndex = xlNone
End If
End With
End If
Next
End If
Application.EnableEvents = True
End Sub