Check column if duplicate record exist in VBA-excel - excel

I'm new to VBA Macro in Excel, and would just like to ask if there's any function for checking duplicate records in excel.
This line of code below removes duplicate referring to column A, but I don't want to actually remove it without user's confirmation, what I wanted to do is to ask for user's confirmation if he wants it to be removed or not, like a popup, and then this line would just execute, but I have no idea if there's a function for checking duplicates.
ActiveSheet.Range("$A$1:$D$38").RemoveDuplicates Columns:=1
Thanks in advance for your help.

Please try the following code. I've set script to make duplicate cell empty, but you can insert your own code.
Sub FindDuplicates()
Dim i As Long
Dim j As Long
Dim lDuplicates As Long
Dim rngCheck As Range
Dim rngCell As Range
Dim rngDuplicates() As Range
'(!!!!!) Set your range
Set rngCheck = ActiveSheet.Range("$A$1:$D$38")
'Number of duplicates found
lDuplicates = 0
'Checking each cell in range
For Each rngCell In rngCheck.Cells
Debug.Print rngCell.Address
'Checking only non empty cells
If Not IsEmpty(rngCell.Value) Then
'Resizing and clearing duplicate array
ReDim rngDuplicates(0 To 0)
'Setting counter to start
i = 0
'Starting search method
Set rngDuplicates(i) = rngCheck.Find(What:=rngCell.Value, After:=rngCell, LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext)
'Check if we have at least one duplicate
If rngDuplicates(i).Address <> rngCell.Address Then
'Counting duplicates
lDuplicates = lDuplicates + 1
'If yes, continue filling array
Do While rngDuplicates(i).Address <> rngCell.Address
i = i + 1
ReDim Preserve rngDuplicates(0 To i)
Set rngDuplicates(i) = rngCheck.FindNext(rngDuplicates(i - 1))
Loop
'Ask what to do with each duplicate
'(except last value, which is our start cell)
For j = 0 To UBound(rngDuplicates, 1) - 1
Select Case MsgBox("Original cell: " & rngCell.Address _
& vbCrLf & "Duplicate cell: " & rngDuplicates(j).Address _
& vbCrLf & "Value: " & rngCell.Value _
& vbCrLf & "" _
& vbCrLf & "Remove duplicate?" _
, vbYesNoCancel Or vbExclamation Or vbDefaultButton1, "Duplicate found")
Case vbYes
'(!!!!!!!) insert here any actions you want to do with duplicate
'Currently it's set to empty cell
rngDuplicates(j).Value = ""
Case vbCancel
'If cancel pressed then exit sub
Exit Sub
End Select
Next j
End If
End If
Next rngCell
'Final message
Call MsgBox("Total number of duplicates: " & lDuplicates & ".", vbExclamation Or vbDefaultButton1, Application.Name)
End Sub
P.S. If you need to remove dulpicates only inside one column, you need to adjust rngCheck variable to that particular column.
P.P.S. In my opinion, it's easier to use conditional formatting.

Related

Excel VBA: How do I add text to a blank cell in a specific column then loop to the next blank cell and add text?

