Populate dynamic columns - excel

I have a VBA script that currently matches Household IDs in two different worksheets (Children and Adults). If there is a match, the Adults worksheet is populated with the child's date of birth (DOB).
However, the adult can have multiple children and I need the all children's DOBs from the same household on separate consecutive columns in the adult's sheet depending on the number of children (Child DOB1, Child DOB2, etc.).
The VBA needs to be dynamic with no hard-coded column references since column locations can change. However, the column names (ex., Household ID) will always be the same.
It's also possible for more than one adult to be part of a household and I need each adult to have the same list of children DOBs.
Any suggestions would be much appreciated. I am limited in my VBA knowledge so any explanations or comments are helpful. Thank you!
Dim shtA As Worksheet
Dim shtC As Worksheet
Set shtA = ActiveWorkbook.Sheets("Adults")
Set shtC = ActiveWorkbook.Sheets("Children")
'Loop through heading row and get column number of "Household ID" column in "Adults" worksheet
'which will be used to match "Household ID" in the "Children" worksheet
Dim lastCol1 As Long
lastCol1 = shtA.Cells(1, Columns.Count).End(xlToLeft).Column
Dim hid1 As Long
Dim aa As Long
For aa = 1 To lastCol1
If LCase(shtA.Cells(1, aa).Value) = "household id" Then
hid1 = aa
Exit For
End If
Next aa
Dim lastCol As Long
lastCol = shtC.Cells(1, Columns.Count).End(xlToLeft).Column
Dim hid As Long
Dim dob As Long
Dim mm As Long
For mm = 1 To lastCol
If LCase(shtC.Cells(1, mm).Value) = "household id" Then
hid = mm
ElseIf LCase(shtC.Cells(1, mm).Value) = "dob" Then
dob = mm
End If
Next mm
'Begin populate new cols for Adults worksheet
Dim lastSRow As Long
Dim lastDRow As Long
Dim z As Long
Dim zz As Long
lastSRow = shtC.Cells(Rows.Count, 1).End(xlUp).Row 'get last row of source sheet
lastDRow = shtA.Cells(Rows.Count, 1).End(xlUp).Row 'get last row of destination sheet
'Would like to have all children in a household on separate columns in the "Adults" sheet
'Currently, only one child's DOB appears in one column named "Child DOB1"
'but I'd like subsequent columns, "Child DOB2", "Child DOB3", etc.
For z = 2 To lastDRow
For zz = 2 To lastSRow
If shtA.Cells(z, hid1).Value = shtC.Cells(zz, hid).Value Then
shtA.Cells(z, lastCol1 + 1).Value = shtC.Cells(zz, dob).Value
End If
Next zz
Next z
'add heading
shtA.Cells(1, lastCol1 + 1).Value = "Child DOB1"

