Merging tables from different sheets in Excel - excel

I have 5 different Sub declarations that all pretty much do the same thing. The only part that differs are the the worksheet variants (each Sub uses 2 specific worksheets).
What I'm trying to do is condense the 5 different Subs into one piece.
The below code is an example of one of my Subs (note the code below is only change throughout the Subs)
Only code that changes in the below group1 Sub.
Set ws = wb.Worksheets("A")
Set addWS = wb.Worksheets("A add")
Code for one of the Subs
' -- Combines table1 and table2 -- '
Sub group1()
Dim wb As Workbook
Dim ws As Worksheet
Dim addWS As Worksheet
Dim counter As Long
Dim counterAdd As Long 'counter for additional trades
Set wb = Workbooks("MASTER.xlsm")
Set ws = wb.Worksheets("A")
Set addWS = wb.Worksheets("A add")
ws.Activate 'activate sheet
' Checks to see if there is only 1 row or is empty
If IsEmpty(ws.Range("A11").Value) = True Then
Exit Sub
End If
If IsEmpty(ws.Range("A12").Value) = True And IsEmpty(ws.Range("A11").Value) = False Then
counter = 1
Else
counter = ws.Range("A11", Range("A11").End(xlDown)).Rows.count
End If
addWS.Activate 'activate additional sheet
' Checks to see if there is only 1 row or is empty
If IsEmpty(addWS.Range("A11").Value) = True Then
Exit Sub
End If
If IsEmpty(addWS.Range("A12").Value) = True And IsEmpty(addWS.Range("A11").Value) = False Then
counterAdd = 1
Else
counterAdd = addWS.Range("A11", Range("A11").End(xlDown)).Rows.count
End If
' Copy / paste additional trades
addWS.Range("A11:AB" & counterAdd + 10).Copy
ws.Activate
ws.Range("A" & counter + 11).PasteSpecial xlPasteAll
End Sub
In the below code I attempted to make this into one Sub with with 2 For loops, however it would get stuck in the second loop. Is there a way that I could loop through two things at once?
' -- Combines table1 and table2 -- '
Sub group()
Dim wb As Workbook
Dim ws As Worksheet
Dim wsAdd As Worksheet
Dim counter As Long
Dim counterAdd As Long 'counter for additional trades
Dim WSArray As Variant
Dim WSArrayAdd As Variant
Set wb = Workbooks("MASTER.xlsm")
WSArray = Array("A", "B", "C", "D", "E")
WSArrayAdd = Array("A add", "B add", "C add", "D add", "E add")
'Loop through WSArray sheets
For Each currentWS In WSArray
Set ws = wb.Worksheets(currentWS)
ws.Activate
' Checks to see if there is only 1 row or is empty
If IsEmpty(ws.Range("A11").Value) = True Then
' do nothing
End If
If IsEmpty(ws.Range("A12").Value) = True And IsEmpty(ws.Range("A11").Value) = False Then
counter = 1
Else
counter = ws.Range("A11", Range("A11").End(xlDown)).Rows.count
End If
For Each currentAddWS In WSArrayAdd
Set wsAdd = wb.Worksheets(currentAddWS)
wsAdd.Activate 'activate additional sheet
' Checks to see if there is only 1 row or is empty
If IsEmpty(wsAdd.Range("A11").Value) = True Then
' do nothing
End If
If IsEmpty(wsAdd.Range("A12").Value) = True And IsEmpty(wsAdd.Range("A11").Value) = False Then
counterAdd = 1
Else
counterAdd = wsAdd.Range("A11", Range("A11").End(xlDown)).Rows.count
End If
' Copy / paste additional trades
wsAdd.Range("A11:AB" & counterAdd + 10).Copy
ws.Activate
ws.Range("A" & counter + 11).PasteSpecial xlPasteAll
Next currentAddWS
Next currentWS
End Sub

