I need assistance finding the next instance of an exact string within a cell.
To be precise, I want to look through a series of headings and find the next instance of a declared variable to obtain the column number, and I want to look through that series of headings to find the next empty cell and save that number, and finally, I'd like to take that first column number, and search from second row until I find the first instance of an empty cell, and save that number into a variable. What I have been doing is this:
With Rows(1)
Set found = .Find(what:=Target, After:=.Cells(1, 1))
End With
But it seems that if I accidentally type "s" it will find the first instance of a cell that contains a string that contains the substring "s" (LastName), and not the first cell that contains only "s".
my fear is that if there are columns with " " in them then my program will not function correctly.
Besides that, I sort by a column and when a cell in that column is empty my program pushes it all the way to the bottom of the list and I am trying to delete that empty cell space.
I tried doing Application.WorksheetFunction.Match, HLookup and VLookup and in general the worksheet functions aren't working for me.
So just to give an example of what I want to do:
I have 10 Columns with headings. I want to find the first instance of a column that
contains exactly the string I send into this class. For instance, if the
columns are "FirstName | LastName | Name", I want it to return "Name"
and not "FirstName".
I want to find a column that the user requests as a sort key and verify it's existence
I also want to find a column that is empty (last column)
Finally, I want to find the last row that has a value in relation to the SortColumn.
If you set the lookat parameter to xlWhole, it will only match the whole contents of the cell, so for example:
With Rows(1)
Set found = .Find(what:=target, After:=.Cells(1, 1), lookat:=xlWhole)
End With
To check whether a value was found, you can check whether found is nothing.
Dim exists As Boolean
If Not found Is Nothing Then exists = True
To locate the first empty cell at the end of a row or column of values, I would use the End property to find the last cell in the row/column containing data, then use Offset to find the next cell:
With Rows(1)
Set found = .Find(what:=target, After:=.Cells(1, 1), lookat:=xlWhole)
End With
Dim emptyCell As Range
If Not found Is Nothing Then
Dim col As Integer
col = found.Column
Set emptyCell = Columns(col).End(xlDown)
emptyCell.Offset(1, 0).Select
End If
However, you can't use this if there are some empty cells in the middle of your table of values. (eg if you have values in A1,A2,A3, then A4 is blank and you have more values in A5,A6,A7).
You can use a do loop:
headerToFind = "Name" 'or whatever header you're looking for
x = 1 'or whatever header row is
y = 1 'or whatever first column with header is
Do Until Cells(x,y) = ""
If Cells(x,y) = headerToFind then
MsgBox "The header you are looking for is in row " & x & ", column " & y
Exit Sub
End If
y = y + 1
Loop
MsgBox "Header not found"
In place of the message boxes, put whatever code you want to do with what you find. The first MsgBox will execute if and when the header is found (with x being equal to the row number and y being the column number). The second MsgBox will execute if the desired header is not found.
Related
I am trying to find the row number of the second last instance for a particular value.
See example screenshot below:
The image has 4 values in the column and I am trying to find the second to last instance of a value say GR 3 which is in row 5. I need to find the second last row.
i.e row 3 however I tried using the below code.
Range("E:E").Find(what:="GR 3", after:=Range("E1"), searchdirection:=xlPrevious)
The above code gives me either the last occurrence.
i.e row 5 and if I remove the search direction parameter it gives me only the first occurrence
i.e row 2.
if you filter that value ,you can use this :
ActiveSheet.Range("$A$1:$A$7").AutoFilter Field:=1, Criteria1:="GR 3"
ActiveSheet.AutoFilter.Range.Offset(2).SpecialCells(xlCellTypeVisible).Cells(1, 1).row
it will select the second row in a filtered column value.
The below code will loop through the range and will consider the range from the first cell up to the value of i. In the first "where" i am setting the value of the last occurrence of my desired value in "what". In the second "where" i am finding the previous occurrence of "what" based on the previous value of where which gives me the address of the my second last occurrence and finally the col variable saves the row number from the address stored in where.
Sub Find()
Dim where As Range
Dim col as Long
For i=2 to ThisWorkbook.Sheets("Sheet1").Range("B" & Rows.Count).End(xlUp).row
Set where = Range("E2:E" & i).Find(what:=ThisWorkbook.Sheets("Sheet1").Cells(i, 5).Value, after:=Range("E2"), searchdirection:=xlPrevious)
Set where = ThisWorkbook.Sheets("Sheet1").Range("E2:E" & i).FindPrevious(where)
col = Mid(where.Address(0, 0), 2)
Next i
End Sub
I have an issue with using a formula that contains a fixed cell in VBA. The issue comes when the row number of the variable in the new data changes.
The issue is explained using a simple example as follow. I hope you find it understandable.
Let's say I have a column of numbers (Time) and I want to multiply them by a variable in a cell (The cell below Variable in the following table, $A$2).
First result from first raw data:
The results in the table are calculated using the following formula "=R2C1*RC[-1]" in vba
Now in the next calculation, the row number and variable change and the part of the formula which is using a fixed cell cause problem.
Second raw data to be processed
Because it does not update the row number and use the old row number. I want it to find its location like the second part of the formula (B2 changes to B7).
Thank you for your help!
Cheers,
Aryan
you should reference the found cell row in your formula
ActiveCell.FormulaR1C1 = "=R" & ActiveCell.Row + 1 & "C1*RC[-1]"
but you should also avoid the Activate/ActiveXXX/Select/Selection pattern since is prone to have you quickly lose control over the actually active thing
finally you an use a loop to find all "Time" occurrences (see Here for more info about the pattern)
Option Explicit
Sub main()
Dim f As Range, firstCell As Range
With Worksheets("myWorksheetName") ' reference your worksheet (change myWorksheetName to your actual sheet name)
With .Range("B1", .Cells(.Rows.Count, "B").End(xlUp)) 'reference its column B cells from row 1 down to last not empty one
Set f = .Find("Time", LookIn:=xlValues, lookat:=xlWhole) 'search referenced range for first occurrence of "time"
If Not f Is Nothing Then ' if found...
Set firstCell = f ' store first occurrence cell
Do
f.Offset(1, 1).Resize(4).FormulaR1C1 = "=R" & f.Row + 1 & "C1*RC[-1]" ' populate the range one column to the right of found cell and 4 rows wide with the formula containg the reference of found cell row +1
Set f = .FindNext(f) ' serach for the next "Time" occurrence
Loop While f.Row <> firstCell.Row ' loop till you wrap back to initial occurrence
End If
End With
End With
End Sub
The notation R2C1 is an absolute reference to row 2, column 1.
If you want a reference that is relative to the current cell, you need to use relative reference notation.
RC[-1] points to a cell in the current row and one column to the left
R[1]C points to a cell one row down from the current cell and in the same column as the current cell.
Google for "R1C1 reference". You will find many articles, for e.g. https://smurfonspreadsheets.wordpress.com/2007/11/12/r1c1-notation/
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 using the find function to search through a number of different ranges for a specific heading.
If this heading is present I would then like to extract the information from the cell below. Is it possible for the find function to return the address of the cell found and then I can simply search for Cell(x +1, y) to extract the data from below?
My current code is;
For i = 1 To LastRow
X = VirtMat(i, 1)
Set TestRange = Range(X)
Set BILL = TestRange.Find("Loss Number:")
If Not BILL Is Nothing Then 'RETURN CELL ADRESS AND EXTRACT INFO
Else: rpd(cr, 1) = "NOINFO"
End If
Next
As per MSDN:
Finds specific information in a range, and returns a Range object that represents the first cell where that information is found.
So the answer your question is yes. You can use Address or Row and Column Property of the Range Obejct.
Debug.Print BILL.Address '~~> default is A1 notation
Debug.Print BILL.Row '~~> returns row number
Debug.Print BILL.Column '~~> column number
In fact you can directly get the value in the cell adjacent or below it.
Example: To get value below the found cell, use Offset Method.
Debug.Print BILL.Offset(1, 0).Value
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