You are missing a dynamic counter in your last netsted loops. Please try this code. I have taken the liberty and abstracted out getting column numbers in a function (one of the functions I almost always have in my applications).
Please note for this code to work, you have to add by hand "Child DOB1" in your Adults sheet.
Please also note how I saved the headings in a variant before looping: This helps the performance of the function. You can do something similar the rest of the code if you have large data.
Sub PopulateDOBs()
Dim shtA As Worksheet
Dim shtC As Worksheet
Set shtA = ActiveWorkbook.Sheets("Adults")
Set shtC = ActiveWorkbook.Sheets("Children")
Dim hid1 As Long
hid1 = GetColNo("household id", "Adults", 1)
Dim hid As Long
Dim dob As Long
hid = GetColNo("household id", "Children", 1)
dob = GetColNo("dob", "Children", 1)
'Begin populate new cols for Adults worksheet
Dim lastSRow As Long
Dim lastDRow As Long
Dim z As Long
Dim zz As Long
lastSRow = shtC.Cells(Rows.Count, 1).End(xlUp).Row 'get last row of source sheet
lastDRow = shtA.Cells(Rows.Count, 1).End(xlUp).Row 'get last row of destination sheet
Dim dob1Col As Long
Dim j As Long ' the missing counter I mentioned
dob1Col = GetColNo("Child DOB1", "Adults", 1)
For z = 2 To lastDRow
j = -1
For zz = 2 To lastSRow
If shtA.Cells(z, hid1).Value = shtC.Cells(zz, hid).Value Then
j = j + 1
shtA.Cells(z, dob1Col + j).Value = shtC.Cells(zz, dob).Value
'Add heading if missing
If shtA.Cells(1, dob1Col + j).Value <> "Child DOB" & (j + 1) Then
shtA.Cells(1, dob1Col + j).Value = "Child DOB" & (j + 1)
End If
End If
Next zz
Next z
End Sub
Function GetColNo(sHeading As String, sSheetName As String, lHeadingsRow As Long) As Long
Dim vHeadings As Variant
Dim lLastCol As Long
Dim j As Long
With ThisWorkbook.Sheets(sSheetName)
lLastCol = .Cells(lHeadingsRow, Columns.Count).End(xlToLeft).Column
vHeadings = .Range(.Cells(lHeadingsRow, 1), .Cells(lHeadingsRow, lLastCol)).Value
GetColNo = 0
For j = 1 To lLastCol
If LCase(vHeadings(1, j)) = LCase(sHeading) Then
GetColNo = j
Exit Function
End If
Next j
End With
End Function

Try this code using FIND rather than looking at each row/column. It also assumes that there's no Adult Household DOB columns present when starting.
Public Sub Test()
Dim Adult As Worksheet
Dim Children As Worksheet
Set Adult = ThisWorkbook.Worksheets("Adults")
Set Children = ThisWorkbook.Worksheets("Children")
'Find Household ID in Adult sheet.
With Adult.Rows(1)
Dim AdultHouseholdID As Range
Set AdultHouseholdID = .Find(What:="household id", After:=.Cells(1), LookIn:=xlValues, _
LookAt:=xlWhole, SearchDirection:=xlNext, MatchCase:=False)
If AdultHouseholdID Is Nothing Then Exit Sub
'Find the last column in Adult sheet.
'This doesn't check if there's already DOB columns in the sheet.
Dim AdultLastColumn As Range
Set AdultLastColumn = .Cells(1, .Cells.Count).End(xlToLeft)
End With
With Children.Rows(1)
'Find Household ID in Children sheet.
Dim ChildHouseholdID As Range
Set ChildHouseholdID = .Find(What:="household id", After:=.Cells(1), LookIn:=xlValues, _
LookAt:=xlWhole, SearchDirection:=xlNext, MatchCase:=False)
If ChildHouseholdID Is Nothing Then Exit Sub
'Find DOB column in Children sheet.
Dim ChildDOBColumn As Range
Set ChildDOBColumn = .Find(What:="DOB", After:=.Cells(1), LookIn:=xlValues, _
LookAt:=xlWhole, SearchDirection:=xlNext, MatchCase:=False)
If ChildDOBColumn Is Nothing Then Exit Sub
End With
'Get the range of Adult Household IDs. The code will check each ID.
Dim AdultHouseHolds As Range
With Adult
Set AdultHouseHolds = .Range(AdultHouseholdID.Offset(1), .Cells(.Rows.Count, AdultHouseholdID.Column).End(xlUp))
End With
Dim HouseHold As Range
Dim FirstAddress As String
Dim DOBOffset As Long
Dim ChildDOB As Range
'Look at each Adult Household in turn.
For Each HouseHold In AdultHouseHolds
With Children.Columns(ChildHouseholdID.Column)
'Find the first DOB with corresponding Household ID.
Set ChildDOB = .Find(What:=HouseHold.Value, After:=.Cells(1), LookIn:=xlValues, _
LookAt:=xlWhole, SearchDirection:=xlNext)
If Not ChildDOB Is Nothing Then
'Remember the address - need to check for when FIND loops back around.
FirstAddress = ChildDOB.Address
DOBOffset = 1
Do
'Place the header - the Offset is reset for each Household ID.
Adult.Cells(1, AdultLastColumn.Column + DOBOffset) = "DOB" & DOBOffset
'Copy the Child DOB to the Adult sheet.
Children.Cells(ChildDOB.Row, ChildDOBColumn.Column).Copy _
Destination:=Adult.Cells(HouseHold.Row, AdultLastColumn.Column + DOBOffset)
DOBOffset = DOBOffset + 1
'Find the next value.
Set ChildDOB = .FindNext(ChildDOB)
Loop While ChildDOB.Address <> FirstAddress 'Keep going til it gets back to the first address.
End If
End With
Next HouseHold
End Sub