I was able to solve this in the code below. This code loops through a series of 10 sheets (each sheet has a corresponding sheet) and combines the tables to one main sheet.
' -- Combines additional trades table with main table -- '
Sub groupTables()
Dim wb As Workbook
Dim ws As Worksheet
Dim wsAdd As Worksheet
Dim counter As Long
Dim counterAdd As Long 'counter for additional trades
Dim WSArray As Variant
Dim WSArrayAdd As Variant
Dim i As Long
Set wb = Workbooks("MASTER.xlsm")
WSArray = Array("1", "2", "3", "4", "5")
'Loop through WSArray sheets
For Each currentWS In WSArray
Set ws = wb.Worksheets(currentWS)
Set wsAdd = wb.Worksheets(WSArray(i) & " add")
ws.Activate
' COUNTS ROWS IN TRADE SHEET
' Checks to see if there is only 1 row or is empty
If IsEmpty(ws.Range("A11").Value) = True Then
counter = 0 'no trades
ElseIf IsEmpty(ws.Range("A12").Value) = True And IsEmpty(ws.Range("A11").Value) = False Then
counter = 1
Else
counter = ws.Range("A11", Range("A11").End(xlDown)).Rows.count
End If
wsAdd.Activate
' COUNTS ROWS IN ADDITIONAL TRADE SHEET
' Checks to see if there is only 1 row or is empty
If IsEmpty(wsAdd.Range("A11").Value) = True Then
counterAdd = 0 'no trades
ElseIf IsEmpty(wsAdd.Range("A12").Value) = True And IsEmpty(wsAdd.Range("A11").Value) = False Then
counterAdd = 1
Else
counterAdd = wsAdd.Range("A11", Range("A11").End(xlDown)).Rows.count
End If
'Copy Additional trades
If counterAdd > 0 Then
wsAdd.Range("A11:AB" & counterAdd + 10).Copy 'Copy additional trades table
ws.Activate
ws.Range("A" & counter + 11).PasteSpecial xlPasteAll ' Paste additional trades to main table
End If
i = i + 1 'iterate
Next currentWS
End Sub

Related

Loop is copying the columns in the wrong ranges

I have made this code to create an output sheet where columns from different sheets are sorted by header name and pasted one after the other.
For some reason, it is not pasting the columns underneath each other, but instead overwriting each one with the next:
Dim ws As worksheet
Dim max_ws As worksheet
Dim output_ws As worksheet
Dim max_ws_header As Range
Dim output_ws_header As Range
Dim header_cell As Range
Dim cc As Long
Dim max_cc As Long
Dim output_header_counter As Long
Dim ws_header_counter As Long
Dim output_header_name As String
Dim ws_header_name As String
Application.DisplayAlerts = False
Sheets("indice").Delete
Sheets("aneca").Delete
Application.DisplayAlerts = True
For Each ws In Worksheets
ws.Rows(1).EntireRow.Delete
ws.Columns.Hidden = False
Next ws
max_cc = 0
For Each ws In Worksheets
cc = last_column_index(ws, 1)
If cc > max_cc Then
max_cc = cc
Set max_ws = ws
End If
Next ws
Sheets.Add.Name = "Output"
Set output_ws = Sheets("Output")
Set max_ws_header = max_ws.Range(max_ws.Cells(1, 1), max_ws.Cells(1, max_cc))
Set output_ws_header = output_ws.Range(output_ws.Cells(1, 1), output_ws.Cells(1, max_cc))
max_ws_header.Copy output_ws_header
For Each ws In Worksheets
If ws.Name <> "Output" Then
For output_header_counter = 1 To max_cc
output_header_name = output_ws.Cells(1, output_header_counter).Value
For ws_header_counter = 1 To max_cc
ws_header_name = ws.Cells(1, ws_header_counter).Value
If ws_header_name = output_header_name Then
ws.Range(Cells(1, ws_header_counter), Cells(last_row_index(ws, ws_header_counter), ws_header_counter)).Copy _
output_ws.Range(Cells(last_row_index(output_ws, output_header_counter) + 1, output_header_counter), Cells(last_row_index(ws, ws_header_counter), output_header_counter))
End If
Next ws_header_counter
Next output_header_counter
End If
The functions last_row_index and last_column_index are UDFs that I made as follows:
Function last_row_index(target_worksheet As worksheet, target_column_index As Long) As Long
last_row_index = target_worksheet.Cells(Rows.Count, target_column_index).End(xlUp).Row
End Function
Function last_column_index(target_worksheet As worksheet, target_row_index As Long) As Long
last_column_index = target_worksheet.Cells(target_row_index, Columns.Count).End(xlToLeft).Column
End Function
Here is an example of the output:
I figured out the solution, posting it here to close the question:
For Each ws In Worksheets
If ws.Name <> "Output" And ws.Name <> "indice" And ws.Name <> "aneca" Then
For row_index = 2 To last_row_index(ws, 1)
output_counter = last_row_index(output_ws, 1)
For column_index = 1 To last_column_index(ws, 1)
ws_header = ws.Cells(1, column_index).Value
For o_column_index = 1 To max_cc
output_header = output_ws.Cells(1, o_column_index).Value
If output_header = ws_header Then
ws.Cells(row_index, column_index).Copy output_ws.Cells(output_counter + 1, column_index)
Exit For
End If
Next o_column_index
Next column_index
Next row_index
End If
Next ws
I made an output counter variable and made it find the last row each time it starts on a new row in the input sheets, and then I add +1 to it every time it pastes a row.

