I have written a code to compare two worksheets WS1 and Ws2. The code reads the primary key of each row from ws1 and finds the corresponding row with the same primary key in ws2 then all the other column attributes are matched between the two worksheets and reported accordingly.
The code is :
Sub DataComparator(ws1 As Worksheet, ws2 As Worksheet)
Dim ws1row As Long, ws2row As Long, ws1col As Integer, ws2col As Integer
Dim maxrow As Long, maxcol As Integer, colval1 As String, colval2 As String
Dim difference As Long, reportrow As Long, reportcol As Long, flag As Boolean
Dim row As Long, col As Long, pki As Long, pk As String, counter As Long
Dim PctDone As Single, cell1 As String, cell2 As String, bfailed As Boolean
TestDataComparator.FrameProgress.Visible = True
TestDataComparator.LabelProgress.Visible = True
'UserForm1.Visible = True
'Application.ScreenUpdating = False
DoEvents
With ws1.UsedRange
ws1row = .Rows.Count
ws1col = .Columns.Count
End With
With ws2.UsedRange
ws2row = .Rows.Count
ws2col = .Columns.Count
End With
maxrow = ws1row
maxcol = ws1col
pk = UCase(TestDataComparator.TextBox1.Value)
For col = 1 To maxcol
If pk = UCase(ws1.Cells(1, col).Formula) Then
pki = col
End If
Next col
If maxrow < ws2row Then maxrow = ws2row
If maxcol < ws2col Then maxcol = ws2col
difference = 0
reportrow = 0
For row = 2 To maxrow
keyval = ws1.Cells(row, 1).Formula
flag = False
bfailed = False
'reportcol = 1
For col = 2 To maxcol
'If col = pki Then
'Exit For
'End If
counter = counter + 1
cell1 = ""
cell2 = ""
cell1 = ws1.Cells(row, col).Formula
On Error Resume Next
'Set Rng = Range("A2:" & Cells(ws2row, "A").Address)
cell2 = Application.WorksheetFunction.VLookup(keyval, ws2.UsedRange, col, False)
If Err.Number <> 0 Then bfailed = True
On Error GoTo 0
If bfailed = True Then
Exit For
End If
If cell1 <> cell2 Then
flag = True
'difference = difference + 1
diffcolname = ws1.Cells(1, col)
ws1.Cells(row, col).Interior.Color = RGB(255, 255, 0)
ws1.Cells(1, col).Interior.Color = RGB(255, 0, 0)
ws1.Cells(row, col).Font.Bold = True
ws1.Cells(1, pki).Interior.Color = RGB(0, 255, 0)
ws1.Cells(row, pki).Interior.Color = RGB(255, 255, 0)
ws1.Cells(row, pki).Font.Color = RGB(255, 0, 0)
ws1.Cells(row, pki).Font.Bold = True
End If
Next col
If flag = True Then
reportrow = reportrow + 1
End If
PctDone = counter / (maxrow * maxcol)
TestDataComparator.FrameProgress.Caption = "Progress..." & Format(PctDone, "0%")
TestDataComparator.LabelProgress.Width = PctDone * (TestDataComparator.FrameProgress.Width - 10)
DoEvents
Next row
TestDataComparator.Totalcount.Value = row - 2
TestDataComparator.mismatchCount.Value = reportrow
TestDataComparator.mismatchCount.Font = Bold
difference = 0
For col = 1 To maxcol
If ws1.Cells(1, col).Interior.Color = RGB(255, 0, 0) Then
difference = difference + 1
TestDataComparator.AttributeNameList.AddItem (ws1.Cells(1, col))
End If
Next col
TestDataComparator.FrameProgress.Visible = False
TestDataComparator.LabelProgress.Visible = False
'TestDataComparator.PleaseWait.Visible = False
MsgBox difference & " columns contain different data! ", vbInformation, "Comparing Two Worksheets"
Application.ScreenUpdating = True
End Sub
I want the vlookup function to search for the match only in the entire column of WS2 which has the primary key (index pki) rather than ws2.UsedRange. Please provide suggestions. Is there any option which will perform better than vlookup? The use of ws2.UsedRange is making it difficult to search in large datasets that is why I want to reduce search space. My dataset has above 40K rows and 155 columns in excel. Also suggest me a formula for calculating the progress in the progress bar if you think it is not appropriate.
Sample data from OP's comment:
Name Height Weight
Jane 5'6'' 78
Mike 5'4'' 89
Monica 5'2'' 56
I think using a Dictionary (aka Hashtable in other languages) can make it faster. You will need to reference the Microsoft Scripting Runtime library.
You need to read ws2 key values with their row numbers into the Dictionary in one loop before you start going through ws1 row by row. Then in your loop you just look up the value in your dictionary to get its row number on ws2. Something like this:
Dim ws2keys As Dictionary
Set ws2keys = New Dictionary
' assuming you have a header row
For row = 2 To ws2.UsedRange.Rows.Count
keyValue = ws1.Cells(row, 1).Value
If keyValue <> "" Then ws2keys.Add(keyValue, row)
Next
' your dictionary is ready
Then in your loop, instead of using VLookup when going row by row on ws1:
ws2RowIndex = ws2keys.Item(ws1KeyValueYouAreLookingFor)
(The code might not be perfect, I do not have anything Microsoft related on this machine to check the syntax, sorry.)
I've reduced your VLOOKUP for every column to a single MATCH to verify that it exists and one MATCH to set the WS2 row where the match occurs. Everything else is done with direct addressing.
Sub DataComparator(ws1 As Worksheet, ws2 As Worksheet)
Dim ws1row As Long, ws2row As Long, ws1col As Long, ws2col As Long
Dim maxrow As Long, maxcol As Long, colval1 As String, colval2 As String
Dim difference As Long, reportrow As Long, reportcol As Long, flag As Boolean
Dim rw As Long, cl As Long, pki As Long, pk As String, counter As Long
Dim cell1 As String, cell2 As String, bfailed As Boolean
Dim iPCT As Long, ws2rw As Long, rWS1cr As Range, rWS2cr As Range, keyval As Variant, app As Application
Set app = Application
'UserForm1.Visible = True
app.ScreenUpdating = False
'DoEvents
With ws1.Cells(1, 1).CurrentRegion
Set rWS1cr = .Cells
ws1row = .Rows.Count
ws1col = .Columns.Count
End With
With ws2.Cells(1, 1).CurrentRegion
Set rWS2cr = .Cells
ws2row = .Rows.Count
ws2col = .Columns.Count
End With
maxrow = ws1row
maxcol = ws1col
'pk = UCase(TestDataComparator.TextBox1.Value)
For cl = 1 To maxcol
If pk = UCase(rWS1cr.Cells(1, cl).Value) Then
pki = cl
Exit For
End If
Next cl
If maxrow < ws2row Then maxrow = ws2row
If maxcol < ws2col Then maxcol = ws2col
difference = 0
reportrow = 0
With rWS1cr
For rw = 2 To maxrow
keyval = ws1.Cells(rw, 1).Value
If Not IsError(app.Match(keyval, rWS2cr.Columns(1), 0)) Then
ws2rw = app.Match(keyval, rWS2cr.Columns(1), 0)
flag = False
For cl = 2 To maxcol
counter = counter + 1
cell1 = vbNullString
cell2 = vbNullString
cell1 = .Cells(rw, cl).Value
cell2 = rWS2cr.Cells(ws2rw, cl).Value
If cell1 <> cell2 Then
flag = True
'diffcolname = .Cells(1, cl)
.Cells(rw, cl).Interior.Color = RGB(255, 255, 0)
.Cells(1, cl).Interior.Color = RGB(255, 0, 0)
.Cells(rw, cl).Font.Bold = True
.Cells(1, pki).Interior.Color = RGB(0, 255, 0)
.Cells(rw, pki).Interior.Color = RGB(255, 255, 0)
.Cells(rw, pki).Font.Color = RGB(255, 0, 0)
.Cells(rw, pki).Font.Bold = True
End If
Next cl
reportrow = reportrow - CLng(flag)
If iPCT <> CLng((rw / maxrow) * 100) Then
iPCT = CLng((rw / maxrow) * 100)
app.StatusBar = "Progress - " & Format(iPCT, "0\%")
End If
End If
Next rw
For cl = 1 To maxcol
If .Cells(1, cl).Interior.Color = RGB(255, 0, 0) Then
difference = difference + 1
'TestDataComparator.AttributeNameList.AddItem (ws1.Cells(1, col))
End If
Next cl
MsgBox difference & " columns contain different data! ", vbInformation, "Comparing Two Worksheets"
End With
difference = 0
app.ScreenUpdating = True
app.StatusBar = vbNullString
Set app = Nothing
End Sub
I prefer .CurrentRegion to .UsedRange as I find it more reliable. This code was not tested but it does compile and I had to comment out a number of external references to get that to happen.
Related
I have two huge workbooks (old & new) of annual employee data and trying to compare. Each workbook has the same header and employees are in random order.
Here is what I'm trying to accomplish:
Use employee ID (in column D) as reference and compare if they’ve changed information, specially Physician (in column L).
Generate report highlight the different cell with added column (Change Information “Yes/No”) if there are changes or not.
Problem:
This code compare cell by cell only (took a lot of time) and not per employee id how could I insert here the looping of employee id? I am newbie in VBA. Any guidance on how I should go about this? Thanks.
Sub compare2Worksheets()
Dim ws1row As Long, ws2row As Long, ws1col As Integer, ws2col As Integer
Dim maxrow As Long, maxcol As Integer, colval1 As String, colval2 As String
Dim report As Workbook, difference As Long
Dim row As Long, col As Integer
Dim ws1 As Workbooks
Dim ws2 As Workbooks
Set report = Workbooks.Add
'range of Data1
Set ws1 = ThisWorkbook.Worksheets(“Data1”)
With ws1.UsedRange
ws1row = .Rows.Count
ws1col = .Columns.Count
End With
'range of Data2
Set ws2 = myworkbook.Worksheets(“Data2”)
With ws2.UsedRange
ws2row = .Rows.Count
ws2col = .Columns.Count
End With
maxrow = ws1row
maxcol = ws1col
If maxrow < ws2row Then maxrow = ws2row
If maxcol < ws2col Then maxcol = ws2col
'generate report
report.Worksheets(“Sheet1”).Activate
Cells.Clear
Range(“A1”) = “FirstName”
Range(“B1”) = “LastName”
Range(“C1”) = “DOB”
Range(“D1”) = “EmployeeID”
Range(“E1”) = “Address”
Range(“F1”) = “Emailadd”
Range(“G1”) = “Mobilenumber”
Range(“H1”) = “DeptID”
Range(“I1”) = “DeptName”
Range(“J1”) = “Position”
Range(“K1”) = “Status”
Range(“L1”) = “Physician”
Range(“M1”) = “Change InformationY / N”
erow = Data1.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).row
'look for differences
difference = 0
For col = 1 To maxcol
For row = 1 To maxrow
colval1 = ws1.Cells(row, col)
colval2 = ws2.Cells(row, col)
If colval1 <> colval2 Then
difference = difference + 1
'not matched display and highlight
Cells(row, col) = colval1 & “ <> ” & colval2
Cells(row, col).Interior.Color = 255
Cells(row, col).Font.ColorIndex = 2
Cells(row, col).Font.Bold = True
'to update “Change InformationY / N”
Cells(row + 1, 13).Value = "Yes"
Else
Cells(row, col) = colval2
Cells(row + 1, 13).Value = "No"
End If
Next row
Next col
'saving report
If difference > 0 Then
Columns("A:B").ColumnWidth = 25
myfilename = InputBox("Enter Filename")
myfilename = myfilename & “.xlsx”
ActiveWorkbook.SaveAs Filename:=myfilename
End If
End Sub
I would do the following here:
First I would create an array for the EmployeeID and the rows I found them in both sheets.
For that I need to declare a RecordType (has to be defined at the beginning of the module, not in the procedure!)
I assume, that you have less than 1024 employees to handle, if more, simply use a higher value in the Dim-Statement.
I also assume, that the Employee-Id is a string, otherwise you have to use 'Long' or 'Integer' instead
Type EmpRowRec
EmpId as string
Row1 as Long
Row2 as Long
End Type
Dim EmpRowArr(1 to 1024) as EmpRowRec, EmpRowCnt as integer
Then I would go through both sheets and search for the row with the data for an employee:
Dim CurRow as long, CurEmpRow as integer,EmpRowOut as integer
…
EmpRowCnt=0
For CurRow = 2 to ws1Row
Colval1=ws1.cells(currow,4).value
EmpRowCnt=EmpRowCnt+1
EmpRowArr(EmpRowCnt).EmpId=colval1
EmpRowArr(EmpRowCnt).row1=CurRow
Next CurRow
For CurRow = 2 to ws2Row
Colval1=ws2.cells(currow,4).value
EmpRowOut=0
For CurEmpRow=1 to EmpRowCnt
If EmpRowArr(CurEmpRow).EmpId=ColVal1 then EmpRowOut=0:Exit For
Next CurEmpRow
If EmpRowOut=0 then ' Employee is only in sheet 2
EmpRowCnt=EmpRowCnt+1
EmpRowArr(EmpRowCnt).EmpId=colval1
EmpRowArr(EmpRowCnt).row2=CurRow
else
EmpRowArr(EmpRowOut).row2=CurRow
End If
Next CurRow
Now you can go through the array and create your report:
Currow =1 'You already copied the head values
For CurEmpRow=1 to EmpRowCnt
with EmpRowArr(CurEmpRow)
If (.row1>0) and (.row2>0) then 'your result will show only employees in both sheets
Currow=currow+1
For col=1 to maxcol
Colval1=ws1.cells(.row1,col).value
Colval2=ws1.cells(.row2,col).value
Report.cells(currow,col).value=colval1
If colval1<>colval2 then report.cells(currow,col).interior.color=rgb(255,200,200)
Next col
End if
End with
Next CurEmpRow
This method shall show you a generic way to solve such a problem (I have to deal very often with). For sure adaptions e.g. how to handle employees appearing only in one sheet, marking changes with low or high impact are needed, but here I can't help you since I don't know your exact requests.
Due to the fact, that I wrote this text only in word I could not test the fragments under VBA, so some small errors may occur. Please try to fix it.
This is the code with your logic:
Type EmpRowRec
EmpId As String
Row1 As Long
Row2 As Long
End Type
Sub compare2Worksheets()
Dim ws1row As Long, ws2row As Long, ws1col As Integer, ws2col As Integer
Dim maxrow As Long, maxcol As Integer, colval1 As String, colval2 As String
Dim report As Workbook, difference As Long
Dim row As Long, col As Integer
Dim CurRow As Long, CurEmpRow As Integer, EmpRowOut As Integer
Dim wbkA As Workbook, wbkB As Workbook
Dim EmpRowArr(1 To 1024) As EmpRowRec, EmpRowCnt As Integer
'get worksheets from the workbooks
Set wbkA = Workbooks("Data1")
Set ws1 = wbkA.Worksheets("Data1")
'range of Data1
With ws1.UsedRange
ws1row = .Rows.Count
ws1col = .Columns.Count
End With
Set wbkB = Workbooks("Data2")
Set ws2 = wbkB.Worksheets("Data2")
'range of Data2
With ws2.UsedRange
ws2row = .Rows.Count
ws2col = .Columns.Count
End With
maxrow = ws1row
maxcol = ws1col
If maxrow < ws2row Then maxrow = ws2row
If maxcol < ws2col Then maxcol = ws2col
'generate report workbook
Set report = Workbooks.Add
report.Worksheets("Sheet1").Activate
Cells.Clear
Range(“A1”) = “FirstName”
Range(“B1”) = “LastName”
Range(“C1”) = “DOB”
Range(“D1”) = “EmployeeID”
Range(“E1”) = “Address”
Range(“F1”) = “Emailadd”
Range(“G1”) = “Mobilenumber”
Range(“H1”) = “DeptID”
Range(“I1”) = “DeptName”
Range(“J1”) = “Position”
Range(“K1”) = “Status”
Range(“L1”) = “Physician”
Range(“M1”) = “Change InformationY / N”
erow = Sheet1.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).row
'go through both sheets and search for the row with the data for an employee
EmpRowCnt = 0
For CurRow = 2 To maxrow 'ws1row
colval1 = ws1.Cells(CurRow, 4).Value
EmpRowCnt = EmpRowCnt + 1
EmpRowArr(EmpRowCnt).EmpId = colval1
EmpRowArr(EmpRowCnt).Row1 = CurRow
Next CurRow
For CurRow = 2 To maxrow 'ws2row
colval1 = ws2.Cells(CurRow, 4).Value
EmpRowOut = 0
For CurEmpRow = 1 To EmpRowCnt
If EmpRowArr(CurEmpRow).EmpId = colval1 Then EmpRowOut = 0: Exit For
Next CurEmpRow
If EmpRowOut = 0 Then ' Employee is only in sheet 2
EmpRowCnt = EmpRowCnt + 1
EmpRowArr(EmpRowCnt).EmpId = colval1
EmpRowArr(EmpRowCnt).Row2 = CurRow
Else
EmpRowArr(EmpRowOut).Row2 = CurRow
End If
Next CurRow
'go through the array and create your report
CurRow = 1 'You already copied the head values
For CurEmpRow = 1 To EmpRowCnt
With EmpRowArr(CurEmpRow)
If (.Row1 > 0) And (.Row2 > 0) Then 'your result will show only employees in both sheets
CurRow = CurRow + 1
For col = 1 To maxcol
colval1 = ws1.Cells(.Row1, col).Value
colval2 = ws1.Cells(.Row2, col).Value
report.Cells(CurRow, col).Value = colval1
If colval1 <> colval2 Then report.Cells(CurRow, col).Interior.Color = RGB(255, 200, 200)
Next col
End If
End With
Next CurEmpRow
If CurRow > 0 Then
Columns("A:Y").ColumnWidth = 25
myfilename = InputBox("Enter Filename")
myfilename = myfilename & “.xlsx”
ActiveWorkbook.SaveAs Filename:=myfilename
End If
End Sub
Use a Dictionary as a lookup table for the row number of each ID on the old data sheet. Then scan down the new sheet comparing rows with the same ID. IDs that appear on the new sheet but not the old are tagged "added". Those on the old sheet but not the new are tagged "deleted".
Option Explicit
Sub compare2Worksheets()
' config
Const COL_ID = "D"
Const COLS = 12 ' header col A to L
Dim wb1 As Workbook, wb2 As Workbook, wbRep As Workbook
Dim ws1 As Worksheet, ws2 As Worksheet, wsRep As Worksheet
Dim LastRow As Long, c As Long, i As Long, r As Long, n As Long
Dim bDiff As Boolean, t0 As Single
t0 = Timer
Dim dict As Object, key As String
Set dict = CreateObject("Scripting.Dictionary")
'range of Data1
Set wb1 = ThisWorkbook
Set wb2 = ThisWorkbook ' or other
Set ws1 = wb1.Sheets("Data1") ' old data
Set ws2 = wb2.Sheets("Data2") ' new data
' build lookup from data1
With ws1
LastRow = .Cells(.Rows.Count, COL_ID).End(xlUp).row
For i = 2 To LastRow
key = Trim(.Cells(i, COL_ID))
If dict.exists(key) Then
MsgBox "Duplicate ID " & key, vbCritical, .Name & " Row " & i
Exit Sub
ElseIf Len(key) > 0 Then
dict.Add key, i
End If
Next
End With
' format report sheet
Set wbRep = Workbooks.Add(1)
Set wsRep = wbRep.Sheets(1)
wsRep.Name = "Created " & Format(Now, "YYYY-MM-DD HHMMSS")
ws1.Range("A1").Resize(, COLS).Copy wsRep.Range("A1")
wsRep.Cells(1, COLS + 1) = "Change InformationY / N"
' copare data2 new data to data1 old data
' copy diff to report
Application.ScreenUpdating = False
With ws2
LastRow = .Cells(.Rows.Count, COL_ID).End(xlUp).row
For i = 2 To LastRow
key = Trim(.Cells(i, COL_ID))
wsRep.Cells(i, COL_ID) = key
If dict.exists(key) Then
r = dict(key)
dict.Remove key ' remove
' check columns in row
bDiff = False
For c = 1 To COLS
If .Cells(i, c) <> ws1.Cells(r, c) Then
With wsRep.Cells(i, c)
.Value = ws2.Cells(i, c) & "<>" & ws1.Cells(r, c)
.Interior.Color = 255
.Font.ColorIndex = 2
.Font.Bold = True
End With
bDiff = True
End If
Next
If bDiff Then
wsRep.Cells(i, COLS + 1) = "Yes"
n = n + 1
Else
wsRep.Cells(i, COLS + 1) = "No"
End If
Else
' copy all
.Cells(i, 1).Resize(, COLS).Copy wsRep.Cells(i, 1)
wsRep.Cells(i, COLS + 1) = "Added"
n = n + 1
End If
Next
End With
' keys remaining
Dim k
With ws1
For Each k In dict.keys
r = dict(k)
.Cells(r, 1).Resize(, COLS).Copy wsRep.Cells(i, 1)
wsRep.Cells(i, COL_ID) = k
wsRep.Cells(i, COLS + 1) = "Deleted"
i = i + 1
n = n + 1
Next
End With
Application.ScreenUpdating = True
Dim s As String, yn
wsRep.Columns("A:M").AutoFit
yn = MsgBox(n & " lines differ, save report Y/N ?", vbYesNo, _
Format(Timer - t0, "0.0 secs"))
If yn = vbYes Then
s = InputBox("Enter Filename")
wbRep.SaveAs Filename:=s & ".xlsx"
End If
wbRep.Close False
End Sub
Sorry, I have overseen, that 'Report' is a workbook, not a sheet. PLease replace 'Report.Cells()' with 'Report.Worksheets("Sheet1").Cells()'
I have the following issue: In one workbook I have multiple sheets.
On Sheet 2 in column "D" starting on row 2, Is a list of 300+ prefixes of 4 digits long e.g. XFTZ, GHTU, ZAQS etc.
On Sheet 1 in column "R" starting on row 3, Is a list of 1000+ values that can have the following values e.g.: AAAA1234556 and ZAQS12565865.
The first value AAAA...... is allowed, where the second value ZAQS..... Should throw an error message when running the VBA code.
The list of values in both sheets can grow over time, so I would like to avoid hard coding of records. I would expect best solution here is to use something like this:
LastRowNr = Cells(Rows.Count, 1).End(xlUp).Row
Try something like the following, replacing Sheet1 with the name in which the actual data is located
Option Explicit
Private Sub searchPrefix()
Dim RangeInArray() As Variant
Dim LastRow1 As Long
Dim LastRow2 As Long
Dim tmpSrch As String
Dim i As Long
LastRow1 = Worksheets("Sheet1").Cells(Rows.Count, 18).End(xlUp).Row
LastRow2 = Worksheets("PREFIXES").Cells(Rows.Count, 4).End(xlUp).Row
RangeInArray = Application.Transpose(Worksheets("PREFIXES").Range("D1:D" & LastRow2).Value)
For i = 3 To LastRow1
If Len(Worksheets("Sheet1").Cells(i, 18).Value) >= 3 Then
tmpSrch = Left(Worksheets("Sheet1").Cells(i, 18).Value, 4) '18: column R
If IsInArray(tmpSrch, RangeInArray) Then
Worksheets("Sheet1").Cells(i, 18).Interior.ColorIndex = xlNone
Worksheets("Sheet1").Cells(i, 18).Font.ColorIndex = 0
Worksheets("Sheet1").Cells(i, 18).Font.Bold = False
Else
Worksheets("Sheet1").Cells(i, 18).Interior.Color = RGB(252, 134, 75)
Worksheets("Sheet1").Cells(i, 18).Font.Color = RGB(181, 24, 7)
Worksheets("Sheet1").Cells(i, 18).Font.Bold = True
End If
End If
Next
End Sub
Function IsInArray(stringToBeFound As String, arr As Variant) As Boolean
IsInArray = (UBound(Filter(arr, stringToBeFound)) > -1)
End Function
Option Explicit
Sub searchPrefix()
Sheets("PREFIXES").Select
Dim CellCntnt As String
Dim tmpSrch As String
Dim isFound As Boolean
isFound = False
Dim QtySrchChar As Integer
QtySrchChar = 4
Dim Cnt As Integer
Cnt = 0
Dim Tag As Integer
Cells.Range("A1").Select
Do Until IsEmpty(ActiveCell)
Cnt = Cnt + 1
ActiveCell.Offset(1, 0).Select
Loop
For Tag = 1 To Cnt - 1
CellCntnt = Cells(1 + i, 1).Value
tmpSrch = Left(CellCntnt, QtySrchChar)
Cells.Range("G1").Select
Do Until IsEmpty(ActiveCell)
If Left(ActiveCell.Value, QtySrchChar) = tmpSrch Then
QtySrchChar = QtySrchChar + 1
tmpSrch = Left(CellCntnt, QtySrchChar)
isFound = True
MsgBox ("True Tags introduced with Std.Prefix " & tmpSrch)
End If
If isFound Then
isFound = False
MsgBox ("False Tags introduced with Std.Prefix " & tmpSrch)
Cells.Range("G1").Select
Else
ActiveCell.Offset(1, 0).Select
End If
Loop
Next Tag
End Sub
My dataset is like this
I want to make them
Please look at the first row.
My code is
Private Sub CommandButton1_Click()
Dim MyColInstance, i As Long
Dim MyWorksheetLastColumn As Byte
MyWorksheetLastColumn = Worksheets(1).Cells(1, columns.Count).End(xlToLeft).Column
For i = 1 To MyWorksheetLastColumn
MyColInstance = ColInstance("Preference", i)
Cells(1, MyColInstance).Value = "Preference" & i
Next i
End Sub
Function ColInstance(HeadingString As String, InstanceNum As Long)
Dim ColNum As Long
On Error Resume Next
ColNum = 0
For X = 1 To InstanceNum
ColNum = (Range("A1").Offset(0, ColNum).Column) + Application.WorksheetFunction.Match(HeadingString, Range("A1").Offset(0, ColNum + 1).Resize(1, Columns.Count - (ColNum + 1)), 0)
Next
ColInstance = ColNum
End Function
The problem is while running this code, it shows an error because the for loop is not complete. What can we do?
Can you do it this way? It seems to me you are just adding a suffix to your headers in the first row...
Sub UpdateColumnHeaders()
Dim headers As Range, header As Range, suffixes As Range, suffix As Range, i As Integer
Set headers = Range(Cells(1, 1), Cells(1, Range("A1").End(xlToRight).Column))
Set suffixes = Range("A1:A" & Range("A1").End(xlDown).Row)
i = 1
For Each header In headers
If header = "Preferences" Then
header = header & suffixes(i)
i = i + 1
End If
Next
End Sub
Private Sub CommandButton1_Click()
Dim Count1, Count2 As Integer
Dim MyWorksheetLastRow As Byte
Dim MyColInstance, emp_i As Long
For Each Row_Cel In Range("1:1")
If Row_Cel.Value = "Employment" Then
Count1 = Count1 + 1
End If
If Row_Cel.Value = "Job" Then
Count2 = Count2 + 1
End If
Next Row_Cel
For emp_i = 1 To Count1
MyColInstance = ColInstance("Employment", emp_i)
Cells(1, MyColInstance).Value = "Employment" & emp_i
Next emp_i
For emp_i = 1 To Count2
MyColInstance = ColInstance("Job", emp_i)
Cells(1, MyColInstance).Value = "Job" & emp_i
Next emp_i
End Sub
Function ColInstance(HeadingString As String, InstanceNum As Long)
Dim ColNum As Long
On Error Resume Next
ColNum = 0
For X = 1 To InstanceNum
ColNum = (Range("A1").Offset(0, ColNum).Column) + Application.WorksheetFunction.Match(HeadingString, Range("A1").Offset(0, ColNum + 1).Resize(1, Columns.Count - (ColNum + 1)), 0)
Next
ColInstance = ColNum
End Function
I've been struggling with this for a few days. Any help would greatly be appreciated!
It's difficult to explain, so I'll do my best.
What I'm trying to do is count the number of results each query has and then categorize them based on that result count.
For example if Query_A has 1 exact result and then Query_Z has 1 exact result then that would be a total of 2 queries that have 1 result.
I'm currently trying to use Loop with if then statements, but I'm at a loss.
Here is some example data and the output I was hoping for: Query_Example_Data_and_Results.xlsx - This is not my real spreadsheet as it is thousands of rows of data and a very large file size.
The code below does pull the query count (removing the query dupes), but does not give the query result count.. I would have provide my code attempts, but I know I'm not even close... So I have removed my failed attempts hoping I'm being clear enough to get steered in the right direction.
Sub Query_Count()
G_40 = 0
Query = ""
Application.StatusBar = " ~~ ~~ QUERY COUNT ~~ RUNNING ~~ ~~ " & x
x = 2
Do Until Sheets(1).Cells(x, 1) = ""
If Sheets(1).Cells(x, 9) = "Yes" Then
If Query <> Sheets(1).Cells(x, 1) Then
G_40 = G_40 + 1
End If
End If
Query = Sheets(1).Cells(x, 1)
x = x + 1
Loop
Application.StatusBar = "DONE RUNNING QUERY COUNT OF " & x & " ROWS!"
G = 40
Sheets(3).Cells(G, 7) = G_40 'query_count:
End Sub
Thank you in advance!
Based on your Example this code will do the job:
Option Explicit
Sub getResults()
Application.ScreenUpdating = False
Dim ws1 As Worksheet, ws2 As Worksheet, lr&
Set ws1 = ThisWorkbook.Sheets("Example_Query_Data")
Set ws2 = ThisWorkbook.Sheets("Example_Results")
lr = ws1.Range("A" & Rows.count).End(xlUp).Row
Dim arr() As String, i&, j&, cnt&
Dim varr() As String
cnt = 0
ReDim arr(lr - 2)
For i = 2 To lr
arr(i - 2) = CStr(ws1.Range("A" & i).Value) ' fill array
Next i
Call RemoveDuplicate(arr) 'remove duplicate
ReDim varr(0 To UBound(arr), 0 To 1)
For i = LBound(arr) To UBound(arr)
varr(i, 0) = arr(i)
varr(i, 1) = getCount(arr(i), ws1, j, lr)
Next i
Call PrepTable(ws2)
Call UpdateTable(ws2, ws1, varr, j, lr) ' Update table
Application.ScreenUpdating = True
End Sub
Function getCount(qName$, ByRef ws1 As Worksheet, ByRef i&, lr&)
Dim count&
count = 0
For i = 2 To lr
If (StrComp(CStr(ws1.Range("A" & i).Value), qName, vbTextCompare) = 0) And _
(StrComp(CStr(ws1.Range("C" & i).Value), "Yes", vbTextCompare) = 0) Then count = count + 1
Next i
getCount = count ' return count
End Function
Sub UpdateTable(ByRef ws As Worksheet, ByRef ws2, ByRef arr() As String, ByRef i&, lr&)
Dim tblIter&
For tblIter = 2 To 12
For i = LBound(arr) To UBound(arr)
If arr(i, 1) = tblIter - 1 Then
ws.Range("B" & tblIter).Value = ws.Range("B" & tblIter).Value + 1
End If
Next i
Next tblIter
Call ElevenAndMore(ws, ws2, arr, lr, i)
End Sub
Sub PrepTable(ws As Worksheet)
ws.Range("B2:B12").ClearContents
End Sub
Sub ElevenAndMore(ByRef ws As Worksheet, ByRef ws2, ByRef arr() As String, lr&, ByRef i)
Dim cnt&, j&
cnt = 0
For i = LBound(arr) To UBound(arr)
For j = 1 To lr
If StrComp(CStr(ws2.Range("A" & j).Value), arr(i, 0), vbTextCompare) = 0 Then
cnt = cnt + 1
End If
Next j
If cnt > 10 Then ws.Range("B12").Value = ws.Range("B12").Value + 1
cnt = 0
Next i
End Sub
Sub RemoveDuplicate(ByRef StringArray() As String)
Dim lowBound$, UpBound&, A&, B&, cur&, tempArray() As String
If (Not StringArray) = True Then Exit Sub ' is empty?
lowBound = LBound(StringArray)
UpBound = UBound(StringArray)
ReDim tempArray(lowBound To UpBound)
cur = lowBound ' first item
tempArray(cur) = StringArray(lowBound)
For A = lowBound + 1 To UpBound
For B = lowBound To cur
If LenB(tempArray(B)) = LenB(StringArray(A)) Then
If InStrB(1, StringArray(A), tempArray(B), vbBinaryCompare) = 1 Then Exit For
End If
Next B
If B > cur Then cur = B: tempArray(cur) = StringArray(A)
Next A
ReDim Preserve tempArray(lowBound To cur) ' reSize
StringArray = tempArray ' copy
End Sub
Post-Comment Edit:
Change these three:
Add +28 to the tblIter
Sub UpdateTable(ByRef ws As Worksheet, ByRef ws2, ByRef arr() As String, ByRef i&, lr&)
Dim tblIter&
For tblIter = 2 To 12
For i = LBound(arr) To UBound(arr)
If arr(i, 1) = tblIter - 1 Then
ws.Range("B" & tblIter + 28).Value = ws.Range("B" & tblIter + 28).Value + 1
End If
Next i
Next tblIter
Call ElevenAndMore(ws, ws2, arr, lr, i)
End Sub
Simply change location to B40
Sub ElevenAndMore(ByRef ws As Worksheet, ByRef ws2, ByRef arr() As String, lr&, ByRef i)
Dim cnt&, j&
cnt = 0
For i = LBound(arr) To UBound(arr)
For j = 1 To lr
If StrComp(CStr(ws2.Range("A" & j).Value), arr(i, 0), vbTextCompare) = 0 Then
cnt = cnt + 1
End If
Next j
If cnt > 10 Then ws.Range("B40").Value = ws.Range("B40").Value + 1
cnt = 0
Next i
End Sub
And prep table change range
Sub PrepTable(ws As Worksheet)
ws.Range("B30:B40").ClearContents
End Sub
and this should do!
I'm getting a type mismatch error while comparing a range value to "" or vbNullString. i read many similar q+a posts that deal with this issue.
The data is all numbers or "".
Sub vegetableCounting()
Dim ws1 As Worksheet, ws2 As Worksheet, ws3 As Worksheet
Dim ws1Range As Excel.range, ws2Range As Excel.range, ws3Range As Excel.range, ws2Loop As Excel.range
Dim ws1Row As Long, ws1Col As Long, ws2Row As Long, ws2Col As Long
'
Dim rowCounter As Long, colCounter As Long, rowsMendo As Long
Dim mendoSum As Double
'
Set ws1 = Sheets("shareSchedule")
Set ws2 = Sheets("shareDistribution")
Set ws3 = Sheets("vegCount")
'***not yet set to the full ranges***
Set ws1Range = ws1.range("E7:H11") 'shareSchedule
Set ws2Range = ws2.range("D7:BB17") 'shareDistribution
Set ws3Range = ws3.range("D7:BB11") 'vegetableCount
'***not yet set to the full ranges***
rowsMendo = 0
rowCounter = 0
colCounter = 0
mendoSum = 0
For ws1Row = 0 To ws1Range.Rows.count Step 1
For ws1Col = 0 To ws1Range.Columns.count Step 1
If ws1Range.Offset(ws1Row, ws1Col).value <> "" Then
For Each ws2Loop In ws2Range '11rows*51cols = 561
ws2Row = ws2Row + rowCounter + rowsMendo
ws2Col = ws2Col + colCounter
If ws2Range.Offset(ws2Row, ws2Col).value = "" Then
Exit For
Else
If ws1Range.Offset(ws1Row, ws1Col).Interior.ColorIndex = 24 And _
ws2Range.Offset(ws2Row, ws2Col).Interior.ColorIndex = 24 Then 'a MENDO match
If rowCounter < 3 Then
mendoSum = mendoSum + ws1Range.Offset(ws1Row, ws1Col).value * ws2Range.Offset(ws2Col, ws2Row)
rowCounter = rowCounter + 1
ElseIf rowCounter = 3 Then
colCounter = colCounter + 1
rowCounter = 0
ElseIf colCounter = ws2Range.Columns.count + 1 And _
ws2Range.Offset(ws2Row, 1).Interior.ColorIndex = 24 And _
ws2Range.Offset(ws2Row + 4, 1).Interior.ColorIndex = 24 Then
colCounter = 0
rowsMendo = rowsMendo + 3
ElseIf colCounter = ws2Range.Columns.count + 1 And _
ws2Range.Offset(ws2Row, 1).Interior.ColorIndex = xlNone And _
ws2Range.Offset(ws2Row + 4, 1).Interior.ColorIndex = xlNone Then
colCounter = 0
rowsMendo = rowsMendo + 1
End If
ws3Range.Offset(ws1Row, ws2Col) = ws1Range.Offset(ws1Row, ws1Col).value * ws2Range.Offset(ws2Row, ws2Col).value
End If
End If
Next
End If
Next ws1Col
Next ws1Row
'for ws2
'Offset(0, 0), Offset(1, 0), Offset(2, 0), then
'Offset(0, 1), Offset(1, 1), Offset(2, 1), then
'Offset(0, 2), Offset(1, 2), Offset(2, 2), then
'etc
End Sub
i get the error on
If ws1Range.Offset(ws1Row, ws1Col).value <> "" Then
and ill prob get it again on
If ws2Range.Offset(ws2Row, ws2Col).value = "" Then
any thoughts? here are some images of the worksheets im trying to pull from
You could try CStr to convert the value to a String. Format could also be used as it handles Null whereas CStr would produce an error.
So either:
If CStr(ws1Range.Offset(ws1Row, ws1Col).value) <> "" Then
or
If Format(ws1Range.Offset(ws1Row, ws1Col).value) <> "" Then
I don't usually use the Offset function, but you can access the cells in a specified range by directly specifiying the row and column like an array.
EG: ws2Range(ws2Row, ws2Col).value
You have to start in 1 for your iterations though, you'll get an error when you start at 0.
when looking at the offset of a range, you get the entire range area, offset by your offset values.
e.g.
set a=sheets(1).range("A1:F40")
debug.print a.offset(1,1).address
giveas a result of
$B$2:$G$41
notice this is (A+1,1+1:F+1,40+1) and not a single cell
there are 2 options:
set your range to a single cell, and use offset to look at the area around that cell
use the range you have now, and use cells(x,y) to look in that range