VBA- Match headers with multiple rows using a mapping table - vba

I have a workbook with Sheet1, Sheet2, Mapping. I want to copy values from Sheet1 to Sheet2 based on both row/column headers in both sheets. The column headers are not the same, so I've created a mapping table in "Mapping" Sheet to match those headers.
The problem is that those headers are made from multiple rows- see this:
For example I need that (POS1 2019 Emp1) = (HR Department Employee 1).
The following code it's working but only if the mapping table looks like this:
(and also I've not added rows headers, I'm not sure how).
So please do you have any idea how can I make the mapping with multiple headers work? Should I put those headers in two separate named ranges? Any advice will be appreciated :)
Sheet1:
Sheet2:
Result that I want:
Public Sub test()
Application.ScreenUpdating = False
stack "Sheet2", "Sheet1", "Mapping"
Application.ScreenUpdating = True
End Sub
Public Sub stack(ByVal Sheet1 As String, ByVal Sheet2 As String, ByVal Mapping As String)
Dim rng As Range, trgtCell As Range, src As Worksheet, trgt As Worksheet, helper As Worksheet
Dim rngSrc As Range, rngDest As Range
Dim sht As Worksheet
Set src = Worksheets("Sheet1")
Set trgt = Worksheets("Sheet2")
Set helper = Worksheets("Mapping")
With src
For Each rng In Intersect(.Rows(3), .UsedRange).SpecialCells(xlCellTypeConstants)
Dim lkup As Variant
With helper
lkup = Application.VLookup(rng.Value, .Range("A2:B" & .Cells(.Rows.Count, "A").End(xlUp).Row), 2, False)
End With
If Not IsError(lkup) Then
Set trgtCell = trgt.Range("$B$2:$F$6").Find(lkup, LookIn:=xlValues, lookat:=xlWhole)
If Not trgtCell Is Nothing Then
.Range(rng.Offset(1), .Cells(.Rows.Count, rng.Column).End(xlUp)).Copy
With trgt
.Range(Split(trgtCell.Address, "$")(1) & 3).PasteSpecial
End With
End If
End If
Next rng
End With
End Sub

Related

Index match match/vlookup in VBA

