Find Row Value in VBA Range Compare - excel

I am comparing 2 rows, and I stuck on trying to copy the row value of the current cell in the range.
Problem:
I have a list of Employee Codes, I am comparing these employee codes and copying any data that does not exist (ie new employees). I also need to skip the "*" in my range compare
However I have a sheet where the data I need to copy is in the row below the row where the employee code is
Sheets are set up like this
WSUPER
A B C
XXXX ... ...
CCCC ... ...
VVVV ... ...
Marco_data
A B C D V W
xx xx xx XXXX
xx xx xx * ... ...
xx xx xx FFFF
xx xx xx * ... ...
What I need to copy is the different employee code (in this case FFFF (only this cell) and the Values in Columns V and W
This is the code I have, but does not work correctly
' insert new data - This checks each employees code from the raw and compares in the exisiting. If it doesn't exist it inserts the record. If it does it skips to the next
For Each rng In Sheets("Macro_data").Range(Cells(2, 4), Cells(Cells(Rows.Count, "D").End(xlUp).Row, 1))
Set wb1rng = Sheets("WSUPER").Range("A:A").Find(rng, LookIn:=xlValues, LookAt:=xlWhole)
' If rng Like "[A-Za-z0-9]" Then
If wb1rng Is Nothing Then
coprng = rng.Cells.Row
MsgBox coprng
rng.Cells(coprng, 4).Copy
Sheets("WSUPER").Cells(wbpaste, 1).PasteSpecial xlPasteValues
coprng = coprng + 1
Sheets("Macro_Data").Range(Cells(coprng, 21), Cells(coprng, 22)).Copy
Sheets("WSUPER").Cells(wbpaste, 2).PasteSpecial xlPasteValues
wbpaste = wbpaste + 1
addedrecords = addedrecords + 1
End If
' End If
Next rng
wbpaste is the last row + 1 which is will be the row to paste data in
the idea is to grab only employee code which does not exist ("D") and Column V and W from the row below.
I set the coprng as the range value, but this fails
To skip the * i tried the following, but all failed
If rng Like "[A-Za-z0-9]" Then
Any help would be great

Look Up New Records (feat. a 3D Array)
Adjust the constants as you see fit.
Option Explicit
Sub lookupNewRecords()
' Constants
Const srcName As String = "Macro_data"
Const srcFirstRow As Long = 2
Dim srcColumns As Variant: srcColumns = Array(4, 22, 23)
Const tgtName As String = "WSUPER"
Const tgtFirstRow As Long = 2
Const tgtColumn As Long = 1
' Other Variables
Dim src As Worksheet ' Source Worksheet (Object)
Dim tgt As Worksheet ' Target Worksheet (Object)
Dim rng As Range ' Source Criteria Column Last Non-Empty Cell,
' Source Criteria Column Range,
' Target Criteria Column Last Non-Empty Cell,
' Target Criteria Column Range
Dim Source(2) As Variant ' Source Array of Arrays (Columns)
Dim Target As Variant ' Target (Criteria Column) Array
Dim Result() As Variant ' Result Array
Dim tgtFR As Long ' Target First Row
Dim i As Long ' Source Arrays Row Counter
Dim j As Long ' Result Array Columns Counter
Dim k As Long ' Result Array Rows Counter,
' Source Array of Arrays Elements (Arrays) Counter
' Read Source Columns and write to Source Array of Arrays.
Set src = ThisWorkbook.Worksheets(srcName)
Set rng = src.Columns(srcColumns(0)) _
.Find("*", , xlFormulas, , , xlPrevious)
If rng Is Nothing Then Exit Sub
If rng.Row < srcFirstRow Then Exit Sub
Set rng = src.Range(src.Cells(srcFirstRow, srcColumns(0)), rng)
Source(0) = rng
' Note: If you don't need a row offset like OP did,
' delete just the "1"-s included in the "1, "-s.
Source(1) = rng.Offset(1, srcColumns(1) - srcColumns(0))
Source(2) = rng.Offset(1, srcColumns(2) - srcColumns(0))
' Read Target Criteria Column and write to Target Array.
Set tgt = ThisWorkbook.Worksheets(tgtName)
Set rng = tgt.Columns(tgtColumn) _
.Find("*", , xlFormulas, , , xlPrevious)
If rng Is Nothing Then Exit Sub
If rng.Row < tgtFirstRow Then Exit Sub
tgtFR = rng.Row + 1
Set rng = tgt.Range(tgt.Cells(tgtFirstRow, tgtColumn), rng)
Target = rng
Set rng = Nothing ' Necessary data is in Arrays.
' Write to Result Array.
For i = 1 To UBound(Source(0))
If Source(0)(i, 1) <> "*" And _
IsError(Application.Match(Source(0)(i, 1), Target, 0)) Then
ReDim Preserve Result(2, j)
For k = 0 To 2
Result(k, j) = Source(k)(i, 1)
Next k
j = j + 1
End If
Next i
If j = 0 Then GoTo NoData
' Write to Result Range.
tgt.Cells(tgtFR, tgtColumn) _
.Resize(UBound(Result, 2) + 1, UBound(Result) + 1) = _
Application.Transpose(Result)
' Note: The Result Array was transposed because you can only resize
' an array by its last dimension. In this case it had "3" rows and
' "j" columns, but we needed "j" rows and "3" columns.
' Inform user.
Dim msg As String
If j > 1 Then msg = "s"
MsgBox "Transferred " & j & " new record" & msg & ".", _
vbInformation, "Success"
Exit Sub
NoData:
MsgBox "No new records found.", vbExclamation, "No Transfer"
End Sub

Related

Place sum beneath each column in range in Excel

I have code that will clear everything below the "----" on a spreadsheet full of data. The "----"comes from an export into excel and I don't want data located under it, so I clear it.
After the code clears everything underneath the "----", I want to sum each column in a range and then place each column total underneath its column of data. The column range is F thru T. I'd like to be able to change this range in the code for other projects.
The row may not be the same each time, so the code must sum the columns after the last row of data.
Can anyone help with this, thanks!
Sub Remove_everything_under()
Dim mtch As Long
mtch = 0
On Error Resume Next
mtch = Application.WorksheetFunction.Match("----", ActiveSheet.Range("A:A"), 0) + 0
On Error GoTo 0
If mtch > 0 Then
ActiveSheet.Range("A" & mtch, ActiveSheet.cells(Rows.Count, Columns.Count)).ClearContents
End If
End Sub
Sum-up Variable Sized Column Ranges
Description
Adjust the values in the constants section.
You can easily rewrite the procedure to use some of the constants as arguments for multi-purpose use.
The following will sum up the columns defined by cAddress and put the results (sums) to the cells below each non-empty column range. Executing it again will double the previous result each next time.
Some Challenges
Sum will fail if an error value so the cells of the column range have to be looped through. Solved.
Sum will sum up date values. Not solved, but not an issue when looping.
The loop will sum up TRUE as -1. Solved, but not an issue when Sum is used.
Option Explicit
Sub sumupVariableSizedColumnRanges()
Const cAddress As String = "F:T" ' Columns Address
Const FirstRow As Long = 2 ' First Row
Const hasDates_Slow As Boolean = True ' If 'True', then loop always.
Dim frOffset As Long: frOffset = FirstRow - 1 ' First Row Offset
Dim rg As Range ' Initial Range: from FirstRow to last worksheet row.
With ActiveSheet.Columns(cAddress)
Set rg = .Resize(.Rows.Count - frOffset).Offset(frOffset)
'Debug.Print "Initial Range address = " & rg.Address(0, 0)
End With
Dim crg As Range ' Column Range
Dim rrg As Range ' Result Range
Dim lCell As Range ' Last Cell (Range)
Dim cError As Long ' Current Error Number
Dim Result As Double ' Result
' If error, then loop.
Dim Data As Variant ' Data Array
Dim cValue As Variant ' Current Value
Dim r As Long ' Data Array Rows Counter
' Starting idea if same last row for all columns:
' Set lCell = rg.Find("*", , xlFormulas, , xlByRows, xlPrevious)
' If lCell Is Nothing Then Exit Sub
' Set rg = rg.Resize(lCell.Row - frOffset)
' Debug.Print "Processing Range address = " & rg.Address(0, 0)
For Each crg In rg.Columns
Set lCell = crg.Find("*", , xlFormulas, , , xlPrevious)
If Not lCell Is Nothing Then
Set rrg = crg.Resize(lCell.Row - frOffset)
'Debug.Print "Current Range address = " & rrg.Address(0, 0)
If hasDates_Slow Then
cError = -1 ' Dates are not summed up.
Else
On Error Resume Next ' Sum 'fails' if error values.
Result = Application.Sum(rrg) ' Dates are also summed up.
cError = Err.Number
On Error GoTo 0
End If
If cError <> 0 Then
'Debug.Print "Current Error Number = " & cError
cError = 0
Result = 0
If rrg.Rows.Count = 1 Then
ReDim Data(1 To 1, 1 To 1): Data = rrg.Value
Else
Data = rrg.Value
End If
For r = 1 To UBound(Data, 1)
cValue = Data(r, 1)
If IsNumeric(cValue) Then
If VarType(cValue) <> vbBoolean Then ' exclude TRUE = -1
Result = Result + cValue
End If
End If
Next r
Erase Data
End If
lCell.Offset(1).Value = Result
Set lCell = Nothing
End If
Next crg
End Sub

Excel VBA: Update a cell based on conditions

I am not that much familiar in VBA code. I am looking to implement two scenarios using VBA code in excel.
Scenario 1: If the value in the "C" column contains specific text, then replace the corresponding values in the "A" column as below
If the value in C contains "abc" then A= "abc".
If the value in C contains "gec" then A= "GEC".
It should loop from the second row to last non-empty row
A
B
C
Two
abc-def
Thr
gec-vdg
Thr
abc-ghi
Expected Result:
A
B
C
abc
Two
abc-def
gec
Thr
gec-vdg
abc
Thr
abc-ghi
Scenario 2: If the value in the "B" column is "A", then replace all the "A" value in the B column as "Active". If the value in the "B" column is I", then replace all the I value in the B column as inactive.
It should loop from the second row to last non-empty row
A
B
C
abc
A
abc-def
gec
I
gec-vdg
abc
A
abc-ghi
Expected Result:
A
B
C
abc
Active
abc-def
gec
Inactive
gec-vdg
abc
Active
abc-ghi
I know that it is possible by using excel formulas. Wondering, how it can be implemented using vba code in excel.
Usually people on here won't just write code for you, this is more for helping you with your code when your stuck. However I've written one for you based on the information you have provided. I've assumed your cells in column C would always have the hyphen and you always want what's left of the hyphen. If there is no hyphen or the relevant cell in column C is empty then nothing will be put into the relevant cell in column A.
I've put in to turn off ScreenUpdating for the code as I don't know how many rows you have. If it's a lot and you have a lot going on, then we can also turn off Calculation and Events to speed it up more, or run it as an array if it's really slow but I suspect that it won't be an issue.
Paste this into your relevant sheet module and change the sheet name as well as the column that's finding the last row if C isn't the right one:
Sub UpdateCells()
Application.ScreenUpdating = False
Dim i As Long, lRow As Long, ws As Worksheet
Set ws = Sheets("Sheet1") 'Change Sheet1 to your sheet name
lRow = ws.Range("C" & Rows.Count).End(xlUp).Row 'Finds your last row using Column C
With ws
For i = 2 To lRow 'Loop from row 2 to last row
If .Range("B" & i) = "A" Then
.Range("B" & i) = "Active"
ElseIf .Range("B" & i) = "I" Then
.Range("B" & i) = "Inactive"
End If
If .Range("C" & i) <> "" Then
If InStr(.Range("C" & i), "-") > 0 Then 'If current row Column C contains hyphen
.Range("A" & i) = Left(.Range("C" & i), InStr(.Range("C" & i), "-") - 1)
End If
End If
Next i
End With
Application.ScreenUpdating = True
End Sub
Replace Values
Option Explicit
Sub replaceCustom()
' Define constants.
Const wsName As String = "Sheet1"
Const ColumnsAddress As String = "A:C"
Const FirstRow As Long = 2
Dim Contains As Variant: Contains = VBA.Array(3, 1) ' 0-read, 1-write
Const findContainsList As String = "abc,gec" ' read
Const replContainsList As String = "abc,gec" ' write
Dim Equals As Variant: Equals = VBA.Array(2, 2) ' 0-read, 1-write
Const findEqualsList As String = "A,I" ' read
Const replEqualsList As String = "Active,Inactive" ' write
Dim CompareMethod As VbCompareMethod: CompareMethod = vbTextCompare
' Define workbook.
Dim wb As Workbook: Set wb = ThisWorkbook ' Workbook containing this code.
' Define range.
Dim rng As Range
With wb.Worksheets(wsName).Columns(ColumnsAddress)
Set rng = .Resize(.Worksheet.Rows.Count - FirstRow + 1) _
.Offset(FirstRow - 1).Find( _
What:="*", _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious)
If rng Is Nothing Then
Exit Sub
End If
Set rng = .Resize(rng.Row - FirstRow + 1).Offset(FirstRow - 1)
End With
' Write values from range to array.
Dim Data As Variant: Data = rng.Value
' Write lists to arrays.
Dim findCo() As String: findCo = Split(findContainsList, ",")
Dim replCo() As String: replCo = Split(replContainsList, ",")
Dim findEq() As String: findEq = Split(findEqualsList, ",")
Dim replEq() As String: replEq = Split(replEqualsList, ",")
' Modify values in array.
Dim i As Long
Dim n As Long
For i = 1 To UBound(Data, 1)
For n = 0 To UBound(Contains)
If InStr(1, Data(i, Contains(0)), findCo(n), CompareMethod) > 0 Then
Data(i, Contains(1)) = replCo(n)
Exit For
End If
Next n
For n = 0 To UBound(Equals)
If StrComp(Data(i, Equals(0)), findEq(n), CompareMethod) = 0 Then
Data(i, Equals(1)) = replEq(n)
Exit For
End If
Next n
Next i
' Write values from array to range.
rng.Value = Data
End Sub

Delete filtered data based on date range

I'm looking to filter for blank cells in ColumnB which I've done below:
Dim f As Range
Set f = ActiveSheet.Rows(1)
f.AutoFilter _
Field:=2, _
Criteria1:="", _
VisibleDropdown:=True
From there I'd like to delete this data if the blank cells from the filter are between two specific dates.
The date range would be July 2019 to May 2020. So the condition would be 1 filter column b for blanks, then if column A and/or column c fall within the July 2019 - May 2020 date range then delete the filtered rows.
Thank you!
I don't know if there is a way to use built-in functions to get what you're looking for, but the following code will work. For a large number of rows this might take a while to run.
Public Sub cleanUp()
Dim sRow As Integer
Dim lRow As Integer
Dim rng As Excel.Range
sRow = 2 'set row where data starts
lRow = ThisWorkbook.Worksheets(1).Cells(1048576, 1).End(xlUp).Row 'find the last row of data
Set rng = ThisWorkbook.Worksheets(1).Cells(lRow, 2) 'start at the last row with data
For r = lRow To sRow Step -1 'work backwards to avoid skipping rows
With ThisWorkbook.Worksheets(1)
If .Cells(r, 2).Value = "" Then
'column B of row r was empty
If .Cells(r, 1).Value > 43647 And .Cells(r, 1).Value < 43982 Then
'column A of row r was between 1 July 2019 and 31 May 2020
'row should be deleted
.Rows(r).Delete
ElseIf .Cells(r, 3).Value > 43647 And .Cells(r, 1).Value < 43982 Then
'column C of row r was between 1 July 2019 and 31 May 2020
'row should be deleted
.Rows(r).Delete
Else
'dates in columns A and C did not match criteria
'do nothing
End If
Else
'column B of row r is not empty
'do nothing
End If
End With
Next r
End Sub
Within
Assumptions
The dates are dates, not strings.
Within July 2019 to May 2020 means 07/01/2019 00:00:00 <= n < 06/01/2020 00:00:00.
There is no data below the dataset.
Usage & Tips
Copy the complete code into a standard module, e.g. Module1.
Only run the first procedure (deleteWithin), the rest of them are being called by it, when necessary.
The constants are adjusted to your setup, but check them before running the code.
You should qualify the worksheet, probably the workbook, too. You would't want to run this code on the wrong worksheet.
Test the accuracy of the code first with Select (adjusted in deleteRows) and only afterwards change to Delete.
The Code
Option Explicit
Sub deleteWithin()
' Constants
Const FirstMMMMYYYY As String = "July 2019"
Const LastMMMMYYYY As String = "May 2020"
Const FirstRow As Long = 2 ' First Row of Data
Const Cols As String = "A:C" ' Address of All Columns
' The following 4 column numbers represent the n-th columns of 'Cols'.
Const LastRowColumn As Long = 1 ' Column to Calculate the Last Row
Const DateColumn1 As Long = 1 ' Column Containing the First Date.
Const DateColumn2 As Long = 3 ' Column Containing the Second Date.
Const CriteriaColumn As Long = 2 ' Column Containing the Criteria ("")
Const Criteria As String = ""
' Define Source Range ('rng').
Dim ws As Worksheet
Set ws = ActiveSheet
Dim rng As Range
Set rng = ws.Columns(LastRowColumn).Find("*", , xlFormulas, , , xlPrevious)
If rng Is Nothing Then
GoTo ProcExit
End If
If rng.Row < FirstRow Then
GoTo ProcExit
End If
Dim FirstColumn As Long
FirstColumn = ws.Columns(Cols).Column
Dim LastColumn As Long
LastColumn = FirstColumn + ws.Columns(Cols).Columns.Count - 1
Set rng = ws.Range(ws.Cells(FirstRow, FirstColumn), _
rng.Offset(, LastColumn - LastRowColumn))
' Write the numbers of the rows to delete to Data Rows Array ('DataRows').
' Write values from Source Range to Data Array ('Data').
Dim Data As Variant
Data = rng.Value
' Define Data Rows Array.
Dim DataRows As Variant
ReDim DataRows(1 To UBound(Data))
' Calculate Date Interval ('DateInterval').
Dim DateInterval As Variant
DateInterval = getDateInterval(FirstMMMMYYYY, LastMMMMYYYY)
' Declare additional variables to use in the following 'For Next' loop.
Dim FirstDate As Variant
Dim SecondDate As Variant
Dim i As Long
Dim k As Long
' Loop through rows of Data Array.
For i = 1 To UBound(Data)
' If Criteria is found then write row number to Data Rows Array.
If Data(i, CriteriaColumn) = Criteria Then
FirstDate = Data(i, DateColumn1)
SecondDate = Data(i, DateColumn2)
If fallsWithin(FirstDate, SecondDate, DateInterval) Then
k = k + 1
DataRows(k) = i + FirstRow - 1
Else
End If
End If
Next i
' Delete rows in worksheet.
' Check if at least once Criteria was found.
If k > 0 Then
' Resize Data Rows Array.
ReDim Preserve DataRows(1 To k)
' Delete rows in one go.
deleteRows ws, DataRows
MsgBox "Rows deleted."
Else
MsgBox "Nothing deleted."
End If
ProcExit:
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Converts two strings in the format "MMMM YYYY" to dates and
' returns a 1D two-element array whose first element is less than
' the second element. The first element is to be tested with ">=", while
' the second element is to be tested with "<".
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function getDateInterval(ByVal FirstMMMMYYYY As String, _
ByVal SecondMMMMYYYY As String) _
As Variant
Dim MMMM As Variant
MMMM = Array("January", "February", "March", "April", "May", "June", _
"July", "August", "September", "October", "November", _
"December")
Dim First As Date
First = DateSerial(CLng(Split(FirstMMMMYYYY)(1)), _
Application.Match(Split(FirstMMMMYYYY)(0), MMMM, 0), _
1)
Dim Second As Date
Second = DateSerial(CLng(Split(SecondMMMMYYYY)(1)), _
Application.Match(Split(SecondMMMMYYYY)(0), MMMM, 0) _
+ 1, _
1)
If First < Second Then
getDateInterval = Array(First, Second)
Else
getDateInterval = Array(Second, First)
End If
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Returns 'True' if two values are within the values of an interval.
' The interval is a 1D two-element array and its first element
' is less than its second element. The first element is to be tested with ">=",
' while the second element is to be tested with "<".
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function fallsWithin(ByVal FirstValue As Variant, _
ByVal SecondValue As Variant, _
Interval As Variant) _
As Boolean
Dim StartValue As Variant
StartValue = Interval(LBound(Interval))
Dim EndValue As Variant
EndValue = Interval(UBound(Interval))
If FirstValue < SecondValue Then
If FirstValue >= StartValue And SecondValue < EndValue Then
fallsWithin = True
End If
Else
If SecondValue >= StartValue And FirstValue < EndValue Then
fallsWithin = True
End If
End If
ProcExit:
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Assumes that 'Sheet' is a valid worksheet and 'Data' is a 1D array
' containing at least one row number.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub deleteRows(Sheet As Worksheet, _
DataRows As Variant)
Dim rng As Range
Set rng = Sheet.Rows(DataRows(LBound(DataRows)))
If UBound(DataRows) - LBound(DataRows) > 0 Then
Dim j As Long
For j = LBound(DataRows) + 1 To UBound(DataRows)
' 'Collect' row ranges into one range.
Set rng = Union(rng, Sheet.Rows(DataRows(j)))
Next j
End If
' Delete rows in one go.
rng.Select ' Change to 'rng.Delete' when tested.
End Sub

