Merge or combine data and tables with Excel VBA macros
This page shows examples of how to merge and combine data and tables (lists) using criteria. You can also download workbooks with the examples.
-
In the first example 2 tables/lists are merged to 1 with no duplicates. The new table is put in a new workbook and sorted.
-
In the second example we loop 2 lists and make 2 new: One with the shared values and one with the values that are not shared.
-
The third example shows how you can combine rows in 2 tables (in 2 different workbooks) if they have a shared value or "key" - in this case a company name. The new, combined table is put in a new workbook.
The examples use arrays, ranges, collections, the worksheet function "CountIf" and loops.
Merge to 1 table without duplicates
This example requires some values (text or numbers) in cell A1 and down on sheet 1 and 2. Copy the code by selecting it with the mouse, press CTRL+C to copy and paste into a VBA module with CTRL+V.
You can also download a zip-compressed workbook (Excel 2003) with this and the next example.
The code merges the values from 2 lists to 1 sorted list. Even if there are shared values in the 2 lists, the output list will have no duplicates. If for instance list 1 and 2 are like below, the output list will be like the third:
1: 2: 3:
Donald Duck Donald Duck Batman
Spiderman Spiderman Berlusconi
Batman Berlusconi Donald Duck
Spiderman
Here comes the code.
Sub MergeLists()
Dim rA As Range
Dim rB As Range
Dim rCell As Range
Dim lCount As Long
Dim colMerge As New Collection
On Error GoTo ErrorHandle
Application.ScreenUpdating = False
Worksheets(1).Activate
Set rA = Range(Range("A1"), Range("A1").End(xlDown))
Worksheets(2).Activate
Set rB = Range(Range("A1"), Range("A1").End(xlDown))
On Error Resume Next
For Each rCell In rA
colMerge.Add rCell.Value, rCell.Value
Next
For Each rCell In rB
colMerge.Add rCell.Value, rCell.Value
Next
On Error GoTo ErrorHandle
Workbooks.Add
With colMerge
For lCount = 1 To .Count
Range("A1").Offset(lCount - 1).Value = .Item(lCount)
Next
End With
Set rA = Range(Range("A1"), Range("A1").End(xlDown))
rA.Sort Key1:=Range("A1")
BeforeExit:
Set rA = Nothing
Set rB = Nothing
Set rCell = Nothing
Set colMerge = Nothing
Application.ScreenUpdating = True
Exit Sub
ErrorHandle:
MsgBox Err.Description & " Procedure MergeLists"
Resume BeforeExit
End Sub
Find shared and not shared values in 2 lists
Like the previous macro this one requires a vertical list starting in cell A1 on both sheet 1 and 2. The macro compares the two lists and makes two new: One with shared values (present in both lists) and one with non-shared values. The new lists are inserted in column J and K on sheet 1.
To check if a value from one list is present in the other, we use the spreadsheet function "CountIf". It counts the instances of a value in a given range. With short lists speed doesn't matter much, but with longer ones we want to minimize looping.
You can download a zip-compressed workbook (Excel 2003) with this and the previous example.
Sub UniqueAndDuplicates()
Dim rA As Range
Dim rB As Range
Dim rCell As Range
Dim vResult()
Dim vResult2()
Dim lCount As Long
Dim lCount2 As Long
On Error GoTo ErrorHandle
Application.ScreenUpdating = False
ThisWorkbook.Worksheets(1).Activate
Set rA = Range(Range("A1"), Range("A1").End(xlDown))
Set rB = Worksheets(2).Range("A1")
Set rB = Range(rB, rB.End(xlDown))
ReDim vResult(1 To rA.Count + rB.Count, 1 To 1)
ReDim vResult2(1 To rA.Count + rB.Count, 1 To 1)
For Each rCell In rA
With rCell
If WorksheetFunction.CountIf(rB, .Value) = 0 Then
lCount = lCount + 1
vResult(lCount, 1) = .Value
Else
lCount2 = lCount2 + 1
vResult2(lCount2, 1) = .Value
End If
End With
Next
For Each rCell In rB
With rCell
If WorksheetFunction.CountIf(rA, .Value) = 0 Then
lCount = lCount + 1
vResult(lCount, 1) = .Value
Else
lCount2 = lCount2 + 1
vResult2(lCount2, 1) = .Value
End If
End With
Next
If lCount > 0 Then
Set rCell = Range("J2").Resize(UBound(vResult), 1)
rCell.Value = vResult()
With Range("J1")
.Value = "Unique:"
.Font.Bold = True
End With
Else
MsgBox "All values are present in both tables."
End If
If lCount2 > 0 Then
Set rCell = Range("K2").Resize(UBound(vResult2), 1)
rCell.Value = vResult2()
With Range("K1")
.Value = "Duplicates:"
.Font.Bold = True
End With
Else
MsgBox "There were no duplicate values."
End If
BeforeExit:
Set rA = Nothing
Set rB = Nothing
Set rCell = Nothing
Erase vResult
Erase vResult2
Application.ScreenUpdating = True
Exit Sub
ErrorHandle:
MsgBox Err.Description & " Procedure UniqueAndDuplicates"
End Sub
For the code above to be foolproof it should verify that the two tables do exist, and it should also check if the array has more rows than the spreadsheet. If for instance the two lists share no values, the output list will be as long as the two lists put together.
Combine 2 tables
The last example shows how you can combine rows in 2 tables (in 2 different workbooks) based on criterion and insert the result in a new workbook. In this case a table with contact persons and one with company information are combined, if company names match.
Making your own workbooks to test this example is a bit tedious, so I recommend that you download the zip-compressed example workbooks, if you want to see how it works.
The macros are in the workbook "persons.xls" and for the stuff to work you must insert the path to "company-list.xls" on the sheet "Macro".
The table with contact persons (in "persons.xls") has the following fields/columns:
Contacts | Company | Tel. | E-mail
The table with companies has the following fields/columns:
Company | Address | Postal code | City | Type | Info
To put it short the macro loops the two tables, and if the company name matches, the data for the contact person(s) is appended. The new, combined table is put into a new workbook.
The number of columns in the combined table depends on the number of contact persons for each company.
Important tools for the macro are a collection, dynamic arrays, ranges and the spreadsheet function "CountIf". The reason for using arrays (and not just ranges) is speed. Here we go with a copy of the whole VBA module:
Option Explicit
Dim bAbort As Boolean
Dim sWbName As String
Sub CombineTables()
Application.ScreenUpdating = False
CheckCompanyList
If bAbort = True Then GoTo BeforeExit
Combine
BeforeExit:
Application.ScreenUpdating = True
bAbort = False
End Sub
Sub CheckCompanyList()
Dim wb As Workbook
Dim sPath As String
On Error Resume Next
With Worksheets("Macro")
sPath = .Range("B1").Value
If Len(sPath) > 4 Then
sWbName = sPath
Else
MsgBox sPath & " is not a valid file name."
bAbort = True
GoTo BeforeExit
End If
sPath = .Range("B2").Value & sWbName
If Len(sPath) < 8 Then
MsgBox sPath & " is not a valid path."
bAbort = True
GoTo BeforeExit
End If
End With
If Len(Dir(sPath)) = 0 Then
MsgBox "The company list workbook is not in the said folder."
bAbort = True
GoTo BeforeExit
End If
Set wb = Workbooks(sWbName)
If wb Is Nothing Then
Workbooks.Open (sPath)
End If
BeforeExit:
Set wb = Nothing
End Sub
Sub Combine()
Dim rPersons As Range
Dim rCompanies As Range
Dim colCompanies As New Collection
Dim vContacts()
Dim vPersons()
Dim vResult()
Dim lCol As Long
Dim lMax As Long
Dim lCount As Long
Dim lCount2 As Long
Dim lCount3 As Long
Dim lPcount As Long
Dim lLast As Long
Dim lHits As Long
Dim dFound As Double
Dim lResultCol As Long
Dim lResultRows As Long
On Error GoTo ErrorHandle
ThisWorkbook.Worksheets("Persons").Activate
If IsEmpty(Range("A2")) Then
MsgBox "First cell in contacts list is empty."
bAbort = True
Exit Sub
End If
If Len(Range("A3")) > 0 Then
Set rPersons = Range(Range("A2"), Range("A2").End(xlDown))
Set rPersons = Range(rPersons, rPersons.Offset(0, 3))
Else
Set rPersons = Range(Range("A2"), Range("A2").Offset(0, 3))
End If
vPersons() = rPersons.Value
Workbooks(sWbName).Worksheets("Companies").Activate
If IsEmpty(Range("A2")) Then
MsgBox "First cell in company list is empty."
bAbort = True
Exit Sub
End If
If Len(Range("A3")) > 0 Then
Set rCompanies = Range(Range("A2"), Range("A2").End(xlDown))
Set rCompanies = Range(rCompanies, rCompanies.Offset(0, 5))
Else
Set rCompanies = Range(Range("A2"), Range("A2").Offset(0, 5))
End If
With rCompanies
vResult() = .Value
lResultCol = .Columns.Count
lResultRows = .Rows.Count
End With
Set rCompanies = Nothing
On Error Resume Next
For lCount = 1 To UBound(vResult)
colCompanies.Add vResult(lCount, 1), vResult(lCount, 1)
Next
On Error GoTo ErrorHandle
With colCompanies
For lCount = 1 To .Count
lLast = 0
dFound = _
WorksheetFunction.CountIf(rPersons.Columns(2), .Item(lCount))
If dFound > 0 Then
lCol = dFound * 3
ReDim vContacts(1 To 1, 1 To lCol)
If lCol > lMax Then lMax = lCol
For lPcount = 1 To UBound(vPersons)
If vPersons(lPcount, 2) = .Item(lCount) Then
lHits = lHits + 1
vContacts(1, lLast + 1) = vPersons(lPcount, 1)
vContacts(1, lLast + 2) = vPersons(lPcount, 3)
vContacts(1, lLast + 3) = vPersons(lPcount, 4)
lLast = lHits * 3
End If
Next
End If
If dFound > 0 Then
If lResultCol < 6 + lMax Then
lResultCol = 6 + lMax
ReDim Preserve vResult(1 To lResultRows, 1 To lResultCol)
End If
For lCount2 = 1 To UBound(vResult)
If vResult(lCount2, 1) = .Item(lCount) Then
For lCount3 = 1 To lCol
vResult(lCount2, 6 + lCount3) = vContacts(1, lCount3)
Next
End If
Next
End If
lHits = 0
Next
End With
Workbooks.Add
Workbooks(Workbooks.Count).Worksheets(1).Activate
Set rPersons = Range(Range("A2"), Range("A2").Offset(lResultCol))
Set rPersons = rPersons.Resize(lResultRows, lResultCol)
rPersons.Value = vResult()
Set rCompanies = _
Range(Range("A1"), Range("A1").Offset(0, lResultCol - 1))
With rCompanies
.Interior.Color = 12688476
.Font.Bold = True
.Font.ColorIndex = 2
.Item(1).Value = "Company"
.Item(2).Value = "Address"
.Item(3).Value = "Postal code"
.Item(4).Value = "City"
.Item(5).Value = "Type"
.Item(6).Value = "Info"
For lCount = 7 To lResultCol
lHits = lCount Mod 3
Select Case lHits
Case 1
.Item(lCount).Value = "Contacts"
Case 2
.Item(lCount).Value = "Tel."
Case 0
.Item(lCount).Value = "E-mail"
End Select
Next
End With
With rPersons
For lCount = 1 To lResultRows Step 2
If lCount > lResultRows Then Exit For
.Rows(lCount).Interior.Color = 15000804
Next
End With
Set rCompanies = rCompanies.Resize(lResultRows + 1)
With rCompanies
.Borders(xlDiagonalDown).LineStyle = xlNone
.Borders(xlDiagonalUp).LineStyle = xlNone
With .Borders(xlEdgeLeft)
.LineStyle = xlContinuous
End With
With .Borders(xlEdgeTop)
.LineStyle = xlContinuous
End With
With .Borders(xlEdgeBottom)
.LineStyle = xlContinuous
End With
With .Borders(xlEdgeRight)
.LineStyle = xlContinuous
End With
With .Borders(xlInsideVertical)
.LineStyle = xlContinuous
End With
With .Borders(xlInsideHorizontal)
.LineStyle = xlContinuous
End With
.Columns.AutoFit
End With
BeforeExit:
Set rPersons = Nothing
Set rCompanies = Nothing
Set colCompanies = Nothing
Erase vResult
Erase vPersons
Erase vContacts
Exit Sub
ErrorHandle:
MsgBox Err.Description & " Procedure Combine"
bAbort = True
End Sub
That was it. As mentioned I recommend downloading the workbooks if you want to see the macros in action. With VBA you can automate a lot in Excel and as shown above also merging and combining data or tables.
Have fun!
Related:
|