I have an Excel document with two different Sheets. Sheet 2 has columns header names and rows header names. Sheet 1 has some of these columns with exact header names and rows header names but it's filled with data.
enter image description here, enter image description here
I want to make a macro that will look through all the column/rows headers in Sheet 1 and find their corresponding match in Sheet2. When the match is found, I need to copy the entry of the Sheet column/row header into the matching header of sheet2. Some entries in Sheet2 will not have matches and will remain blank.
I want it to look like this:
enter image description here
This is my code so far, it is working for the column headers but I don't know how to add for row headers as well. Any help is welcomed :)
Sub CopyData()
Application.ScreenUpdating = False
Dim LastRow As Long, header As Range, foundHeader As Range, lCol As Long, srcWS As Worksheet, desWS As Worksheet
Set srcWS = Sheets("Sheet1")
Set desWS = Sheets("Sheet2")
LastRow = srcWS.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
lCol = desWS.Cells(3, Columns.Count).End(xlToLeft).Column
For Each header In desWS.Range(desWS.Cells(3, 2), desWS.Cells(3, lCol))
Set foundHeader = srcWS.Rows(2).Find(header, LookIn:=xlValues, lookat:=xlWhole)
If Not foundHeader Is Nothing Then
srcWS.Range(srcWS.Cells(3, foundHeader.Column), srcWS.Cells(LastRow, foundHeader.Column)).Copy desWS.Cells(4, header.Column)
End If
Next header
Application.ScreenUpdating = True
End Sub
You can use built-in Range.Consolidate method (https://learn.microsoft.com/en-us/office/vba/api/excel.range.consolidate):
(Edit2)
Option Explicit
Sub ConsolidateThis()
Dim rng1 As Range, rng2 As Range, addr As String
With ThisWorkbook
' determine source and destination ranges
Set rng1 = getTableRange(.Worksheets("Sheet1").Range("A2"))
Set rng2 = getTableRange(.Worksheets("Sheet2").Range("A3"))
' make full address of consolidated range like "'[Consolidate.xlsm]Sheet1'!R3C1:R6C5"
addr = "'[" & .Name & "]" & rng1.Parent.Name & "'!" & rng1.Address(ReferenceStyle:=xlR1C1)
' do consolidation
rng2.Consolidate Sources:=Array(addr), Function:=xlSum, TopRow:=True, LeftColumn:=True
End With
End Sub
' Returns the range that starts with the top left corner cell and is bounded
' on the right and bottom by empty cells
Function getTableRange(LeftTopCornerCell As Range) As Range
Dim ws As Worksheet, rightEdge As Long, downEdge As Long
With LeftTopCornerCell(1)
Set ws = .Parent
rightEdge = ws.Cells(.Row, ws.Columns.Count).End(xlToLeft).Column
downEdge = ws.Cells(ws.Rows.Count, .Column).End(xlUp).Row
End With
Set getTableRange = ws.Range(LeftTopCornerCell(1), ws.Cells(downEdge, rightEdge))
End Function
Your best solution might to set 2 ranges, each taking values from tables in Sheet1 and Sheet2. Let's call them rgSrcTable and rgDestTable. Then you need to loop using For Each through each range and compare top and left headers, and when you find a match, copy the value of the cell in rgSrcTable to the cell in rgDestTable.
Edit: Code sample. Feel free to adapt ranges to your needs. Since this routine used Range.Value property, you can filter any data (string, numbers, etc.)
Option Explicit
Sub CopyDataWithFilter()
Dim iRowHeader As Integer, iColHeader As Integer
Dim rngSrc As Range, rngDest As Range, celSrc As Range, celDest As Range
iRowHeader = 2
iColHeader = 1
With ThisWorkbook
' Set source and destination ranges. Modify ranges according to your needs
Set rngSrc = .Worksheets("shtSrc").Range("$B$3:$E$5")
Set rngDest = .Worksheets("shtDest").Range("$B$3:$E$5")
' Loop through source range and dest range
For Each celDest In rngDest
For Each celSrc In rngSrc
' Compare top headers and left headers respectively. If matching, copy the value in destination table.
If .Worksheets("shtSrc").Cells(celSrc.Row, iColHeader).Value = .Worksheets("shtDest").Cells(celDest.Row, iColHeader).Value And _
.Worksheets("shtSrc").Cells(iRowHeader, celSrc.Column).Value = .Worksheets("shtDest").Cells(iRowHeader, celDest.Column).Value Then
celDest.Value = celSrc.Value
End If
Next celSrc
Next celDest
End With
End Sub
Result:

copy rows from 1 source worksheet to worksheets that match the worksheet name

I have a master worksheet that contains data with many columns.
Next I have also created multiple worksheets from a list.
Now, I would like to copy the rows from the master worksheet to the respective worksheets if the value in the column matches against all the worksheet name, else copy to an 'NA' sheet.
Previously I could only think of hardcoding, but it is not feasible because the number of worksheets may increase to 50+, so I need some help on how I can achieve this..
'find rows of master sheet
With sh
LstR = .Cells(.Rows.Count, "C").End(xlUp).Row 'find last row of column C
Set rng = .Range("C3:C" & LstR) 'set range to loop
End With
'start the loop
'loop through, then loop through each C cell in template. if cell.value == worksheet name, copy to respective worksheet... elseif... else copy to NA
For Each c In rng.Cells
If c = "WEST" Then
c.EntireRow.Copy wsl1.Cells(wsl1.Rows.Count, "A").End(xlUp).Offset(1) 'copy row to first empty row in WEST
ElseIf c = "PKM" Then
c.EntireRow.Copy wsl2.Cells(wsl2.Rows.Count, "A").End(xlUp).Offset(1)
Else
c.EntireRow.Copy wsl7.Cells(wsl7.Rows.Count, "A").End(xlUp).Offset(1)
End If
Next c
Thanks to #user9770531, I was able to do what I want for the macro.
However, now I would like to make the macro more flexible.
For example, I have this additional table in another worksheet with
ColA_id and ColB_group
Instead of just matching checking worksheet name against the values in column C, I would like to do this:
if the master file column C matches "ColA_id", copy the data to respective "ColB_group" worksheets. Assuming ColB_group have been used to create the worksheet names.
Use code bellow - all subs in the same (standard) module
It searches Master.ColumnC for each sheet name (except Master and NA)
Uses AutoFilter for each sheet name, and copies all rows at once
All rows not assigned to a specific sheet will be copied to NA
It assumes sheet NA is already created, with Headers
Option Explicit
Const NA_WS As String = "NA" 'Create sheet "NA" if it doesn't exist
Public Sub DistributeData()
Const MASTER_WS As String = "Master"
Const MASTER_COL As String = "C" 'AutoFilter column in Master sheet
Dim wb As Workbook
Set wb = Application.ThisWorkbook
Dim ws As Worksheet, lr As Long, lc As Long, ur As Range, fCol As Range, done As Range
With wb.Worksheets(MASTER_WS)
lr = .Cells(.Rows.Count, MASTER_COL).End(xlUp).Row
lc = .Cells(1, .Columns.Count).End(xlToLeft).Column
Set ur = .Range(.Cells(3, 1), .Cells(lr, lc))
Set fCol = .Range(.Cells(2, MASTER_COL), .Cells(lr, MASTER_COL))
Set done = .Range(.Cells(1, MASTER_COL), .Cells(2, MASTER_COL))
End With
Application.ScreenUpdating = False
For Each ws In wb.Worksheets
If ws.Name <> MASTER_WS And ws.Name <> NA_WS Then
fCol.AutoFilter Field:=1, Criteria1:=ws.Name
If fCol.SpecialCells(xlCellTypeVisible).Cells.Count > 1 Then
UpdateWs ws, ur
Set done = Union(done, fCol.SpecialCells(xlCellTypeVisible))
End If
End If
Next
If wb.Worksheets(MASTER_WS).AutoFilterMode Then
fCol.AutoFilter
UpdateNA done, ur
End If
Application.ScreenUpdating = True
End Sub
Private Sub UpdateWs(ByRef ws As Worksheet, ByRef fromRng As Range)
fromRng.Copy
With ws.Cells(ws.Rows.Count, "A").End(xlUp).Offset(1, 0)
.PasteSpecial xlPasteAll
End With
ws.Activate
ws.Cells(1).Select
End Sub
Private Sub UpdateNA(ByRef done As Range, ByRef ur As Range)
done.EntireRow.Hidden = True
If ur.SpecialCells(xlCellTypeVisible).Cells.Count > 1 Then
UpdateWs ThisWorkbook.Worksheets(NA_WS), ur.SpecialCells(xlCellTypeVisible)
End If
done.EntireRow.Hidden = False
Application.CutCopyMode = False
ur.Parent.Activate
End Sub

vba: using combobox value as autofilter criteria

I'm trying to use a value from a Combo Box with a list of names as criteria of an AutoFilter and then copy the results from my database (BASE_ACOMPANHAMENTOS) to my results sheet (BUSCA). My Combo Box name is busca_lista and this is what I've tried so far:
Private Sub OKBUSCA_Click()
Dim ws As Worksheet
Dim ws1 As Worksheet
Dim lstrw As Long
Set ws = Sheets("BASE_ACOMPANHAMENTOS")
Set ws1 = Sheets("BUSCA")
lstrw = ws.Cells(Rows.Count, 2).End(xlUp).Row
ws1.Range("C12:H100").Clear
ws1.Range("X5").Value = busca_lista.Value
ws.Range("A1:F" & lstrw).AdvancedFilter Action:= _
xlFilterCopy, CriteriaRange:=ws1.Range("X5"), CopyToRange:=ws1.Range("C11:H11"), Unique:=False
End Sub
As you guys can see, I'm copying the value from my Combo Box to one cell and then using this value as a criteria, but it isn't working because it's showing all the names in my database, not the name that I've searched for. You can see the example below:
The form:
My database:
The results:
Your CriteriaRange needs to include both column heading and values. Based on your code, I've added the heading "Nome" in X4. Your criteria range is X4:X5.
Private Sub OKBUSCA_Click()
Dim ws As Worksheet
Dim ws1 As Worksheet
Dim lstrw As Long
Set ws = Sheets("BASE_ACOMPANHAMENTOS")
Set ws1 = Sheets("BUSCA")
lstrw = ws.Cells(Rows.Count, 2).End(xlUp).Row
ws1.Range("C12:H100").Clear
ws1.Range("X4").Value = "Nome"
ws1.Range("X5").Value = busca_lista.Value
ws.Range("A1:F" & lstrw).AdvancedFilter Action:= _
xlFilterCopy, CriteriaRange:=ws1.Range("X4:X5"), CopyToRange:=ws1.Range("C11:H11"), Unique:=False
End Sub
You have given the value to filter on but not the column. You need to have a cell which also holds the value Nome in in X4 and have your criteriarange as X4:X5

How to merge 3 cells with using an absolute cell reference in VBA

Trying to put together a VBA macro Im trying to reference Sheet1A8(CSWAH_) as an absolute cell reference and merge SeparateA7(Last Name) and SeperateB7(First Name) so they'll display in SeperateE7(ASA Naming) and be able to carry it all the way down from E7 to E100.
Is something like this even possible?
Try this.
Sub Demo()
Dim ws As Worksheet
Dim rng As Range
Dim lastRow As Long
Set ws = ThisWorkbook.Sheets("Sheet1") 'change Sheet1 to your data sheet
With ws
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row 'last row with data using Column A
Set rng = .Range("E7:E" & lastRow) 'set range in Column E
rng.Formula = "=$B$1&""_""&A7&"",""&B7" 'enter formula in range
rng.Value = rng.Value 'display values in range
End With
End Sub

VBA 'Vlookup' function operating on dynamic number of rows

I am not sure how to combine a Function with a Sub. Most likely, the Sub I have below needs corrections.
I have two tables in two separate sheets: Sheet1 and Sheet2.
Both tables have dynamic number of rows but the first rows always start in the same place and the number of columns in both tables is constant, too. Sheet1 data starts in A2 and ends in R2:R and Sheet2 data starts in A3 and ends in H3:H.
I am trying to implement VLOOkUP in column O of Sheet1, that would populate each cell in column O of Sheet1 with relevant values of column D in Sheet2. So far I managed to come up with code as below.
Public Function fsVlookup(ByVal pSearch As Range, ByVal pMatrix As Range, ByVal pMatColNum As Integer) As String
Dim s As String
On Error Resume Next
s = Application.WorksheetFunction.VLookup(pSearch, pMatrix, pMatColNum, False)
If IsError(s) Then
fsVlookup = ""
Else
fsVlookup = s
End If
End Function
Public Sub Delinquency2()
Dim ws1 As Worksheet, ws2 As Worksheet
Dim rng As Range
Dim rCell As Range
Set ws1 = Worksheets("Sheet1")
Set ws2 = Worksheets("Sheet2")
pSearch = ws1.Range("D2:D" & Cells(Rows.Count, "A").End(xlDown).Row)
pMatrix = ws2.Range("$A3:$H" & Cells(Rows.Count, "C").End(xlDown).Row)
pMatColNum = 4
Set rng = ws1.Range("O2:O" & Cells(Rows.Count, "A").End(xlDown).Row)
For Each rCell In rng.Cells
With rCell
rCell.FormulaR1C1 = s
End With
Next rCell
End Sub
You will need to call the function in your sub using a similar line to below. It then takes your values from your sub and inputs them into the function and returns the value.
You need to dim the ranges in order for them to be recognized correctly in your function. I have updated your code to make it work and you can fiddle around with it to make it work the way you want it to. I also updated a few other spots to figure out the correct ranges, you don't want to use xlDown where you were using it, causes an enormous loop covering cells you don't want it to.
Public Function fsVlookup(ByVal pSearch As Range, ByVal pMatrix As Range, ByVal pMatColNum As Integer) As String
Dim s As String
On Error Resume Next
s = Application.WorksheetFunction.VLookup(pSearch, pMatrix, pMatColNum, False)
If IsError(s) Then
fsVlookup = ""
Else
fsVlookup = s
End If
End Function.
Public Sub Delinquency2()
Dim ws1 As Worksheet, ws2 As Worksheet
Dim rng As Range
Dim rCell As Range, pMatrix As Range
Set ws1 = Worksheets("Sheet1")
Set ws2 = Worksheets("Sheet2")
pSearchCol = ws1.Range("D2:D2").Column
Set pMatrix = ws2.Range("$A3:$H" & ws2.Cells(Rows.Count, "C").End(xlUp).Row)
pMatColNum = 4
Set rng = ws1.Range("O2:O" & ws1.Cells(Rows.Count, "A").End(xlUp).Row)
For Each rCell In rng.Cells
With rCell
rCell.Value = fsVlookup(ws1.Cells(rCell.Row, pSearchCol), pMatrix, pMatColNum)
End With
Next rCell
End Sub