So i need to add 2021 to the Assessment year column in my worksheet but the range keeps breaking
LastRow is returning the correct value but i cant manage to figure out why range isnt working
Sub AutoFill()
Dim rFind As Range
Dim ColumnLetter As Variant
Dim ColumnNumber As Variant
Dim LastRow As Variant
Dim Fill As Range
With Range("A1:P1")
Set rFind = .Find(What:="AssessmentYear", LookAt:=xlWhole, MatchCase:=False, SearchFormat:=False)
If Not rFind Is Nothing Then
ColumnNumber = rFind.Column
ColumnLetter = Split(Cells(1, ColumnNumber).Address, "$")(1)
LastRow = Cells(Rows.Count, 1).End(xlUp).Row
MsgBox ColumnLetter
MsgBox LastRow
Cells(LastRow, ColumnNumber).Value = "Here"
Range("ColumnLetter & 2 : ColumnLetter & LastRow").Value = "2021"
End If
End With
End Sub
I keep getting a method global failed error
The issue is VBA sees "ColumnLetter & 2 : ColumnLetter & LastRow" as a string and is not actually using the variables ColumnLetter or LastRow. Just change it to the below and it should work fine.
Range(ColumnLetter & "2 : " & ColumnLetter & LastRow).Value = "2021"
Auto Fill Data Column
Option Explicit
Sub AutoFillDataColumn()
Const hAddress As String = "A1:P1"
Const hTitle As String = "AssessmentYear"
Const cValue As Variant = 2021
Dim ws As Worksheet: Set ws = ActiveSheet
Dim hrg As Range: Set hrg = ws.Range(hAddress)
Dim rg As Range: Set rg = RefDataColumnByHeader(hrg, hTitle)
If rg Is Nothing Then Exit Sub
rg.Value = cValue
End Sub
Function RefDataColumnByHeader( _
ByVal HeaderRange As Range, _
ByVal HeaderTitle As String) _
As Range
If HeaderRange Is Nothing Then Exit Function
Dim hCell As Range
With HeaderRange
Set hCell = .Find(HeaderTitle, .Cells(.Rows.Count, .Columns.Count), _
xlFormulas, xlWhole, xlByRows)
If hCell Is Nothing Then Exit Function
If hCell.Row = .Worksheet.Rows.Count Then Exit Function
Debug.Print "Header Cell Address: " & hCell.Address(0, 0)
End With
Dim hrrg As Range
Set hrrg = Intersect(HeaderRange, hCell.EntireRow)
Debug.Print "Header Row Range Address: " & hrrg.Address(0, 0)
With hrrg
Dim lcrg As Range
Set lcrg = .Resize(.Worksheet.Rows.Count - .Row).Offset(1)
Debug.Print "Last Cell Range Address: " & lcrg.Address(0, 0)
Dim lCell As Range
Set lCell = lcrg.Find("*", , xlFormulas, , xlByRows, xlPrevious)
If lCell Is Nothing Then Exit Function
Debug.Print "Last Cell Address: " & lCell.Address(0, 0)
Set RefDataColumnByHeader = hCell.Resize(lCell.Row - .Row).Offset(1)
Debug.Print "Data Column Address: " _
& RefDataColumnByHeader.Address(0, 0)
End With
End Function
Related
in my workbook Column I contains Dates.
I can get last Row easily by:
Dim LastRow As Long
LastRow = ActiveSheet.Cells(Rows.Count, "I").End(xlUp).Row
I need to put Row of that column in variable (Long) if first occurrence cell contains today.
actually , the expected code like this:
Set Rng = ActiveSheet.Range("I" & FirstRow & ":I" & LastRow)
Note: using VBA AutoFilter is not applicable on my workbook , Because it is protected and shared on the same time
Please, test the next simple code. All credit should go to #Simon, who clearly described what is to be done. I only put it in place, using a Variant (mtch) variable, able to be checked even if an error (in case of no any match) occurs:
Since your data in I:I does mean Time (something as 03.01.2022 21:27:37), the range must be corrected for the Date Long value to be matched. Please, test the code:
Sub firstCellTest()
Dim sh As Worksheet, firstCell As Long, lastCell As Long, rng As Range, mtch, arr
Set sh = ActiveSheet
lastCell = sh.Range("I" & sh.rows.Count).End(xlUp).row
Set rng = sh.Range("I1:I" & lastCell)
arr = Evaluate("INDEX(int(" & rng.Address & "),0)") 'place in an array only the Date part of existing time
mtch = Application.match(CLng(Date), arr, 0)
If IsNumeric(mtch) Then
firstCell = mtch
Set rng = sh.Range("I" & firstCell, "I" & lastCell)
Else
MsgBox "Today date could not be found..."
End If
If Not rng Is Nothing Then Debug.Print rng.Address
End Sub
Reference a Range Using the Find Method
This solution will find the first occurrence of today's date in a column and create a reference to the range from this cell to the bottom-most non-empty cell in the same column.
The RefTodaysRangeTEST procedure illustrates how to use the RefTodaysRange function (the way to go).
The TodaysRange procedure does the same thing without using a function yet cluttering your code.
The TodaysRangeDebugPrintStudy procedure prints the range addresses at the various stages to the Immediate window (Crtl+G).
Option Explicit
Sub RefTodaysRangeTEST()
Const fCellAddress = "A3"
Dim ws As Worksheet: Set ws = ActiveSheet
Dim fCell As Range: Set fCell = ws.Range(fCellAddress)
Dim trg As Range: Set trg = RefTodaysRange(fCell)
' Continue, e.g.:
If Not fCell Is Nothing Then
MsgBox "Today's Range Address: " & trg.Address(0, 0)
Else
MsgBox "Today's Range Address: not available."
End If
End Sub
Function RefTodaysRange( _
FirstCell As Range) _
As Range
If FirstCell Is Nothing Then Exit Function
Dim lCell As Range ' last (bottom-most) non-empty cell
Dim fCell As Range ' first (top-most) cell containing today's date
With FirstCell
Dim crg As Range
Set crg = .Resize(.Worksheet.Rows.Count - .Row + 1)
Set lCell = crg.Find("*", , xlFormulas, , , xlPrevious)
If lCell Is Nothing Then Exit Function ' no data
Set crg = .Resize(lCell.Row - .Row + 1)
Set fCell = crg.Find(Date, lCell, xlValues, xlWhole)
If fCell Is Nothing Then Exit Function ' today's date not found
End With
Set RefTodaysRange = fCell.Resize(lCell.Row - fCell.Row + 1)
End Function
Sub TodaysRange()
Const fCellAddress = "A3"
Dim ws As Worksheet: Set ws = ActiveSheet
Dim fCell As Range: Set fCell = ws.Range(fCellAddress)
Dim crg As Range: Set crg = fCell.Resize(ws.Rows.Count - fCell.Row + 1)
Dim lCell As Range: Set lCell = crg.Find("*", , xlFormulas, , , xlPrevious)
If lCell Is Nothing Then Exit Sub ' no data from 'fCell' to the bottom
Set crg = fCell.Resize(lCell.Row - fCell.Row + 1)
Set fCell = crg.Find(Date, lCell, xlValues, xlWhole)
If fCell Is Nothing Then Exit Sub ' today's date not found
Set crg = ws.Range(fCell, lCell)
End Sub
Sub TodaysRangeDebugPrintStudy()
Const fCellAddress = "A3"
Dim ws As Worksheet: Set ws = ActiveSheet
Debug.Print "Worksheet: " & ws.Name
Dim fCell As Range: Set fCell = ws.Range(fCellAddress)
Debug.Print "First Cell: " & fCell.Address(0, 0)
Dim crg As Range: Set crg = fCell.Resize(ws.Rows.Count - fCell.Row + 1)
Debug.Print "Column Range: " & crg.Address(0, 0)
Dim lCell As Range: Set lCell = crg.Find("*", , xlFormulas, , , xlPrevious)
If lCell Is Nothing Then Exit Sub ' no data from 'fCell' to the bottom
Debug.Print "Last Cell: " & lCell.Address(0, 0)
Set crg = fCell.Resize(lCell.Row - fCell.Row + 1)
Debug.Print "Column Range: " & crg.Address(0, 0)
Set fCell = crg.Find(Date, lCell, xlValues, xlWhole)
If fCell Is Nothing Then Exit Sub ' today's date not found
Debug.Print "First Cell: " & fCell.Address(0, 0)
Set crg = ws.Range(fCell, lCell)
Debug.Print "Column Range: " & crg.Address(0, 0)
End Sub
Sub try()
Dim SValue As Range
With Range("A1:Z100")
Set SValue = .Find(What:="FF", LookAt:=xlWhole, MatchCase:=False, SearchFormat:=False)
If Not SValue Is Nothing Then
Cell_Split_R = Split(SValue.Address(ReferenceStyle:=xlR1C1), "R")
Cell_Split_C = Split(Cell_Split_R(1), "C")
SRow = Cell_Split_C(0)
SCol = Cell_Split_C(1)
Range(SCol & "4:" & SCol & "100").Select
End If
End With
End sub
'this function does not work even though the variable shows the column number when I run it.
'Any other method of getting the same result is appreciated.
I need to use this to copy columns where the headers are not always in the same position (For eg- "Receipt Number" is in A column sometimes and in R column sometimes).
I do not want to use offset with xldown as I may have blank rows in between.
Get Column Number vs Find First Match
In your case, the first solution is just fine.
Get Column Number
Option Explicit
Sub SelectUsingColumnNumber()
Const hRow As Long = 1
Const sCols As String = "A:Z"
Const sRows As String = "4:100"
Const Title As String = "FF"
Dim ws As Worksheet: Set ws = ActiveSheet
Dim sCol As Long: sCol = GetColumnNumber(ws.Columns(sCols), Title, hRow)
If sCol = 0 Then
MsgBox "The title '" & Title & "' was not found in row '" _
& hRow & "'.", vbCritical, "Select Column Range"
Exit Sub
End If
ws.Rows("4:100").Columns(sCol).Select
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: In a row ('RowNumber') of a range ('rg'), returns
' the worksheet column number of the first cell
' whose value is equal to a string ('Title'). Case-insensitive.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function GetColumnNumber( _
ByVal rg As Range, _
ByVal Title As String, _
Optional ByVal RowNumber As Long = 1) _
As Long
If rg Is Nothing Then Exit Function
If RowNumber < 1 Then Exit Function
If RowNumber > rg.Rows.Count Then Exit Function
Dim fCell As Range
With rg.Rows(RowNumber)
Set fCell = .Find(Title, .Cells(.Cells.Count), xlFormulas, xlWhole)
End With
If fCell Is Nothing Then Exit Function
GetColumnNumber = fCell.Column
End Function
To Utilize the Matching Cell Reference
Sub SelectUsingCell()
Const hRow As Long = 1
Const sCols As String = "A:Z"
Const sRows As String = "4:100"
Const Title As String = "FF"
Dim ws As Worksheet: Set ws = ActiveSheet
Dim fCell As Range
Set fCell = RefFirstMatchInRow(ws.Columns(sCols), Title, hRow)
If fCell Is Nothing Then
MsgBox "The title '" & Title & "' was not found in row '" _
& hRow & "'.", vbCritical, "Select Column Range"
Exit Sub
End If
ws.Rows("4:100").Columns(fCell.Column).Select
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: In a row ('RowNumber') of a range ('rg'), creates
' a reference to the first cell whose value is equal
' to a string ('Title'). Case-insensitive.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function RefFirstMatchInRow( _
ByVal rg As Range, _
ByVal Title As String, _
Optional ByVal RowNumber As Long = 1) _
As Range
If rg Is Nothing Then Exit Function
If RowNumber < 1 Then Exit Function
If RowNumber > rg.Rows.Count Then Exit Function
With rg.Rows(RowNumber)
Set RefFirstMatchInRow _
= .Find(Title, .Cells(.Cells.Count), xlFormulas, xlWhole)
End With
End Function
I tried to implement this but I have a compiler error ("wrong qualification", or something like this, it's not an English version of Excel I have). I suppose it has to do with range / string things ?
Function SearchForTotal(givenLocation As Range, searchText As String) As Range
Debug.Print givenLocation 'gives $U$83
Dim startSearchFrom As String
'-1 because it's from previous column you'll be searching in
startSearchFrom = givenLocation.Offset(0, -1).Address
Debug.Print startSearchFrom
Dim i As Integer: i = startSearchFrom.Row
Do While i > 0
If (searchText = ThisWorkbook.Sheets("Sheet1").Range(startSearchFrom.column & i).Value) Then
Set SearchForTotal= Range(startSearchFrom.column & i)
Exit Do
End If
i = i - 1
Loop
End Function
The error comes from the line "Dim i As Integer: i = startSearchFrom.Row"
I also tried with the variable startSearchFrom as a range instead of a string (and then with the Set) but with this code I have a compiler error too ("types do not match").
startSearchFrom.column is a number so use .Cells(rowno,colno) rather than .Range()
Option Explicit
Function SearchForTotal(givenLocation As Range, searchText As String) As Range
Dim ws As Worksheet, iCol As Long, iRow As Long
Set ws = ThisWorkbook.Sheets("Sheet1")
'-1 because it's from previous column you'll be searching in
iCol = givenLocation.Offset(0, -1).Column
iRow = givenLocation.Row
Do While iRow > 0
If (searchText = ws.Cells(iRow, iCol).Value) Then
Set SearchForTotal = ws.Cells(iRow, iCol)
Exit Do
End If
iRow = iRow - 1
Loop
End Function
Sub test()
Debug.Print SearchForTotal(Range("U83"), "test").Address
End Sub
Find Value Using Loop
Using the Find method would certainly be a better (more efficient) way.
Option Explicit
Function SearchForTotalLoop( _
ByVal GivenLocation As Range, _
ByVal SearchText As String) _
As Range
If GivenLocation Is Nothing Then Exit Function
' There's nothing to left of column `A`:
If GivenLocation.Column = 1 Then Exit Function
'-1 because it's from the previous column you'll be searching in
Dim rgStart As Range: Set rgStart = GivenLocation.Offset(0, -1)
Dim ws As Worksheet: Set ws = GivenLocation.Worksheet
Dim r As Long: r = rgStart.Row
Dim Col As Long: Col = rgStart.Column
Do While r > 0
If ws.Cells(r, Col).Value = SearchText Then ' A<>a
' To ignore case i.e. 'A = a', rather use the following:
'If StrComp(ws.Cells(r, Col).Value, SearchText, vbTextCompare) = 0 Then
Set SearchForTotal = ws.Cells(r, Col)
Exit Do
End If
r = r - 1
Loop
End Function
Sub SearchForTotalTEST()
' s - Start
' f - Found
Dim sCell As Range: Set sCell = Range("B83")
Dim fCell As Range: Set fCell = SearchForTotal(sCell, "Total")
If fCell Is Nothing Then Exit Sub
MsgBox "Starting Cell: " & sCell.Address & vbLf _
& "Found Cell: " & fCell.Address & vbLf _
& "Found Value: " & fCell.Value, vbInformation, "Find Total"
End Sub
EDIT
Using the Find method, you could do something like the following (not tested).
Function SearchForTotal( _
ByVal GivenLocation As Range, _
ByVal SearchText As String) _
As Range
' These two could be additionally used as arguments of the function.
Const FirstRow As Long = 1
Const ColOffset As Long = -1
If GivenLocation Is Nothing Then Exit Function
' There's nothing to left of column `A`:
If GivenLocation.Column + ColOffset < 1 Then Exit Function
If FirstRow > GivenLocation.Row Then Exit Function
Dim ws As Worksheet: Set ws = GivenLocation.Worksheet
If GivenLocation.Column + ColOffset > GivenLocation.Columns.Count _
Then Exit Function
If FirstRow > GivenLocation.Rows.Count Then Exit Function
Dim lCell As Range: Set lCell = GivenLocation.Cells(1).Offset(0, ColOffset)
Dim fCell As Range: Set fCell = ws.Cells(FirstRow, lCell.Column)
Dim rg As Range: Set rg = ws.Range(fCell, lCell)
Dim rCell As Range
Set rCell = rg.Find(SearchText, , xlFormulas, xlWhole, , xlPrevious)
If rCell Is Nothing Then Exit Function
Set SearchForTotal = rCell
End Function
Objective is to highlight rows that meet two different conditions:
If column A is equal to the previous workday (taking into consideration of holidays mentioned in the Reference sheet)
If column B is not equal to "AA"
I have the following code, but am unable to get appropriate rows highlighted (no rows get highlighted due to condition #1 not being met):
Sub code()
Dim lrow As Long
lrow = Cells(Rows.Count, "A").End(xlUp).Row
For i = 2 To lrow
If Cells(i, "A").Value = "=WORKDAY(today(),-1,Reference!$A$2:$A$12)" And Cells(i, "B").Value <> "AA" Then Cells(i, 1).EntireRow.Interior.ColorIndex = 6
Next i
End Sub
You could try this:
Option Explicit
Sub code()
Dim i As Long, lrow As Long
Dim objRangeHolidays As Range
Set objRangeHolidays = Worksheets("Reference").Range("$A$2", "$A$12")
lrow = Cells(rows.Count, "A").End(xlUp).row
For i = 2 To lrow
If CDate(Cells(i, "A").Value) = CDate(Application.WorksheetFunction.WorkDay(Date, -1, objRangeHolidays)) And Cells(i, "B").Value <> "AA" Then
Cells(i, 1).EntireRow.Interior.ColorIndex = 6
End If
Next i
Set objRangeHolidays = Nothing
End Sub
Your original code does not work as "=WORKDAY(today(),-1,Reference!$A$2:$A$12)" is a literal string on VBA, not a function call.
We use CDate() function to make our cell values comparable with WorksheetFunction.Workday() function.
WorksheetFunction.Today() is the same as Date() in VBA.
objRangeHolidays holds holidays defined in Reference sheet.
This is my test result:
Highlight Entire Rows
Adjust the values in the constants section.
Option Explicit
Sub highlightPreviousWorkday()
' Source
Const sName As String = "Sheet1"
Const sFirst As String = "A2"
Const sCritCol As String = "B"
Const sCriteria As String = "AA"
Const sColorIndex As Long = 6
' Holiday
Const hName As String = "Reference"
Const hFirst As String = "A2"
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
wb.Activate ' `Evaluate` will fail if not active.
' Source
Dim srg As Range
With wb.Worksheets(sName).Range(sFirst)
Dim slCell As Range
Set slCell = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If slCell Is Nothing Then Exit Sub
Set srg = .Resize(slCell.Row - .Row + 1)
End With
' Holiday
Dim Holiday As String
With wb.Worksheets(hName).Range(hFirst)
Dim hlCell As Range
Set hlCell = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If Not hlCell Is Nothing Then
Holiday = ",'" & hName & "'!" _
& .Resize(hlCell.Row - .Row + 1).Address
End If
End With
' Evaluation
Dim evDate As Variant
evDate = Evaluate("WORKDAY(TODAY(),-1" & Holiday & ")")
' Combine
Dim drg As Range
If VarType(evDate) = vbDouble Then
Dim sCell As Range
Dim sValue As Variant
Dim sString As String
For Each sCell In srg.Cells
sValue = sCell.Value
If VarType(sValue) = vbDate Then
If CDbl(sValue) = evDate Then
sString = CStr(sCell.EntireRow.Columns(sCritCol).Value)
If sString <> sCriteria Then
Set drg = getCombinedRange(drg, sCell)
End If
End If
End If
Next sCell
End If
' Color
Application.ScreenUpdating = False
srg.EntireRow.Interior.ColorIndex = xlNone
If Not drg Is Nothing Then
drg.EntireRow.Interior.ColorIndex = sColorIndex
End If
Application.ScreenUpdating = True
End Sub
Function getCombinedRange( _
ByVal BuiltRange As Range, _
ByVal AddRange As Range) _
As Range
If BuiltRange Is Nothing Then
Set getCombinedRange = AddRange
Else
Set getCombinedRange = Union(BuiltRange, AddRange)
End If
End Function
I need to replace all cells in column B such that if the cell content has a space between two words e.g abc cde then replace the content with " " i.e make that cell empty.
I think I can do it with a find/replace and wildcard I am tring What:="""*"" ""*"""
the macro runs but nothing is replaced
Thank you
Sub Replace()
Dim rng As Range, cell As Range
Dim ws As Excel.Worksheet
Dim LR As Long
Set ws = ActiveWorkbook.Sheets(1)
LR = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
Set rng = ws.Range("A2:A" & LR)
rng.Select
Selection.Replace What:="""*"" ""*""", Replacement:=" ", LookAt:=xlWhole, _
SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:=False, _
ReplaceFormat:=False
[A2].Select
End Sub
I continued my search and fond something I could alter
Check if field contains any numbers?
Sub CustomerName_AfterUpdate()
Dim ws As Worksheet
Dim rng As Range
Dim acell As Range
Dim LR As Long
Set ws = ThisWorkbook.Sheets("XXX")
LR = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
Set rng = ws.Range("A2:A" & LR)
For Each acell In rng
If HasNumber(acell.Text) Then
Else
acell.Value = " "
End If
Next acell
End Sub
Function HasNumber(strData As String) As Boolean
Dim iCnt As Integer
For iCnt = 1 To Len(strData)
If IsNumeric(Mid(strData, iCnt, 1)) Then
HasNumber = True
Exit Function
End If
Next iCnt
End Function