While Deleting Repeated Headers

Using the below code to delete the repeated headers from combined into one excel but getting error.
Application.Goto DestSh.Cells(1)
' AutoFit the column width in the summary sheet.
DestSh.Columns.AutoFit
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
Dim xWs As Worksheet
Application.ScreenUpdating = False
Application.DisplayAlerts = False
For Each xWs In Application.ActiveWorkbook.Worksheets
If xWs.Name <> "Combined Sheet" Then
xWs.Delete
End If
Next
Application.DisplayAlerts = True
Application.ScreenUpdating = True
Dim lstRow As Integer, ws As Worksheet
Set ws = ThisWorkbook.Sheets("Combined Sheet")
With ws
lstRow = .Cells(rows.Count, "B").End(xlUp).Row ' Or "C" or "A" depends
.Range("A1:E" & lstRow).SpecialCells(xlCellTypeBlanks).EntireRow.Delete ERROR GETTING HERE
End With
enter image description here
Please add "on error resume next" before using SpecialCells method and after using use "on error GoTo 0"
.SpecialCells(xlCellTypeBlanks)
This expression gives you every blank cell in a Range. Rows that you are going to delete includes non-blank cells also, so vba will not delete them.
You can try a RemoveDuplicates method like:
.Range("A1:E" & lstRow).RemoveDuplicates Columns:=Array(1, 2, 3, 4, 5), Header :=xlNo
It can be not safe to use the method, but for your task it's may be Ok.
This sub is a safe variant to delete your headers. you can call the sub by the Call statement, and don't forget to set your header address.
Call removeHeaders()
Sub removeHeaders()
Dim hdrRangeAdr As String
Dim l, frstRow, lstRow, offsetRow As Long
Dim counter, row1, row2 As Integer
Dim item As Variant
Dim hdrRng, tRng As Range
Dim ws As Worksheet
' setting of the first header address
hdrRangeAdr = "A1:O1"
Set ws = ThisWorkbook.Sheets("Combined Sheet")
' setting of the header range
Set hdrRng = ws.Range(hdrRangeAdr)
hdrRowsQty = hdrRng.Rows.Count
frstRow = hdrRng.Row
lstRow = hdrRng.Parent.UsedRange.Rows.Count + frstRow
'checking row by row
For l = 1 To lstRow - frstRow
offsetRow = l + hdrRowsQty - 1
counter = 0
' compare row/rows value with the header
For Each item In hdrRng.Cells
If item = item.Offset(offsetRow, 0) Then
counter = counter + 1
End If
Next
' if they are equial then delete rows
If counter = hdrRng.Count Then
row1 = frstRow + offsetRow
row2 = row1 + hdrRowsQty - 1
ws.Rows(row1 & ":" & row2).Delete Shift:=xlUp
'reseting values as rows qty reduced
l = 1
lstRow = hdrRng.Parent.UsedRange.Rows.Count + frstRow
End If
Next
Set ws = Nothing
Set hdrRng = Nothing
End Sub
Good luck

Vba: Delete excel sheets not mentioned in the list (the list only contains numeric value)