I need a macro to add text to blank cells in Column A. The macro needs to skip cells that have text. The macro needs to stop looping at the end of the data set.
I am trying to use an If Else statement, but I think I'm on the wrong track. My current, non-working code is below. Thank you so much - I'm still new to VBA
Sub ElseIfi()
For i = 2 To 100
If Worksheets("RawPayrollDump").Cells(2, 1).Value = "" Then
Worksheets("RawPayrollDump").Cells(2, 1).Value = "Administration"
Else if(not(worksheets("RawPayrollDump").cells(2,1).value="")) then 'go to next cell
End If
Next
End Sub
To find the last row of data, use the End(xlUp) function.
Try this code. It replaces all empty cells in column A with Administration.
Sub ElseIfi()
Set ws = Worksheets("RawPayrollDump")
lastrow = ws.Cells(Rows.Count, 1).End(xlUp).Row ' last data row
For i = 2 To lastrow ' all rows until last data row
If ws.Cells(i, 1).Value = "" Then ' column A, check if blank
ws.Cells(i, 1).Value = "Administration" ' set text
End If
Next
End Sub
There is no need to loop. Please try this code.
Sub FillBlanks()
Dim Rng As Range
With Worksheets("RawPayrollDump")
Set Rng = Range(.Cells(2, "A"), .Cells(.Rows.Count, "A").End(xlUp))
End With
On Error Resume Next
Set Rng = Rng.SpecialCells(xlCellTypeBlanks)
If Err Then
MsgBox "There are no blank cells" & vbCr & _
"in the specified range.", _
vbInformation, "Range " & Rng.Address(0, 0)
Else
Rng.Value = "Administration"
End If
End Sub
Replace Blanks feat. CurrentRegion
Range.CurrentRegion
Since OP asked for "... stop looping at the end of the data set. ",
I've written this CurrentRegion version.
As I understand it, the end of the data set doesn't mean that there
cannot be blank cells below the last cell containing data in column
A.
Use the 1st Sub to test the 2nd, the main Sub (replaceBlanks).
Adjust the constants including the workbook (in the 1st Sub) to fit your needs.
Criteria is declared as Variant to allow other data types not just strings.
The Code
Option Explicit
Sub testReplaceBlanks()
Const wsName As String = "RawPayrollDump"
Const FirstCellAddress As String = "A2"
Const Criteria As Variant = "Administration"
Dim wb As Workbook: Set wb = ThisWorkbook
Dim ws As Worksheet: Set ws = wb.Worksheets(wsName)
replaceBlanks ws, FirstCellAddress, Criteria
End Sub
Sub replaceBlanks(Sheet As Worksheet, _
FirstCellAddress As String, _
Criteria As Variant)
' Define column range.
Dim ColumnRange As Range
Set ColumnRange = Intersect(Sheet.Range(FirstCellAddress).CurrentRegion, _
Sheet.Columns(Sheet.Range(FirstCellAddress) _
.Column))
' To remove the possibly included cells above the first cell:
Set ColumnRange = Sheet.Range(Range(FirstCellAddress), _
ColumnRange.Cells(ColumnRange.Cells.Count))
' Note that you can also use the addresses instead of the cell range
' objects in the previous line...
'Set ColumnRange = sheet.Range(FirstCellAddress, _
ColumnRange.Cells(ColumnRange.Cells.Count) _
.Address)
' or a mixture of them.
' Write values from column range to array.
Dim Data As Variant
If ColumnRange.Cells.Count > 1 Then
Data = ColumnRange.Value
Else
ReDim Data(1 To 1, 1 To 1): Data(1, 1) = ColumnRange.Value
End If
' Modify array.
Dim i As Long, k As Long
For i = 1 To UBound(Data)
If IsEmpty(Data(i, 1)) Then Data(i, 1) = Criteria: k = k + 1
Next i
' Write modified array to column range.
' The following line is used when only the first cell is known...
'Sheet.Range(FirstCellAddress).Resize(UBound(Data)).Value = Data
' ...but since the range is known and is the same size as the array,
' the following will do:
ColumnRange.Value = Data
' Inform user.
If k > 0 Then GoSub Success Else GoSub Fail
Exit Sub
' Subroutines
Success:
MsgBox "Wrote '" & Criteria & "' to " & k & " previously " _
& "empty cell(s) in range '" & ColumnRange.Address & "'.", _
vbInformation, "Success"
Return
Fail:
MsgBox "No empty cells in range '" & ColumnRange.Address & "'.", _
vbExclamation, "Nothing Written"
Return
End Sub

Filling a specific column based on user inputs and tracking result