how to merge cells with same value in one row

How do I merge cells with the same value and color in a row?
and the result should be :
I think you could try this:
Option Explicit
Sub test()
Dim LastRow As Long, i As Long, Value As Long
Dim Color As Double
With ThisWorkbook.Worksheets("Sheet1")
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = LastRow To 2 Step -1
Value = .Range("A" & i).Value
Color = .Range("A" & i).Interior.Color
If .Range("A" & i - 1).Value = Value And .Range("A" & i - 1).Interior.Color = Color Then
.Rows(i).EntireRow.Delete
End If
Next i
End With
End Sub
Copy Consecutive to One
Adjust the values in the constants section to fit your needs.
The image looks like you want all this to happen in the same column
of the same worksheet, which is adjusted in the constants section.
Before writing to Target Column (cTgtCol), the code will clear its
contents. Be careful not to lose data.
Colors are applied using a loop, which will slow down the fast array approach of copying the data.
The Code
Sub CopyConsecutiveToOne()
' Source
Const cSource As Variant = "Sheet1" ' Worksheet Name/Index
Const cSrcCol As Variant = "A" ' Column Letter/Number
Const cSrcFR As Long = 1 ' Column First Row Number
' Target
Const cTarget As Variant = "Sheet1" ' Worksheet Name/Index
Const cTgtCol As Variant = "A" ' Column Letter/Number
Const cTgtFR As Long = 1 ' Column First Row Number
Dim rng As Range ' Source Column Last Used Cell Range,
' Source Column Range, Target Column Range
Dim vntS As Variant ' Source Array
Dim vntT As Variant ' Target Array
Dim vntC As Variant ' Color Array
Dim i As Long ' Source Range/Array Row/Element Counter
Dim k As Long ' Target/Color Array Element Counter
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
'On Error GoTo ProcedureExit
' In Source Worksheet
With ThisWorkbook.Worksheets(cSource).Columns(cSrcCol)
' Calculate Source Column Last Used Cell Range.
Set rng = .Find("*", , xlFormulas, xlWhole, xlByColumns, xlPrevious)
' Check if data in Source Column.
If Not rng Is Nothing Then ' Data found.
' Calculate Source Range.
Set rng = .Range(.Cells(cSrcFR), .Cells(rng.Row))
' Copy values from Source Range to Source Array.
vntS = rng
Else ' Data Not Found.
With .Cells(1)
MsgBox "No Data in column '" & .Split(.Address, "$")(1) & "'."
GoTo ProcedureExit
End With
End If
End With
' In Arrays
' Count the number of elements in Target/Color Array.
k = 1 ' The first element will be included before the loop.
' Loop through elements of Source Array.
For i = 2 To UBound(vntS)
' Check if current value is different then the previous one.
If vntS(i, 1) <> vntS(i - 1, 1) Then
' Count element of Target/Color Array.
k = k + 1
End If
Next
' Write to Target/Color Arrays
' Resize Target/Color Arrays.
ReDim vntT(1 To k, 1 To 1)
ReDim vntC(1 To k, 1 To 1)
' Reset Counter
k = 1 ' The first element will be included before the loop.
' Write first value from Source Array to Target Array.
vntT(1, 1) = vntS(1, 1)
' Write first color value to Target Color Array.
vntC(1, 1) = rng.Cells(1, 1).Interior.Color
' Loop through elements of Source Array.
For i = 2 To UBound(vntS)
' Check if current value is different then the previous one.
If vntS(i, 1) <> vntS(i - 1, 1) Then
' Count element of Target/Color Array.
k = k + 1
' Write from Source Array to Target Array.
vntT(k, 1) = vntS(i, 1)
' Write color values from Source Range to Color Array.
vntC(k, 1) = rng.Cells(i, 1).Interior.Color
End If
Next
' All necessary data is in Target/Color Arrays.
Erase vntS
Set rng = Nothing
' In Target Worksheet
With ThisWorkbook.Worksheets(cTarget).Cells(cTgtFR, cTgtCol)
' Clear contents of range from Target First Cell to Target Bottom Cell.
.Resize(Rows.Count - .Row + 1).ClearContents
' Calculate Target Column Range.
Set rng = .Resize(k)
' Copy Target Array to Target Range.
rng = vntT
' Apply colors to Target Range.
With rng
' Loop through cells of Target Column Range.
For i = 1 To k
' Apply color to current cell of Target Range using the values
' from Color Array.
.Cells(i, 1).Interior.Color = vntC(i, 1)
Next
End With
End With
ProcedureExit:
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
Create a custom function in Visual Basic Editor that will return to the color index of the cell:
Function COLOR(Target As Range)
COLOR = Target.Interior.ColorIndex
End Function
Then in the right column use a formula similar to this:
=IF(OR(A2<>A3,COLOR(A2)<>COLOR(A3)),1,0)
Then filter to show only 1's.