I need to delete sheets not mentioned in the given list(Range is A7:A350).
I found this vba but the problem is it deletes all the sheets from my workbook, maybe because sheetname is in numeric. I would really appreciate any help.
Sub Deletenotinlist()
Dim i As Long
Dim cnt As Long
Dim xWb, actWs As Worksheet
Set actWs = ThisWorkbook.ActiveSheet
cnt = 0
Application.DisplayAlerts = False
For i = Sheets.Count To 1 Step -1
If Not ThisWorkbook.Sheets(i) Is actWs Then
xWb = Application.Match(Sheets(i).Name, actWs.Range("A7:A350"), 0)
If IsError(xWb) Then
ThisWorkbook.Sheets(i).Delete
cnt = cnt + 1
End If
End If
Next
Application.DisplayAlerts = True
If cnt = 0 Then
MsgBox "Not find the sheets to be seleted", vbInformation, "Kutools for Excel"
Else
MsgBox "Have deleted" & cnt & "worksheets"
End If
End Sub
I think I would do it this way.
Sub DeleteSheets()
Dim sht As Worksheet
Dim rng As Range
Set rng = Sheets("Sheet1").Range("A2:A10")
Application.DisplayAlerts = False
For Each sht In ActiveWorkbook.Worksheets
If Application.CountIf(rng, sht.Name) = 0 Then
sht.Delete
End If
Next sht
Application.DisplayAlerts = True
End Sub
What you try doing can be accomplished in many ways, but I tried adapting your code to place the missing sheets name in an array and select them at the end. If selection is convenient, you can replace Select with Delete:
Sub Deletenotinlist()
Dim i As Long, cnt As Long, xWb, actWs As Worksheet, lastR As Long, arrSh(), k As Long
Set actWs = ThisWorkbook.ActiveSheet
lastR = actWs.Range("A" & actWs.rows.count).End(xlUp).row
ReDim arrSh(ThisWorkbook.Sheets.count - 1)
cnt = 0
For i = 1 To Sheets.count
If Not ThisWorkbook.Sheets(i) Is actWs Then
xWb = Application.match(Sheets(i).Name, actWs.Range("A7:A" & lastR), 0)
If IsError(xWb) Then
arrSh(k) = CStr(ThisWorkbook.Sheets(i).Name): k = k + 1
cnt = cnt + 1
End If
End If
Next
ReDim Preserve arrSh(k - 1) 'keep only the filled array elements
Sheets(arrSh).Select 'You can replace 'Select' with 'Delete', if it returns correctly
If cnt = 0 Then
MsgBox "Not find the sheets to be seleted", vbInformation, "Kutools for Excel"
Else
MsgBox "Have deleted " & cnt & " worksheets"
End If
End Sub
It processes all existing values in column A:A, starting from the 7th row.
But I'm afraid that the range you try processing does not contain any existing sheet name...
In order to test the above supposition, please run the next test sub, which will place all existing sheets name in column B:B, starting from the 7th row. Then delete some rows and run the previous code, replacing "A" with "B" in lastR = actWs.Range("A" &... and actWs.Range("A7:A" & lastR). The code should select all missing sheets:
Sub testArraySheets()
Dim arrSh, ws As Worksheet, k As Long
ReDim arrSh(ActiveWorkbook.Sheets.count - 1)
For Each ws In ActiveWorkbook.Sheets
If Not ws Is ActiveSheet Then
arrSh(k) = ws.Name: k = k + 1
End If
Next
ActiveSheet.Range("B7").Resize(UBound(arrSh) + 1, 1).Value = Application.Transpose(arrSh)
End Sub

How to copy row from Excel sheet and paste it in another workbook in a specific row

In Workbook 1, I have a spreadsheet that tracks the inventory of meat products.
Row 1 is used for the column names: "Parcel Tracking Number" in column A and other data related to the parcel in the other columns (Such as "Date of export", "Weight" and "Content" among other things).
Column I describes the parcel's "Content" and these parcels all contain "Meat".
The rows of information in this spreadsheet have been copied from Workbook 2 which contains parcels that contain "Meat", "Cheese", "Milk" and "Eggs" in column I.
Both workbooks have the same columns names in row 1.
In workbook 1, I update the data on some of the rows and I want the change to be applied in Workbook 2 by copying workbook 1 rows and pasting them in Workbook 2 in the rows where the "Parcel Tracking Number" in column A matches.
So far, I have the code to copy all the "Meat" parcel rows from Workbook 2 and paste them in Workbook 1 but now I need help with this new situation.
The program is executed by opening Workbook 2 and pressing a command button which opens workbook 1 and starts copying the rows to the Meat worksheet.
Here it is:
Private Sub CommandButton1_Click()
Application.ScreenUpdating = False ' Screen Update application turned off in order to make program run faster
Dim y As Workbook '
Dim sh As Worksheet '
Set y = Workbooks.Open("\\SCF1\USERS-D\Robert\My Documents\Excel VBA code\Meat.xlsx") '
a = ThisWorkbook.Worksheets("Products").Cells(Rows.Count, 1).End(xlUp).Row
Set sh = Workbooks("Meat.xlsx").Worksheets("Meat")
With ThisWorkbook.Worksheets("Products")
For i = 2 To a ' value ''i'' is the column number
If ThisWorkbook.Worksheets("Products").Cells(i, 9).Value Like "*Meat*" And IsError(Application.Match(.Cells(i, "A").Value, sh.Columns("A"), 0)) Then ' this sets the condition for which the data can only be copied if the row has '' Meat '' included in the 9th column (substance) and if the row is not already copied in the Meat worksheet.
ThisWorkbook.Worksheets("Products ").Rows(i).Copy
Workbooks("Meat.xlsx").Worksheets("Meat").Activate
b = Workbooks("Meat.xlsx").Worksheets("Meat ").Cells(Rows.Count, 1).End(xlUp).Row
Workbooks("Meat.xlsx").Worksheets("Meat").Cells(b + 1, 1).Select
ActiveSheet.Paste
ThisWorkbook.Worksheets("Products").Activate
End If
Next
On Error Resume Next '1004 error kept appearing so this function allows us to continue to next step without error appearing
ThisWorkbook.Worksheets("Products").Cells(1, 1).Select
End With
MsgBox "All rows from Products worksheet have been copied."
Application.ScreenUpdating = True
End Sub
Any help is greatly appreciated. Thanks.
Use Find to check if Tracking Number exists and to locate row when transferring data back to Products.
Option Explicit
Sub CommandButton1_Click()
' update meat
Const PATH = "\\SCF1\USERS-D\Robert\My Documents\Excel VBA code\"
Const WB_NAME = "Meat.xlsx"
Dim wb As Workbook, ws As Worksheet, iLastRow As Long, iRow As Long
Dim wbTarget As Workbook, wsTarget As Worksheet, iTargetRow As Long
Set wbTarget = Workbooks.Open(PATH & WB_NAME)
Set wsTarget = wbTarget.Sheets("Meat")
iTargetRow = wsTarget.Cells(Rows.count, 1).End(xlUp).Row + 1
Set wb = ThisWorkbook
Set ws = wb.Sheets("Products")
iLastRow = ws.Cells(Rows.count, 1).End(xlUp).Row
Dim sContent As String, sTrackId As String
Dim res As Variant, count As Long
'Application.ScreenUpdating = False
count = 0
For iRow = 2 To iLastRow
sTrackId = ws.Cells(iRow, "A")
sContent = ws.Cells(iRow, "I")
If LCase(sContent) Like "*meat*" Then
' check not already on sheet
Set res = wsTarget.Range("A:A").Find(sTrackId)
If (res Is Nothing) Then
ws.Rows(iRow).Copy wsTarget.Cells(iTargetRow, 1)
iTargetRow = iTargetRow + 1
count = count + 1
End If
End If
Next
'wbTarget.Save
'wbTarget.Close
MsgBox count & " rows inserted from Products worksheet."
'Application.ScreenUpdating = True
End Sub
Sub CommandButton2_Click()
' update product
Const PATH = "\\SCF1\USERS-D\Robert\My Documents\Excel VBA code\"
Const WB_NAME = "Meat.xlsx"
Dim wb As Workbook, ws As Worksheet, iRow As Long
Dim wbSource As Workbook, wsSource As Worksheet, iLastSourceRow As Long
Set wbSource = Workbooks.Open(PATH & WB_NAME, False, True) 'no link update, read-only
Set wsSource = wbSource.Sheets("Meat")
iLastSourceRow = wsSource.Cells(Rows.count, 1).End(xlUp).Row + 1
Set wb = ThisWorkbook
Set ws = wb.Sheets("Products")
Dim sTrackId As String
Dim res As Variant, count As Long
'Application.ScreenUpdating = False
count = 0
For iRow = 2 To iLastSourceRow
sTrackId = wsSource.Cells(iRow, "A")
' find row on product sheet
Set res = ws.Range("A:A").Find(sTrackId)
If (res Is Nothing) Then
MsgBox "Could not update " & sTrackId, vbExclamation
Else
wsSource.Rows(iRow).Copy ws.Cells(res.Row, 1)
count = count + 1
End If
Next
wbSource.Close
MsgBox count & " rows updated from Meat workbook."
'Application.ScreenUpdating = True
End Sub

Delete rows on two different sheets based on cell value in a more efficient way [VBA Excel]

I have a two different worksheets with the same number of rows each one. In column R I have "New" or "Old" depending on the row (this is a dynamic value). What I want to do is, if a row in Worksheet1 contains "Old" in column R, then delete that row in both Worksheet1 and Worksheet2.
Now, I have tried two codes for this:
Dim w1 As Worksheet
Dim w2 As Worksheet
Set w1= Worksheets("Sheet1")
Set w2= Worksheets("Sheet2")
'-----------------------------------------------------
'Code 1
'-----------------------------------------------------
Application.ScreenUpdating = False
For r = w1.UsedRange.Rows.Count To 1 Step -1
If Cells(r, "R") = "Old" Then
w1.Rows(r).EntireRow.Delete
w2.Rows(r).EntireRow.Delete
End If
Next r
Application.ScreenUpdating = True
'-----------------------------------------------------
'Code 2
'-----------------------------------------------------
Dim i As Long
i = 1
Application.ScreenUpdating = False
Do While i <= w1.Range("R1").CurrentRegion.Rows.Count
If InStr(1, w1.Cells(i, 18).Text, "Old", vbTextCompare) > 0 Then
w1.Cells(i, 1).EntireRow.Delete
w2.Cells(i, 1).EntireRow.Delete
Else
i = i + 1
End If
Loop
Application.ScreenUpdating = True
Usually I have +800 rows, so Code 1 works as desired but it sometimes takes too long, like 3 minutes. Code 2 gets stuck so far.
What is an efficient way of doing this?
Delete Rows In Sheets
Implementing Union should considerably speed up the process.
The Code
Sub DeleteRowsInSheets()
Const cSheet1 As Variant = "Sheet1" ' First Worksheet Name/Index
Const cSheet2 As Variant = "Sheet2" ' First Worksheet Name/Index
Const cVntCol As Variant = "R" ' Search Column Letter/Number
Const cStrCriteria As String = "Old" ' Search Criteria String
Dim rngU1 As Range ' Union Range 1
Dim rngU2 As Range ' Union Range 2
Dim LastUR As Long ' Last Used Row
Dim i As Long ' Row Counter
With Worksheets(cSheet1)
' Calculate Last Used Row.
If .Cells.Find("*", .Cells(.Rows.Count, .Columns.Count), -4123, , 1) _
Is Nothing Then Exit Sub
LastUR = .Cells.Find("*", , , , , 2).Row
' Add found cells to Union Ranges.
For i = 1 To LastUR
If StrComp(.Cells(i, cVntCol), cStrCriteria, vbTextCompare) = 0 Then
If Not rngU1 Is Nothing Then
Set rngU1 = Union(rngU1, .Cells(i, 1))
Set rngU2 = Union(rngU2, Worksheets(cSheet2).Cells(i, 1))
Else
Set rngU1 = .Cells(i, 1)
Set rngU2 = Worksheets(cSheet2).Cells(i, 1)
End If
End If
Next
End With
' Delete rows.
If Not rngU1 Is Nothing Then
rngU1.EntireRow.Delete ' Hidden = True
rngU2.EntireRow.Delete ' Hidden = True
Set rngU2 = Nothing
Set rngU1 = Nothing
End If
End Sub
I think that there could be lots of formulas. So Application.Calculation = xlManual at the begining and Application.Calculation = xlCalculationAutomatic at the end should be good idea too.
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
For r = w1.UsedRange.Rows.Count To 1 Step -1
If Cells(r, "R") = "Old" Then
w1.Rows(r).EntireRow.Delete
w2.Rows(r).EntireRow.Delete
End If
Next r
Application.ScreenUpdating = true
Application.Calculation = xlCalculationAutomatic

Resources