You're on the right lines.
What you really want your code to do is like this:
For each Child row (search by ID)
Find Matching Adult ID/s (by row)
Add that Child's DOB to the end of the relevant row.
(NB that I'm assuming the DOBs get put at the end of the row, rather than you inserting a dynamic amount of columns in the middle.)
Anyway, in code that would translate roughly to;
Dim LastCol As Integer, AdIDCol As Integer, ChIDcol As Integer, ChDOBCol as Integer
Dim shtA As Worksheet, shtC As Worksheet
Set shtA = ActiveWorkbook.Sheets("Adults")
Set shtC = ActiveWorkbook.Sheets("Children")
LastCol = ShtA.UsedRange.Columns.Count 'Children's DOBs will be put after this column.
'Identify relevant Columns in sheets - checking both sheets in one loop.
For a = 1 to Worksheetfunction.Max(LastCol, shtC.UsedRange.Columns.Count) 'This ensures that all of both sheets will be checked
If LCase(shtA.Cells(1,a).Value) = "household id" Then
AdIDCol = a
End If
If LCase(shtC.Cells(1,a).Value) = "household id" Then
ChIDCol = a
ElseIf LCase(shtC.Cells(1,a).Value) = "dob" Then
ChDOBCol = a
End If
Next a
'Now some nested loops to match children with adults
Dim AdultsFound as Integer 'this will be handy to speed up the loop
'First loop checks through children
For x = 2 to ShtC.UsedRange.Rows.Count
'Second loop checks through Adults
For y = 2 to ShtA.UsedRange.Rows.Count
If ShtC.Cells(x, ChIDCol).Value = ShtA.Cells(y, AdIDCol) Then
AdultsFound = AdultsFound + 1
'Third Loop checks to find what empty cell to copy the DOB into
z = Lastcol
Do While ShtA.Cells(y, z) <> ""
z = z+1 'moves to next column along
Loop
'Once found an empty cell in that row, copy the DOB to it
ShtC.Range(Cells(x, ChDOBCol), Cells(x, ChDOBCol)).Copy ShtA.Range(Cells(y,z), Cells(y,z))
End If
'If there are no more relevant adults in the sheet then stop searching for any more...
If AdultsFound = WorksheetFunction.Countif(ShtA.Cells(1, AdIDCol).EntireColumn, shtC.Cells(x, ChIDCol)) Then Exit For
Next y
Next x
Hope that helps?

Change the last lines of your code to something like this: (untested, but it should give you the idea)
Dim maxDOBColOffset As Long
For z = 2 To lastDRow
Dim DOBColOffset As Long
DOBColOffset = 1
For zz = 2 To lastSRow
If shtA.Cells(z, hid1).Value = shtC.Cells(zz, hid).Value Then
shtA.Cells(z, lastCol1 + DOBColOffset).Value = shtC.Cells(zz, dob).Value
DOBColOffset = DOBColOffset + 1
If maxDOBColOffset < DOBColOffsetThen
shtA.Cells(1, lastCol1 + DOBColOffset).Value = "Child DOB" & DOBColOffset
maxDOBColOffset = DOBColOffsetThen
End If
End If
Next zz
Next z

Related

Find All Matches of Cell Data Based on Cell Value and Iterate Down Rows

How can I make this code find all occurrences of the cell value? Right now it iterates and then pastes the same row (first time it appears), it's not moving past that row to find the remaining rows that match the row. Sheet A has the part appear more than once. Any help would be appreciated! Thanks!
Sub Update_Data()
Dim d As Worksheet: Set d = ThisWorkbook.Worksheets("Sheet D")
Dim a As Worksheet: Set a = ThisWorkbook.Worksheets("Sheet A")
' **IMPORTANT** header row locations
Dim d_headerRow As Integer: d_headerRow = 1
Dim a_headerRow As Integer: a_headerRow = 1
Dim i As Long, j As Long, k As Integer, part As String
Dim d_lastRow As Long: d_lastRow = d.Cells(d.Rows.Count, 1).End(xlUp).Row
Dim a_lastRow As Long: a_lastRow = a.Cells(a.Rows.Count, 1).End(xlUp).Row
Dim a_lastCol As Integer: a_lastCol = a.Cells(a_headerRow, a.Columns.Count).End(xlToLeft).Column
For i = d_headerRow + 1 To d_lastRow
part = d.Cells(i, 1).Value
For j = a_headerRow + 1 To a_lastRow
If part = a.Cells(j, 1).Value Then
a.Range(a.Cells(j, 1), a.Cells(j, a_lastCol)).Copy Destination:=d.Range(d.Cells(i, 11), d.Cells(i, 11))
Exit For
End If
Next j
Next i
End Sub

Advanced INDEX-MATCH

In my workbook I have 2 worksheets: Sheet1 and Sheet2.
In Sheet1 I have the following data set:
In Sheet2 I have the following data set:
I need to create a code that will do the following:
Populate the score columns ("Score of SpeGro", "Score of PrimSpe", etc.)
For example, for the "Score of SpeGro" column it needs to:
Search in Sheet1 the column header corresponding to SpeGro (in this case it's column 4);
The values of column 4 need to match the values in column 3 of Sheet2.
Only consider the values in Sheet2 with the DIMENSION "SpeGro" (in this case);
Only consider the values with PrdInd (Sheet1) = PrdInd (Sheet2).
Extra info: I have a INDEX-MATCH formula that works if I only had DIMENSION:
For k = 2 To RowNum
tWb.Sheets("Sheet1").Cells(k, 6).Value = Application.IfError(Application.Index(tWb.Sheets("Sheet2").Range("D:D"), Application.Match(tWb.Sheets("Sheet1").Cells(k, 4), tWb.Sheets("Sheet2").Range("C:C"), 0)), 0)
Next k
Any idea on how I can achieve this?
This is just to give you an idea on how you can tackle this task. But the code is working if you want to try it.
'task: Populate the score columns ("Score of SpeGro", "Score of PrimSpe", etc.)
'if conditions are met
Sub Whatever()
Dim strSearch As String
Dim aCell As Range
Dim col_n As Integer
Dim last_row As Long
Dim first_row As Byte
Dim Count As Long
'Search in Sheet1 the column header corresponding to
'"Score of SpeGro" (in this case it's column 6)
'CONFIG
'-------------
strSearch = "Score of SpeGro"
first_row = 2 'first row of the data sets in sheet 1 and 2
'-------------
Set aCell = Sheet1.Rows(1).Find(What:=strSearch, LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
col_n = aCell.Column
'column numbers sheet1
'Scoreof PrimSpe column numner = col_n +1
'SpeGro column number = col_n - 2
'Prdlnd column number = col_n - 5
last_row = Sheets("Sheet1").Cells(Rows.Count, col_n - 5).End(xlUp).Row
For Count = first_row To last_row
If Sheets("Sheet1").Cells(Count, col_n - 2) = Sheets("Sheet2").Cells(Count, 3) _
And Sheets("Sheet2").Cells(Count, 2) = "SpeGro" _
And Sheets("Sheet1").Cells(Count, col_n - 5) = Sheets("Sheet2").Cells(Count, 1) Then
Sheets("Sheet1").Cells(Count, col_n) = "Put something here"
End If
Next Count
End Sub
Option Explicit
Sub Button1_Click()
'task: Populate the score columns ("Score of SpeGro", "Score of PrimSpe", etc.)
'if conditions are met
Dim strSearch1 As String, strSearch2 As String
Dim aCell1 As Range, aCell2 As Range
Dim col_n1 As Integer, col_n2 As Integer
Dim last_row1 As Long, last_row2 As Long
Dim first_row As Byte
Dim Count As Long
Dim myArray As Variant, element As Variant
'Search in Sheet1 the column header corresponding to
'"Score of SpeGro" (in this case it's column 6)
myArray = Array("Specialty Grouping", "Primary Specialty")
'strSearch1 = "Score of Specialty Grouping"
'strSearch2 = "Specialty Grouping"
For Each element In myArray
Set aCell1 = Sheet1.Rows(1).Find(What:="Score of " & element)
Set aCell2 = Sheet1.Rows(1).Find(What:=element)
col_n1 = aCell1.Column
col_n2 = aCell2.Column
'column numbers sheet1
'SpeGro column number = col_n2
last_row1 = Sheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row
last_row2 = Sheets("Sheet2").Cells(Rows.Count, 1).End(xlUp).Row
'The values of col_n2 need to match the values in column 3 of USER_INPUTS.
For Count = 2 To last_row1
Sheets("Sheet1").Cells(Count, col_n1) = Application.Index(ThisWorkbook.Sheets("Sheet2").Range("D2:D" & last_row2), _
Application.Match(ThisWorkbook.Sheets("Sheet1").Cells(Count, 1), ThisWorkbook.Sheets("Sheet2").Range("A2:A" & last_row2), 0) * _
Application.Match(ThisWorkbook.Sheets("Sheet1").Cells(1, col_n2), ThisWorkbook.Sheets("Sheet2").Range("B2:B" & last_row2), 0) * _
WorksheetFunction.IfError(Application.Match(ThisWorkbook.Sheets("Sheet1").Cells(Count, col_n2), ThisWorkbook.Sheets("Sheet2").Range("C2:C" & last_row2), 0), 0))
Next Count
Next element
End Sub

Find a cells value (text) based on two criteria

I've spent the majority of my afternoon looking for a way to return a text value in a cell based on two columns. I'm looking to match a values from Sheet1, columns A & F to sheet2, returning the value in column B where these two match into sheet 1.
To visualize:
Sheet 1 Sheet 2
A F A B F
x b x c y
x g x k b
Is there a way to use VLOOKUP to do this that I missed? I'm pretty confident that I'm missing something simple, but it's giving me a hard time.
Thanks in advance!
The following Subscript does exactly what you asked:
Sub DoThaThing()
Dim i As Long, lastRow1 As Long
Dim Sheet1A As Variant, Sheet1F As Variant, firstFound As String
Dim findData As Range
lastRow1 = Sheets("Sheet1").Cells(Rows.Count, "A").End(xlUp).Row
For i = 1 To lastRow1 Step 1
Sheet1A = Sheets("Sheet1").Cells(i, "A").Value
Sheet1F = Sheets("Sheet1").Cells(i, "F").Value
Set findData = Sheets("Sheet2").Columns("A:A").Find(What:=Sheet1A, _
After:=Sheets("Sheet2").Range("A1"), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False)
If Not findData Is Nothing Then
'First instance found, loop if needed
firstFound = findData.Address
Do
'Found, check Column F (5 columns over with offset)
If findData.Offset(0, 5).Value = Sheet1F Then
'A and F match get data from B (1 column over with offset)
Sheets("Sheet1").Cells(i, "B").Value = findData.Offset(0, 1).Value
Exit Do
Else
'F doesnt match, search next and recheck
Set findData = Sheets("Sheet2").Columns("A:A").FindNext(findData)
End If
Loop While Not findData Is Nothing And firstFound <> findData.Address
Else
'Value on Sheet 1 Column A was not found on Sheet 2 Column A
Sheets("Sheet1").Cells(i, "B").Value = "NOT FOUND"
End If
Next
End Sub
Edit: Infinite Loop Fixed.
try this code, it's work for me :
Option Explicit
Sub test()
' Active workbook
Dim wb As Workbook
Set wb = ThisWorkbook
Dim i As Long
Dim j As Long
'*******************************************
'Adapt this vars
'define your sheets
Dim ws_1 As Worksheet
Dim ws_2 As Worksheet
Set ws_1 = wb.Sheets("Feuil1") 'change name of the sheet to complete
Set ws_2 = wb.Sheets("Feuil2") 'change name of the sheet with all data
'definie the last Rows
Dim lastRow_ws1 As Long
Dim lastRow_ws2 As Long
lastRow_ws1 = ws_1.Range("A" & Rows.Count).End(xlUp).Row + 1 'if you need, adjust column to find last row
lastRow_ws2 = ws_2.Range("A" & Rows.Count).End(xlUp).Row + 1 'if you need, adjust column to find last row
'*******************************************
Dim keyMach1 As String
Dim keyMach2 As String
For j = 1 To lastRow_ws1
For i = 1 To lastRow_ws2
Dim keySearch As String
Dim keyFind As String
keySearch = ws_1.Cells(j, 1).Value & ws_1.Cells(j, 6).Value 'I concat both cell to create o key for the search
keyFind = ws_2.Cells(i, 1).Value & ws_1.Cells(i, 6).Value ' idem to match
If keySearch = keyFind Then
ws_1.Cells(j, 2).Value = ws_2.Cells(i, 2).Value
End If
Next i
Next j
End Sub

Continue assigning persons list to the next sheet

I have a excel file with 5 sheets:
Main Sheet
PRP Sharepoint
SAP
FO
BO
In the Main sheet, I have a list of persons starting from F12 cell:
What the code below do is to copy these persons to column "A" of other sheets depending on the number of rows in their "B" column.
What I wanted it to do:
After assigning of persons for each item in "PRP sharepoint" sheet, it will then proceed to the next sheet which is "SAP", provided the sequence of the assignee going to the next sheet should start depending on who is the next person after the last one on the previous sheet, instead of restarting from the first person once again.
I want to start loop from "PRP Sharepoint" sheet up to "BO" sheet while maintaining the sequence of persons.
Sub Assign()
Dim WS As Worksheet
Dim LastRow As Long
Dim Main As Worksheet
Set Main = Sheets("Main")
Dim SrchRng As Range, cel As Range
For Each WS In Worksheets
LastRow = WS.Range("B" & WS.Rows.Count).End(xlUp).Row
ALR = Main.Range(Main.Range("F12"), Main.Range("F" & Main.Rows.Count).End(xlUp)).Count
For x = 2 To LastRow
For A = 2 To ALR + 1
If x = LastRow + 1 Then GoTo z
WS.Cells(x, 1).value = Main.Cells(A + 10, 6).value
x = x + 1
Next A
x = x - 1
Next x
z:
'Do nothing
Next WS
MsgBox "Done"
End Sub
How can I alter the code that can meet what I need?
UPDATE 2:
In the Main sheet, I have added a new column G which contains CWID (ID for each person).
The code in the answer works perfectly but since I have added a new column in main sheet, now I have to adjust the code to work with the "SAP" Sheet
Question:
How would I adjust the code to meet the requirement below
In "SAP" there is a column (Created by) which also contains CWID same with the new column G in main sheet. Whenever the CWID in main sheet is the same with the row in "SAP" sheet, it will not assign that processor to that assignment while still continuing the sequence of assigning.
So if it matches, it will skip it while it fills the row.
Basically, by basing on the (Created by) column in "SAP" Sheet, the person should not be assigned to the item created by him/her.
I don't have access to pc right now, i'll try to give probable changes you need for your new request about "Created by" column in SAP
Sub Assign()
Dim Main As Worksheet, WS As Worksheet
Dim PersonFirstRow As Long, PersonLastRow As Long, PersonRow As Long
Dim WSLastRow As Long, r As Long
Dim iWorksheet As Integer
Set Main = Sheets("Main")
PersonFirstRow = 12 'row of F12
PersonLastRow = Main.Cells(Main.Rows.Count, "F").End(xlUp).Row
PersonRow = PersonFirstRow 'current row in Main sheet
Dim SN As String, sameCWID As Boolean
For iWorksheet = 2 To Worksheets.Count 'start from sheet2
Set WS = Worksheets(iWorksheet)
SN = WS.Name
WSLastRow = WS.Range("B" & WS.Rows.Count).End(xlUp).Row 'last row in column B of this sheet
For r = 2 To WSLastRow 'loop through rows of this sheet
If Trim(WS.Cells(r, "A").Value) = "" Then 'skip this cell if it has already some value
sameCWID = False
If SN="SAP" Then If WS.Cells(r,"D").Value = Main.Cells(PersonRow,"G") Then sameCWID=True
If sameCWID Then 'dont choose this person
r = r-1 ' stay at this row to set by next person
Else
WS.Cells(r, "A").Value = Main.Cells(PersonRow, "F") 'F12, F13, ...
End If
PersonRow = PersonRow + 1
If PersonRow > PersonLastRow Then PersonRow = PersonFirstRow 'reset back to first row when we reach the last person
End If
Next r
Next iWorksheet
MsgBox "Done"
End Sub
Update 2: (skip sheet cells if they has already some value in column "A")
As i understand, you want to continue person names when you go from every sheet to next while filling their column "A". So, you should not use two For-Loops for persons and sheet rows. but you have to use a procedure level variable and reset it to first row only when you reach the last person (not when you switch to next sheet).
Here is the working code:
Sub Assign()
Dim Main As Worksheet, WS As Worksheet
Dim PersonFirstRow As Long, PersonLastRow As Long, PersonRow As Long
Dim WSLastRow As Long, r As Long
Dim iWorksheet As Integer
Set Main = Sheets("Main")
PersonFirstRow = 12 'row of F12
PersonLastRow = Main.Cells(Main.Rows.Count, "F").End(xlUp).Row
PersonRow = PersonFirstRow 'current row in Main sheet
For iWorksheet = 2 To Worksheets.Count 'start from second sheet (first sheet is Main)
Set WS = Worksheets(iWorksheet)
'WS.Range("A2", "A" & WS.Rows.Count).Clear 'if you want to clear column "A" before fill (if there are more items in it)
WSLastRow = WS.Range("B" & WS.Rows.Count).End(xlUp).Row 'last row in column B of this sheet
For r = 2 To WSLastRow 'loop through rows of this sheet
If Trim(WS.Cells(r, "A").Value) = "" Then 'skip this cell if it has already some value
WS.Cells(r, "A").Value = Main.Cells(PersonRow, "F") 'F12, F13, ...
PersonRow = PersonRow + 1
If PersonRow > PersonLastRow Then PersonRow = PersonFirstRow 'reset back to first row when we reach the last person
End If
Next r
Next iWorksheet
MsgBox "Done"
End Sub

VBA for duplicate rows

I have a sheet of columns.
I want to compare data in multiple columns, and return a flag in another column to indicate rows that are duplicates. I found a little code online which was meant for checking one column of data, and have so far been unsuccessful in being able to tweek it for multiple columns. The final code will need to look at specific columns which I will define later however for the moment say the sheet is as follows:
StaffNumber CallType
1 A
2 B
1 A
4 D
5 E
6 F
7 G
8 H
1 A
2 C
1 Z
6 P
The Col A is labelled Staff Number. Col B is labelled CallType. In Col C I want the flag to be entered against the row.
My Code is as follows:
Sub DuplicateIssue()
Dim last_StaffNumber As Long
Dim last_CallType As Long
Dim match_StaffNumber As Long
Dim match_CallType As Long
Dim StaffNumber As Long
Dim CallType As Long
last_StaffNumber = Range("A65000").End(xlUp).Row
last_CallType = Range("B65000").End(xlUp).Row
For StaffNumber = 1 To last_StaffNumber
For CallType = 1 To last_CallType
'checking if the Staff Number cell is having any item, skipping if it is blank.
If Cells(StaffNumber, 1) <> " " Then
'getting match index number for the value of the cell
match_StaffNumber = WorksheetFunction.Match(Cells(StaffNumber, 1), Range("A1:A" & last_StaffNumber), 0)
If Cells(CallType, 2) <> " " Then
match_CallType = WorksheetFunction.Match(Cells(CallType, 2), Range("B1:B" & last_CallType), 0)
'if the match index is not equals to current row number, then it is a duplicate value
If StaffNumber <> match_StaffNumber And CallType <> match_CallType Then
'Printing the label in the column C
Cells(StaffNumber, 3) = "Duplicate"
End If
End If
End If
Next
Next
End Sub
My problem is that only when Col 1 is a duplicate will the macro enter "Duplicate" into Col C, and it is not checking if the value of Col B is also the same.
Any Help would be much appreciated.
Try this code:
.
Option Explicit
Public Sub showDuplicateRows()
Const SHEET_NAME As String = "Sheet1"
Const LAST_COL As Long = 3 ' <<<<<<<<<<<<<<<<<< Update last column
Const FIRST_ROW As Long = 2
Const FIRST_COL As Long = 1
Const DUPE As String = "Duplicate"
Const CASE_SENSITIVE As Byte = 1 'Matches UPPER & lower
Dim includedColumns As Object
Set includedColumns = CreateObject("Scripting.Dictionary")
With includedColumns
.Add 1, "" ' <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< col 1 as dupe criteria
.Add 3, "" ' <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< col 3 as dupe criteria
End With
Dim searchRng As Range
Dim memArr As Variant
Dim i As Long
Dim j As Long
Dim unique As String
Dim totalRows As Long
Dim totalCols As Long
Dim totalURCols As Long
Dim valDict As Object
Set valDict = CreateObject("Scripting.Dictionary")
If CASE_SENSITIVE = 1 Then
valDict.CompareMode = vbBinaryCompare
Else
valDict.CompareMode = vbTextCompare
End If
With ThisWorkbook.Sheets(SHEET_NAME)
totalRows = .UsedRange.Rows.Count 'get last used row on sheet
totalURCols = .UsedRange.Columns.Count 'get last used col on sheet
Set searchRng = .Range( _
.Cells(FIRST_ROW, FIRST_COL), _
.Cells(totalRows, LAST_COL) _
)
If LAST_COL < totalURCols Then
.Range( _
.Cells(FIRST_ROW, LAST_COL + 1), _
.Cells(FIRST_ROW, totalURCols) _
).EntireColumn.Delete 'delete any extra columns
End If
End With
memArr = searchRng.Resize(totalRows, LAST_COL + 1) 'entire range with data to mem
For i = 1 To totalRows 'each row, without the header
For j = 1 To LAST_COL 'each col
If includedColumns.exists(j) Then
unique = unique & searchRng(i, j) 'concatenate values on same row
End If
Next
If valDict.exists(unique) Then 'check if entire row exists
memArr(i, LAST_COL + 1) = DUPE 'if it does, flag it in last col
Else
valDict.Add Key:=unique, Item:=i 'else add it to the dictionary
End If
unique = vbNullString
Next
searchRng.Resize(totalRows, LAST_COL + 1) = memArr 'entire memory back to the sheet
End Sub
.
Result:

Resources