Check if all column values exists in another list

The excel vba macro I have created goes through an entire column and searches each value in the column against another column found on another worksheet. I have a T/F column where I mark down "T" if it is found, "F" if it is not found. However, I feel like the way I am doing it might not be very efficient, as the macro takes about 30 minutes to go through 31,000 rows of values to be searched from another column with about 27,000 number of values.
For a simple illustration, I have included a few images which explains what the macro does.
Initially the T/F column will be empty. Only after executing the macro, would it be filled. I loop through each row in column A and try to find the value against the SearchCol in the next picture.
Here is the vba code I am currently using.
Sub CheckIfValuesExist()
Dim ActiveWS As Worksheet, WS2 As Worksheet
Dim ValueColLetter As String, SearchColLetter As String, TFColLetter As String
Dim LastRow As Long, i As Long
Dim target As Variant, rng As Range
Set ActiveWS = ActiveWorkbook.Worksheets(1)
Set WS2 = ActiveWorkbook.Worksheets(2)
ValueColLetter = "A"
SearchColLetter = "A"
TFColLetter = "B"
LastRow = ActiveWS.Cells.Find(What:="*", SearchOrder:=xlRows, _
SearchDirection:=xlPrevious, _
LookIn:=xlFormulas).Row
For i = 2 To LastRow
target = ActiveWS.Range(ValueColLetter & i).Value
If target <> "" Then
With WS2.Range(SearchColLetter & ":" & SearchColLetter) 'searches all of column A
Set rng = .Find(What:=target, _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not rng Is Nothing Then
ActiveWS.Range(TFColLetter & i).Value = "T" 'value found
Else
ActiveWS.Range(TFColLetter & i).Value = "F" 'value not found
End If
End With
End If
Next i
End Sub
The macro works as intended, I just find it to be slow. Is there any better way to do the same thing but in a quicker manner?
Check Column Against Column
Array Match Range Version
Sub CheckIfValuesExist()
Const cSheet1 As Variant = 1 ' Value Worksheet Name/Index
Const cSheet2 As Variant = 2 ' Search Worksheet Name/Index
Const cFirst As Long = 2 ' First Row
Const cVal As Variant = "A" ' Value Column
Const cSrc As Variant = "A" ' Search Column
Const cTF As Variant = "B" ' Target Column
Const cT As String = "T" ' Found String
Const cF As String = "F" ' Not Found String
Dim RngS As Range ' Search Range
Dim vntV As Variant ' Value Array
Dim vntT As Variant ' Target Array
Dim LastV As Long ' Value Last Column Number
Dim LastS As Long ' Search Last Column Number
Dim i As Long ' Value/Target Row Counter
Dim dummy As Long ' Match Dummy Variable
With Application
.Calculation = xlCalculationManual
.ScreenUpdating = False
End With
On Error GoTo ProcedureExit
With ThisWorkbook.Worksheets(cSheet1)
LastV = .Columns(.Cells(1, cVal).Column).Find("*", , -4123, , 2, 2).Row
vntV = .Range(.Cells(cFirst, cVal), .Cells(LastV, cVal))
End With
With ThisWorkbook.Worksheets(cSheet2)
LastS = .Columns(.Cells(1, cSrc).Column).Find("*", , -4123, , 2, 2).Row
Set RngS = .Range(.Cells(cFirst, cSrc), .Cells(LastS, cSrc))
ReDim vntT(1 To UBound(vntV), 1 To 1)
For i = 1 To UBound(vntV)
On Error Resume Next
If vntV(i, 1) <> "" Then
dummy = Application.Match(vntV(i, 1), RngS, 0)
If Err Then
vntT(i, 1) = cF
Else
vntT(i, 1) = cT
End If
End If
On Error GoTo 0
Next
End With
On Error GoTo ProcedureExit
With ThisWorkbook.Worksheets(cSheet1)
.Range(.Cells(cFirst, cTF), .Cells(.Rows.Count, cTF)).ClearContents
.Cells(cFirst, cTF).Resize(UBound(vntT)) = vntT
End With
ProcedureExit:
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
Let us assume that data included in Sheet 1.
Try:
Option Explicit
Sub VlookUp()
Dim LastRowSV As Long, LastRowV As Long, Counts As Long
Dim wsName As String
Dim wsListSV As Range, cellSV As Range, wsListV As Range, cellV As Range
With ThisWorkbook.Worksheets("Sheet1")
'Find the last row of Search Values
LastRowSV = .Cells(.Rows.Count, "D").End(xlUp).Row
'Find the last row of Values
LastRowV = .Cells(.Rows.Count, "A").End(xlUp).Row
'Set the list with the Search Values
Set wsListSV = .Range(Cells(2, 4), Cells(LastRowSV, 4))
'Set the list with the Values
Set wsListV = .Range(Cells(3, 1), Cells(LastRowV, 1))
'Loop each value in Search Values
For Each cellV In wsListV
Counts = Application.WorksheetFunction.CountIf(wsListSV, cellV)
If Counts <> 0 Then
cellV.Offset(0, 1).Value = "T"
Else
cellV.Offset(0, 1).Value = "F"
End If
Next
End With
End Sub
Result:
Why don't you use the MATCH formula?
If your values are in Col A and the search values are at
the cells $F$5:$F$10 the formula is:
=MATCH(A2,$F$5:$F$10,0)
or if you insist on a T/F result:
=IF(ISERROR(MATCH(A2,$F$5:$F$10,0)),"T","F")
Of cause you can insert this formula also with a macro.

Resources