I have range of products that are routinely tested every quarter, each product is tested once annually.
I need an excel VBA that prompts the user to input what product was tested and then prompt the user to input in which quarter (e.g. Q1,Q2 etc ) the product was tested. Then in a specific column this information about which quarter the product is tested is displayed and inputted into a cell.
I then want to be able to keep track of this information about which quarter each product was tested every year so for the next test for each product, would like excel to fill the row next to it. Shown below is an visual example of what I'm trying to achieve.
Example of Excel Worksheet
Also attached is the code I have been trying mould to fit my problem.
Dim myValue As Variant
myValue = InputBox("Give me some input")
Dim SearchString As String
Dim SearchRange As Range, cl As Range
Dim FirstFound As String
Dim sh As Worksheet
' Set Search value
SearchString = myValue
Application.FindFormat.Clear
' loop through all sheets
For Each sh In ActiveWorkbook.Worksheets
' Find first instance on sheet
Set cl = sh.Cells.Find(What:=SearchString, _
After:=sh.Cells(1, 1), _
LookIn:=xlValues, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False)
If Not cl Is Nothing Then
' if found, remember location
FirstFound = cl.Address
' format found cell
Do
cl.Font.Bold = True
cl.Interior.ColorIndex = 3
' find next instance
Set cl = sh.Cells.FindNext(After:=cl)
' repeat until back where we started
Loop Until FirstFound = cl.Address
End If
Next
End Sub
In your code you have this line to get the product
myValue = InputBox("Give me some input")
Just add another line to get the Quarter
myValue2 = InputBox("Give me some more input")
The search command is working correctly although it could be made more efficient by restricting the search to the first column not the whole sheet.
Set cl = sh.Cells.Find(What:=SearchString, _
After:=sh.Cells(1, 1), _
LookIn:=xlValues, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False)
To match the whole string rather than a part change the parameter LookAt:=xlWhole.
If you only have one product that matches the user input then this code can be deleted.
Do
cl.Font.Bold = True
cl.Interior.ColorIndex = 3
' find next instance
Set cl = sh.Cells.FindNext(After:=cl)
' repeat until back where we started
Loop Until FirstFound = cl.Address
The row number is found simply with
rowno = cl.Row
The next part you seem to be having difficulty with is locating the next available
blank column in that row. The VBA is as a user would do by using Ctrl-CursorLeft
from the end column.
colno = ws.range(rowno,Columns.count).End(xlToLeft.Column +1
Since it's very unlikely your sheet will span more 702 years this might be clearer
colno = ws.range("ZZ" & rowno).End(xlToLeft).Column + 1
Now update that cell
wc.cell(rowno,colno) = Value2
Put those components together using sensible variable names, add some validation on what the user is entering, insert some debugging messages at critical points and you should get something like this ;
Sub enterdata()
Const DBUG As Boolean = False ' set to TRUE to see each step
Const YR1COL = 5 'E
Dim wb As Workbook, ws As Worksheet
Set wb = ThisWorkbook
Dim sProduct As String
Dim iRowno As Long, iQu As Integer, iColno As Integer
Dim rng As Range, iLastRow As Long, wsMatch As Worksheet, cellMatch As Range
Dim chances As Integer: chances = 3
LOOP1: ' get valid product
sProduct = InputBox(Title:="Input Product", prompt:="Product is ")
If DBUG Then Debug.Print sProduct
If Len(sProduct) > 0 Then
' search through all sheets
For Each ws In wb.Sheets
iLastRow = ws.Range("A" & ws.Rows.count).End(xlUp).Row
If DBUG Then Debug.Print ws.Name & " " & iLastRow
' Search col A of sheet using xlWhole for exact match
Set rng = ws.Range("A2:A" & iLastRow) ' avoid header
Set cellMatch = rng.Find( _
What:=sProduct, _
After:=rng.Cells(2, 1), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False)
' exit on first match
If Not cellMatch Is Nothing Then
Set wsMatch = ws
GoTo LOOP2
End If
Next
Else
Exit Sub
End If
' no match so try again
If cellMatch Is Nothing Then
chances = chances - 1
If chances < 1 Then
MsgBox "Too many tries", vbCritical, "Exiting"
Exit Sub
End If
MsgBox sProduct & " NOT FOUND - " & chances & " tries left", vbExclamation, "Error"
GoTo LOOP1
End If
LOOP2:
iRowno = cellMatch.Row
If DBUG Then Debug.Print wsMatch.Name & " Row = " & iRowno
' determine column
With wsMatch
iColno = .Cells(iRowno, Columns.count).End(xlToLeft).Column + 1
If iColno < YR1COL Then iColno = YR1COL ' start in E
End With
wsMatch.Activate
wsMatch.Cells(iRowno, iColno).Select
If DBUG Then
wsMatch.Cells(iRowno, iColno).Interior.ColorIndex = 6 ' yellow
Debug.Print "Column = " & iColno
End If
If DBUG Then MsgBox "Target cell " & wsMatch.Name & " Row " & iRowno & " Col " & iColno, vbInformation
chances = 3
LOOP3: ' get valid QU
iQu = Application.InputBox(Title:="Input Quarter", prompt:="Test Qu (1-4) for " & sProduct, Type:=1) ' type 1 number
If iQu = 0 Then
GoTo LOOP1
ElseIf iQu > 4 Then
chances = chances - 1
If chances < 1 Then
MsgBox "Too many tries", vbExclamation, "Error"
Exit Sub
End If
MsgBox iQu & " NOT VALID - " & chances & " tries left", vbExclamation, "Error"
GoTo LOOP3
End If
' Update sheet
wsMatch.Cells(iRowno, iColno) = iQu
If DBUG Then wsMatch.Cells(iRowno, iColno).Interior.ColorIndex = 4 ' green
MsgBox "Product=" & sProduct & vbCr _
& wsMatch.Name & " Row=" & iRowno & " Col=" & iColno & " Qu=" & iQu, vbInformation, "Updated"
GoTo LOOP1 ' next product
End Sub

Find Duplicate Entry

I am using Excel 2010.
I have some VBA code which creates a unique key and then looks for duplicate unique key entries. Any duplicates are coloured in red.
I need to automate this a little further. If there is a duplicate unique key, copy the information from the newest entry, and paste it into the line where the original entry is. I then want the newest entry deleted.
The unique key is a concat of the customer name and the date the file was created. There will only ever be at most one duplicate entry per customer and that will be because the date the file was last updated has changed. I need the duplicate concat entry with the newest date to copy the info over the top of the entry with the oldest date on it then delete the original newest date entry. This is because we have other checks that have been completed further along the sheet that we need to keep intact.
Ideally I would like for the message box to still advise how many duplicate entries were found and for the entry to remain coloured red once the copy/paste/delete has taken place to highlight the entry that has been changed.
Private Sub CommandButton1_Click()
'Start of Concatenate Code
Dim i As Integer
Dim r As Range
On Error Resume Next
' Tells Excel to look in column 3 (Column C) for the last one with data in it
lRow = Cells(Rows.Count, 3).End(xlUp).Row
' Tell Excel to focus on cells 4 to 5000
For i = 4 To lRow
' Tell Excel to paste the contents of cell 4 (column D) followed by |
' then the contents of cell 8 (column H) into cell 2 (column B)
Cells(i, 2).Value = Cells(i, 11) & " | " & Cells(i, 7)
Next i
'End of Concatenate Code
'Start of Check for Duplicates code
Dim j As Integer
Dim myCell As Range
Dim myRange As Integer
myRange = Range("A4:A5000").Count
j = 0
' Select the Range
For Each myCell In Range("B4:B5000")
' Check that the cells in the range are not blank
If WorksheetFunction.CountIf(Range("B4:B5000"), myCell.Value) > 1 Then
' Colour the duplicate entries in red
myCell.EntireRow.Interior.ColorIndex = 3
j = j + 1
End If
Next
MsgBox "There are " & j & " duplicates found." & vbCrLf & vbCrLf & _
"Any duplicates have been highlighted in red.", vbInformation + vbOKOnly, _
"Duplicate Entry Checker"
' End of Check for Duplicates code
End Sub
Screenshot of spreadsheet
Thank you #rickmanalexander, I just tried your code (and changed the name of the sheet) but I get a subscript out of range error with the number 9 in the msgbox title. There must be something i have missed but i am not sure what?
Here is the code I used:
Private Sub CommandButton1_Click()
On Error GoTo CleanFail
Dim wrkSht As Worksheet
Set wrkSht = Sheets("Raw Data")
Dim lRow As Long
lRow = wrkSht.Cells(wrkSht.Rows.Count, 3).End(xlUp).Row
Dim arrySheet As Variant
'get the worksheet data into an array
arrySheet = wrkSht.Range("D1:H" & lRow).Value2
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
Dim keyValue As Variant
Dim i As Long
Dim rowNum As Long
Dim dupCount As Long
For i = LBound(arrySheet, 1) To UBound(arrySheet, 1)
'a concatenated key consisting of the:
'row number
'customer's name
keyValue = Join(Array(i, arrySheet(i, 1)), "|")
If Not dict.Exists(keyValue) Then
dict(keyValue) = arrySheet(i, 8) 'save the date for this unique key
Else
'if we make it here, then this is a duplicate customer
'for which we want to check the date
'If the current row's date is greater than the previouly saved date, then
'delete the current row
'determine the row umber for the previously saved entry
'place the most recent date in place of the old date
'color it red
'increase the duplicate counter
If arrySheet(i, 8) > dict(keyValue) Then
wrkSht.Rows(i).EntireRow.Delete
rowNum = CLng(Split(keyValue, "|")(0))
wrkSht.Cells(rowNum, "B").Value = CDate(arrySheet(i, 8))
wrkSht.Rows(rowNum).EntireRow.Interior.ColorIndex = 3
dupCount = dupCount = dupCount + 1
End If
End If
'clear variables
keyValue = vbNullString: rowNum = 0
Next i
MsgBox "There were " & dupCount & " duplicates found." & _
vbCrLf & vbCrLf & _
"Any duplicates have been highlighted in red.", _
vbInformation + vbOKOnly, "Duplicate Entry Checker"
CleanExit:
Exit Sub
CleanFail:
MsgBox Err.Description, vbCritical, Err.Number
Resume CleanExit
End Sub
Edit:
OP was getting Error 9 subscript out of range, because I used arrySheet(i, 8) instead of arrySheet(i, 4). I was thinking that I defined the array from the range starting at column A. Simple mistake with an easy fix.
The Dictionary Object is the perfect candidate for duplicate checks, so that is what I went with. The code below is untested, but should work for your needs.
Option Explicit
Private Sub CommandButton1_Click()
On Error GoTo CleanFail
Dim wrkSht As Worksheet
Set wrkSht = Sheets("Raw Data")
Dim lRow As Long
lRow = wrkSht.Cells(wrkSht.Rows.Count, 3).End(xlUp).Row
Dim arrySheet As Variant
'get the worksheet data into an array
arrySheet = wrkSht.Range("D1:H" & lRow).Value2
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
Dim keyValue As Variant
Dim i As Long
Dim rowNum As Long
Dim dupCount As Long
For i = LBound(arrySheet, 1) To UBound(arrySheet, 1)
'a concatenated key consisting of the:
'row number
'customer's name
keyValue = Join(Array(i, arrySheet(i, 1)), "|")
If Not dict.Exists(keyValue) Then
dict(keyValue) = arrySheet(i, 4) 'save the date for this unique key
Else
'if we make it here, then this is a duplicate customer
'for which we want to check the date
'If the current row's date is greater than the previouly saved date, then
'delete the current row
'determine the row umber for the previously saved entry
'place the most recent date in place of the old date
'color it red
'increase the duplicate counter
If arrySheet(i,4) > dict(keyValue) Then
wrkSht.Rows(i).EntireRow.Delete
rowNum = CLng(Split(keyValue, "|")(0))
wrkSht.Cells(rowNum, "B").Value = CDate(arrySheet(i, 4))
wrkSht.Rows(rowNum).EntireRow.Interior.ColorIndex = 3
dupCount = dupCount = dupCount + 1
End If
End If
'clear variables
keyValue = vbNullString: rowNum = 0
Next i
MsgBox "There were " & dupCount & " duplicates found." & _
vbCrLf & vbCrLf & _
"Any duplicates have been highlighted in red.", _
vbInformation + vbOKOnly, "Duplicate Entry Checker"
CleanExit:
Exit Sub
CleanFail:
MsgBox Err.Description, vbCritical, Err.Number
Resume CleanExit
End Sub

how to iterate over all rows of a excel sheet in VBA

I have this code (This code is in Access VBA which tries to read an excel file and after checking, possibly import it):
Set ExcelApp = CreateObject("Excel.application")
Set Workbook = ExcelApp.Workbooks.Open(FileName)
Set Worksheet = Workbook.Worksheets(1)
now I want to iterate over all rows of the excel worksheet. I want something such as this:
for each row in Worksheet.rows
ProcessARow(row)
next row
where
function ProcessARow(row as ???? )
' process a row
' how Should I define the function
' how can I access each cell in the row
' Is there any way that I can understand how many cell with data exist in the row
end function
My questions:
How to define the for each code that it iterate correctly on all
rows that has data?
How to define ProcessARow properly
How to get the value of each cell in the row.
How to find how many cell with data exist in the row?
Is there any way that I detect what is the data type of each cell?
edit 1
The link solves on problem :
How to define the for each code that it iterate correctly on all rows that has data?
but what about other questions?
For example, how to define ProcessARow correctly?
If you need the values in the Row, you need use the 'Value' Property and after do an cycle to get each value
for each row in Worksheet.rows
Values=row.Value
For each cell in Values
ValueCell=cell
next cell
next row
Unfortunately you questions are very broad however I believe the below sub routine can show you a few ways of achieving what you are after. In regards to what datatype each cell is more involved as it depends what data type you wish to compare it to however I have included some stuff to hopefully help.
sub hopefullyuseful()
dim ws as worksheet
dim rng as Range
dim strlc as string
dim rc as long, i as long
dim lc as long, j as long
dim celltoprocess as range
set ws = activeworkbook.sheets(activesheet.name)
strlc = ws.cells.specialcells(xlcelltypeLastCell).address
set rng = ws.range("A1:" & lc)
rc = rng.rows.count()
debug.print "Number of rows: " & rc
lc = rng.columns.count()
debug.print "Number of columns: " & lc
'
'method 1 looping through the cells'
for i = 1 to rc
for j = 1 to lc
set celltoprocess = ws.cells(i,j)
'this gives you a cell object at the coordinates of (i,j)'
'[PROCESS HERE]'
debug.print celltoprocess.address & " is celltype: " & CellType(celltoprocess)
'here you can do any processing you would like on the individual cell if needed however this is not the best method'
set celltoprocess = nothing
next j
next i
'method 2 looping through the cells using a for each loop'
for each celltoprocess in rng.cells
debug.print celltoprocess.address & " is " & CellType(celltoprocess)
next celltoprocess
'if you just need the data in the cells and not the actual cell objects'
arrOfCellData = rng.value
'to access the data'
for i = lbound(arrOfCellData,1) to ubound(arrOfCellData,1)
'i = row'
for j = lbound(arrOfCellData,2) to ubound(arrOfCellData,2)
'j = columns'
debug.print "TYPE: " & typename(arrOfCellData(i,j)) & " character count:" & len(arrOfCellData(i,j))
next j
next i
set rng=nothing
set celltoprocess = nothing
set ws = nothing
end sub
Function CellType(byref Rng as range) as string
Select Case True
Case IsEmpty(Rng)
CellType = "Blank"
Case WorksheetFunction.IsText(Rng)
CellType = "Text"
Case WorksheetFunction.IsLogical(Rng)
CellType = "Logical"
Case WorksheetFunction.IsErr(Rng)
CellType = "Error"
Case IsDate(Rng)
CellType = "Date"
Case InStr(1, Rng.Text, ":") <> 0
CellType = "Time"
Case IsNumeric(Rng)
CellType = "Value"
End Select
end function
sub processRow(byref rngRow as range)
dim c as range
'it is unclear what you want to do with the row however... if you want
'to do something to cells in the row this is how you access them
'individually
for each c in rngRow.cells
debug.print "Cell " & c.address & " is in Column " & c.column & " and Row " & c.row & " has the value of " & c.value
next c
set c = nothing
set rngRow = nothing
exit sub
if you want your other questions answered you will have to be more specific as to what you are trying to accomplish
While I like the solution offered by #krazynhazy I believe that the following solution might be slightly shorter and closer to what you asked for. Still, I'd use the CellType function offered by Krazynhazy rather than all the Iif I currently have in the below code.
Option Explicit
Sub AllNonEmptyCells()
Dim rngRow As Range
Dim rngCell As Range
Dim wksItem As Worksheet
Set wksItem = ThisWorkbook.Worksheets(1)
On Error GoTo EmptySheet
For Each rngRow In wksItem.Cells.SpecialCells(xlCellTypeConstants).EntireRow.Rows
Call ProcessARow(wksItem, rngRow.Row)
Next rngRow
Exit Sub
EmptySheet:
MsgBox "Sheet is empty." & Chr(10) & "Aborting!"
Exit Sub
End Sub
Sub ProcessARow(wksItem As Worksheet, lngRow As Long)
Dim rngCell As Range
Debug.Print "Cells to process in row " & lngRow & ": " & wksItem.Range(wksItem.Cells(lngRow, 1), wksItem.Cells(lngRow, wksItem.Columns.Count)).SpecialCells(xlCellTypeConstants).Count
For Each rngCell In wksItem.Range(wksItem.Cells(lngRow, 1), wksItem.Cells(lngRow, wksItem.Columns.Count)).SpecialCells(xlCellTypeConstants)
Debug.Print "Row: " & lngRow, _
"Column: " & rngCell.Column, _
"Value: " & rngCell.Value2, _
IIf(Left(rngCell.Formula, 1) = "=", "Formula", IIf(IsDate(rngCell.Value), "Date", IIf(IsNumeric(rngCell.Value2), "Number", "Text")))
Next rngCell
End Sub
Note, that you have to call the sub to call a row must also include the sheet on which a row should be processed.

Error capture while using .Find is not identifing error

When .Find does not find a result, I want an error msg. I have used the method that is almost universally recommended online, but it is not working. When a value is not found, nothing happens. There should be a msg box identified the error.
If Not rFoundCell Is Nothing Then
MsgBox "val: " & rValue.Value & " Matching Cell: " & rFoundCell.Address
Cells(Range(rFoundCell.Address).Row, Range(rFoundCell.Address).Column).Select
Else
MsgBox (rValue.Value & " not found.")
GoTo end_search
End If
I've tried the other way as well:
If rFoundCell Is Nothing Then
Display a msg "not found"
else
Keep going.
That didn't work either. What am i missing?
Full code follows:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim PostRng As Range
Dim PendRng As Range
Dim rValue As Range
Dim lLoop As Long
Dim rFoundCell As Range
Dim INTRng As Range
Set PostRng = Range("g:g")
Set PendRng = Range("k:k")
'"Intersect" will ensure your current cell lies on correct column.
Set INTRng = Intersect(Target, PostRng)
'IF conditions to trigger code.
'This IF confirms only one cell changed. -- I think
If Target.Columns.Count = 1 And Target.Rows.Count = 1 Then
If Not INTRng Is Nothing And LCase(Target.Text) = "y" Then
'This block will return the range & value on the row where "y" or "Y" are entered.
Set rValue = Target.Offset(0, -3) 'Returns value in Col D
If rValue = 0 Or rValue = "" Then Set rValue = Target.Offset(0, -2)
Debug.Print "Target "; Target
Debug.Print "rvalue.value "; rValue.Value
'This will loop through a different column, to find the value identified above, and return its cell address in the other column.
With PendRng
Set rFoundCell = .Cells(1, 1)
For lLoop = 1 To WorksheetFunction.CountIf(.Cells, rValue.Value)
Set rFoundCell = .Find(What:=rValue.Value, _
After:=rFoundCell, _
LookIn:=xlValues, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
Debug.Print "rfoundcell " & rFoundCell
If Not rFoundCell Is Nothing Then
MsgBox "val: " & rValue.Value & " Matching Cell: " & rFoundCell.Address
'This will use the cell address identified above to move the active cell to that address.
'Have to convert the address to row/column to use in Cell.Select.
Cells(Range(rFoundCell.Address).Row, Range(rFoundCell.Address).Column).Select
Else
MsgBox (rValue.Value & " not found.")
GoTo end_search
End If
Next lLoop
End With
End If
End If
end_search:
End Sub
Received help w/ this code here:
Execute a subroutine when a user enters a trigger into a cell
I believe that your code is skipping the If statement that generates the error box if there is not a match.
This is due to For lLoop = 1 To WorksheetFunction.CountIf(.Cells, rValue.Value) exiting when there is no matches because it equates to For lLoop = 1 To 0
I moved all of your error message code into an If statement above the lLoop as follows:
If WorksheetFunction.CountIf(.Cells, rValue.Value) = 0 Then
MsgBox (rValue.Value & " not found.")
GoTo end_search
End If

Resources