Delete old data in table after copy and paste macro - excel

I have developed a working macro in VBA that automatically opens a workbook and then copies the data and pastes it on a table in the workbook I am working on. I perform this task monthly.
The data set varies in rows every month but does not vary in columns.
I am running into issues when the data set in less rows than the previous month and I am forced to manually delete lines that remained in the table because the previous month had more rows.
I was hoping to add to my existing code to automatically delete the old data after pasting the new data.
I perform a manual keystroke of selecting the last row of new data and move down one cell in column A then do a Ctrl+Shift+Down+Right to grab the data and select delete. So essentially that is the task I am trying to replace.
Thanks.
Sub Import_File()
Dim wbSourceData As Workbook
Dim wbDestination As Workbook
Dim wsSourceData As Worksheet
Dim wsDestination As Worksheet
Dim strFName As String
Dim rng As Range
Dim tbl As ListObject
Dim Cl As Long
Dim Rl As Long
Set wbDestination = ThisWorkbook
Set wsDestination = wbDestination.Sheets("DataTab")
strFName = wbDestination.Worksheets("Macros").Range("C2").Value
Set wbSourceData = Workbooks.Open(strFName)
Set wsSourceData = wbSourceData.Worksheets(3)
Set tbl = wsDestination.ListObjects("Data_Report")
tbl.DataBodyRange.ClearContents
With wsSourceData
Cl = .Cells(2, .Columns.Count).End(xlToLeft).Column
Rl = .Cells(.Rows.Count, "A").End(xlUp).Row
Set rng = .Range(.Cells(2, "A"), .Cells(Rl, Cl))
End With
rng.Copy
wsDestination.Range("A4").PasteSpecial xlValues
Application.CutCopyMode = False
wbSourceData.Close SaveChanges:=False
End Sub

Overwrite Data Body Range
It is assumed that only values of the range will be copied.
The Code
Option Explicit
Sub overwriteDataBodyRangeTEST()
Dim rg As Range: Set rg = Range("G2:K11")
Dim tbl As ListObject: Set tbl = DataTab.ListObjects("Data_Report")
overwriteDataBodyRange rg, tbl
End Sub
Sub overwriteDataBodyRange( _
ByVal rg As Range, _
ByVal tbl As ListObject)
With tbl.DataBodyRange
Dim rCount As Long: rCount = rg.Rows.Count
Dim tCount As Long: tCount = .Rows.Count
If rg.Columns.Count = .Columns.Count Then
.Resize(rCount).Value = rg.Value
If rCount < tCount Then
.Resize(tCount - rCount).Offset(rCount).Delete
End If
Else
MsgBox "Different number of columns.", vbCritical, "Fail"
End If
End With
End Sub
EDIT
The following will copy the range to the table overwriting the previous data. If the previous data has more rows, they will be deleted.
Integrated
Option Explicit
Sub Import_File()
' Define Destination Table.
Dim dwb As Workbook: Set dwb = ThisWorkbook ' workbook containing this code
Dim dws As Worksheet: Set dws = dwb.Worksheets("DataTab")
Dim tbl As ListObject: Set tbl = dws.ListObjects("Data_Report")
' Define Source Range.
Dim sName As String: sName = dwb.Worksheets("Macros").Range("C2").Value
Dim swb As Workbook: Set swb = Workbooks.Open(sName)
Dim sws As Worksheet: Set sws = swb.Worksheets(3)
Dim rng As Range
Dim LastRow As Long
Dim LastColumn As Long
With sws
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
LastColumn = .Cells(2, .Columns.Count).End(xlToLeft).Column
Set rng = .Range(.Cells(2, "A"), .Cells(LastRow, LastColumn))
End With
' Copy Source Range to Destination Table.
With tbl.DataBodyRange
Dim tCount As Long: tCount = .Rows.Count
Dim rCount As Long: rCount = rng.Rows.Count
.Resize(rCount).Value = rng.Value ' values only
'rng.Copy .Resize(rCount) ' values, formats, and formulas
If rCount < tCount Then
.Resize(tCount - rCount).Offset(rCount).Delete
End If
End With
' Close Source Workbook (it was just read from).
swb.Close SaveChanges:=False
End Sub

Related

Simple loop does not paste ranges correctly

I have a simple loop that should copy ranges form three sheets and stack them on top of each other in another sheet. I define the ranges of each of the three sheets via a cell that counts rows in the Control Sheet.
I do not get an error message, however only the range of the first sheets gets pasted. I troubleshooted already to see if the loop is running until end and indeed it does. I cannot wrap my head around why only the range from the first sheets gets pasted in the final sheet.
Sub Loop()
Dim ws_Sheet As Worksheet, ws As Worksheet
Dim lng_LastRow As Long, lng_LastColumn As Long, lng_LastRowSheet As Long
Dim rng_WorkRange As Range
Dim arrSht, i
Dim counter As Integer
arrSht = Array("a", "b", "c")
Set ws_Sheet = Worksheets("d")
ws_Sheet.Cells.ClearContents
counter = 1
For i = 0 To 2
Set ws = Worksheets(arrSht(i))
lng_LastRow = Worksheets("Control").Range("E" & counter).Value + 1
lng_LastColumn = ws.Cells(1, Columns.Count).End(xlToLeft).Column
lng_LastRowSheet = ws_Sheet.Cells(Rows.Count, 1).End(xlUp).Row
Set rng_WorkRange = ws.Range(ws.Cells(1, 1), ws.Cells(lng_LastRow, lng_LastColumn))
rng_WorkRange.Copy ws_Sheet.Range("A" & lng_LastRowSheet)
counter = counter + 1
Next i
End Sub
The issue is
lng_LastRowSheet = ws_Sheet.Cells(Rows.Count, 1).End(xlUp).Row
is the last used row (the last row that has data).
And then you use that to start pasting
rng_WorkRange.Copy ws_Sheet.Range("A" & lng_LastRowSheet)
so you overwrite the last row of data!
The next free row is lng_LastRowSheet + 1 so you should paste there:
rng_WorkRange.Copy ws_Sheet.Range("A" & (lng_LastRowSheet + 1))
You can also see that in the debug data:
a $A$1:$B$338 to A1
b $A$1:$B$91 to A338
c $A$1:$B$356 to A428
a goes from A1:B338 but you start pasting b in A338 so it overwrites the last row of a.
I gave it a test:
Created worksheet Control with data like
Then created worksheets a, b and c like
with data until row 500 so there is enough.
Then created an empty worksheet d for the output.
And used the following code. Note I have optimized it so it uses meaningful variable names, which is much easier to read, understand and debug.
Option Explicit
Public Sub CopyData()
Dim SheetNames() As Variant
SheetNames = Array("a", "b", "c")
Dim wsDestination As Worksheet
Set wsDestination = ThisWorkbook.Worksheets("d")
wsDestination.Cells.ClearContents
Dim i As Long
For i = 0 To 2
Dim wsSource As Worksheet
Set wsSource = ThisWorkbook.Worksheets(SheetNames(i))
Dim SourceLastRow As Long
SourceLastRow = ThisWorkbook.Worksheets("Control").Range("E" & i + 1).Value + 1
Dim SourceLastColumn As Long
SourceLastColumn = wsSource.Cells(1, wsSource.Columns.Count).End(xlToLeft).Column
Dim DestinationFreeRow As Long
DestinationFreeRow = wsDestination.Cells(wsDestination.Rows.Count, 1).End(xlUp).Row + 1 ' Last used row +1
Dim SourceRange As Range
Set SourceRange = wsSource.Range(wsSource.Cells(1, 1), wsSource.Cells(SourceLastRow, SourceLastColumn))
SourceRange.Copy wsDestination.Range("A" & DestinationFreeRow)
Next i
End Sub
And I get a perfect output like:
Note that in the output I have hidden some rows so you can see eveything is there. This code perfectly does what it is supposed to.
Stack Ranges (Vertically) From Multiple Worksheets
Sub StackRanges()
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Source
Dim sWorksheetNames() As Variant: sWorksheetNames = VBA.Array("a", "b", "c")
' Lookup (Source Last Row)?
Dim lws As Worksheet: Set lws = wb.Worksheets("Control")
Dim llrCell As Range: Set llrCell = lws.Range("E1")
' Destination
Dim dws As Worksheet: Set dws = wb.Worksheets("d")
dws.UsedRange.ClearContents
Dim dfCell As Range: Set dfCell = dws.Range("A1")
Dim sws As Worksheet
Dim srg As Range
Dim slRow As Long
Dim slColumn As Long
Dim i As Long
' Loop.
For i = 0 To UBound(sWorksheetNames)
Set sws = wb.Worksheets(sWorksheetNames(i))
slRow = llrCell.Value + 1
slColumn = sws.Cells(1, sws.Columns.Count).End(xlToLeft).Column
Set srg = sws.Range("A1", sws.Cells(slRow, slColumn))
srg.Copy dfCell
' If you only need to copy values (since you're using '.ClearContents'),
' instead, use the most efficient:
'dfCell.Resize(srg.Rows.Count, srg.Columns.Count).Value = srg.Value
Set llrCell = llrCell.Offset(1) ' next source last row lookup cell
Set dfCell = dfCell.Offset(srg.Rows.Count) ' next first dest. cell
Next i
End Sub
The counter and the lng_lastRow variable is too messy.
I repaleced some code as follow:
Sub newLoop()
Dim ws_Sheet As Worksheet, ws As Worksheet
Dim lng_LastRow As Long, lng_LastColumn As Long, lng_LastRowSheet As Long
Dim rng_WorkRange As Range, rng_lastRange As Range
Dim arrSht, i
Dim counter As Integer
arrSht = Array("a", "b", "c")
Set ws_Sheet = Worksheets("Control")
ws_Sheet.Cells.ClearContents
For i = 0 To 2
Set ws = Worksheets(arrSht(i))
Set rng_lastRange = ws_Sheet.Cells(Rows.Count, 1).End(xlUp)
lng_LastRow = ws.Cells(Rows.Count, 1).End(xlUp).Row
lng_LastColumn = ws.Cells(1, Columns.Count).End(xlToLeft).Column
Set rng_WorkRange = ws.Range(ws.Cells(1, 1), ws.Cells(lng_LastRow, lng_LastColumn))
rng_WorkRange.Copy rng_lastRange.Offset(1, 0)
Next i
End Sub

Extract values from each cell to a separate sheet

I have a file with a few sheets, I need to extract values from each not empty cell into a column on another sheet.
Would be awesome if while doing that duplicates can be removed as well.
The following code infinitely loops. I don't see how to break the loop since all the events are being used in the body of the code.
Range where the cells are being looked for on both sheets are different, that is why I used .End(xlUp) to define the last row with values in cells.
I cannot use empty cells as a trigger for stopping the loop because there are empty cells between cells with values.
Sub updt()
Dim ws As Worksheet, currWs As Worksheet, Lng As Integer, rng As Range
Set ws = wb.Worksheets("Sheet1") 'the source sheet
Set currWs = Sheets("Sheet2") 'the destination sheet
Lng = ws.Cells(Rows.Count, 1).End(xlUp).Row
Set rng = ws.Range("A2:A" & Lng)
For Each c Lng rng
If WorksheetFunction.CountIf(currWs.Range("A:A"), c.Value) = 0 Then
currWs.Range("A" & currWs.Cells(Rows.Count, 1).End(xlUp).Row)(2) = c.Value
End If
Next
End Sub
Update Column With Unique Non-Existing Values From a Column of Another Worksheet Using a Dictionary
To avoid further complications, no arrays are used.
Option Explicit
Sub UpdateWorksheet()
' Reference the workbook.
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Reference the source worksheet, calculate the last row
' and reference the source column range.
Dim sws As Worksheet: Set sws = wb.Worksheets("Sheet1")
Dim slRow As Long: slRow = sws.Cells(sws.Rows.Count, "A").End(xlUp).Row
Dim srg As Range: Set srg = sws.Range("A2:A" & slRow)
' Reference the destination worksheet and calculate the last row.
Dim dws As Worksheet: Set dws = wb.Worksheets("Sheet2")
Dim dlRow As Long: dlRow = dws.Cells(dws.Rows.Count, "A").End(xlUp).Row
' Define a dictionary (object).
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare ' case-insensitive
' Declare variables.
Dim cCell As Range
Dim cKey As Variant
' Write the unique values from the destination column range
' to the dictionary.
If dlRow > 1 Then ' 1 means 'first row - 1' i.e. '2 - 1'
Dim drg As Range: Set drg = dws.Range("A2:A" & dlRow)
For Each cCell In drg.Cells
cKey = cCell.Value
If Not IsError(cKey) Then ' exclude error values
If Len(cKey) > 0 Then ' exclude blanks
dict(cKey) = Empty
End If
End If
Next cCell
End If
' Add the unique values from the source column range
' to the dictionary.
For Each cCell In srg.Cells
cKey = cCell.Value
If Not IsError(cKey) Then ' exclude error values
If Len(cKey) > 0 Then ' exclude blanks
dict(cKey) = Empty
End If
End If
Next cCell
' Check if the dictionary is empty.
If dict.Count = 0 Then
MsgBox "No valid values found.", vbCritical
Exit Sub
End If
' Clear the previous values from the destination first cell to the bottom
' of the worksheet.
Dim dCell As Range: Set dCell = dws.Range("A2")
With dCell
.Resize(dws.Rows.Count - .Row + 1).ClearContents
End With
' Write the unique values from the dictionary to the destination worksheet.
For Each cKey In dict.Keys
dCell.Value = cKey ' write
Set dCell = dCell.Offset(1) ' reference the cell below
Next cKey
' Inform.
MsgBox "Worksheet updated.", vbInformation
End Sub
You might want to use AdvancedFilter:
Option Explicit
Sub Copy_Advanced()
Dim ws As Worksheet, currWs As Worksheet, Lng As Integer, rng As Range
Set ws = Worksheets("Sheet1") 'the source sheet
Set currWs = Sheets("Sheet2") 'the destination sheet
Lng = ws.Cells(Rows.Count, 1).End(xlUp).Row
Set rng = ws.Range("A1:A" & Lng)
ws.Range("D1").Value = ws.Range("A1").Value
ws.Range("D2") = ">0"
ws.Range(rng.Address).AdvancedFilter Action:=xlFilterCopy, _
CriteriaRange:=ws.Range("D1:D2"), _
CopyToRange:=currWs.Range("A1"), _
Unique:=True
End Sub

Clear contents of cells E through AN if cell E has contents

I am trying to clear all of the contents from columns E through AN for only the rows that have data in Column E (and not continue through the rest of the sheet). I would like the formulas in the cells to stay intact.
Any help greatly appreciated!
Sub ClearJEdetails()
Dim rng As Range
Set rng = Range("e12:AI1048530")
'Selecting only hardcoded data
rng.SpecialCells(xlCellTypeConstants).Select
Selection.ClearContents
End Sub
Sub ClearJEformulas()
Dim rng As Range
Set rng = Range("aj13:Ak1048530")
'Selecting only formulas
rng.SpecialCells(xlCellTypeFormulas).Select
Selection.ClearContents
End Sub
Clear Contents of Filtered Rows in Certain Columns
Adjust the values in the constants section.
Option Explicit
Sub DeleteFilteredEntireRows()
Const wsName As String = "Sheet1"
Const hRow As Long = 1
Const lrCol As String = "E"
Const Cols As String = "E:AN"
Const Criteria As String = "<>" ' non-blanks
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim ws As Worksheet: Set ws = wb.Worksheets(wsName)
If ws.AutoFilterMode Then ws.AutoFilterMode = False
Dim lRow As Long: lRow = ws.Cells(ws.Rows.Count, lrCol).End(xlUp).Row
Dim rCount As Long: rCount = lRow - hRow + 1
Dim scrg As Range: Set scrg = ws.Cells(hRow, lrCol).Resize(rCount)
Dim scdrg As Range: Set scdrg = scrg.Resize(rCount - 1).Offset(1)
scrg.AutoFilter 1, Criteria
Dim vcdrg As Range
On Error Resume Next
Set vcdrg = scdrg.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
ws.AutoFilterMode = False
If vcdrg Is Nothing Then Exit Sub
Dim drg As Range: Set drg = Intersect(vcdrg.EntireRow, ws.Columns(Cols))
drg.ClearContents
End Sub

How to copy from a cell and paste in a new row of a different table VBA

I am trying to do the following:
Check each row if "Order" column is empty in table1 from sheet1 (there is only one table in this sheet)
if it is "Order" column is empty, copy the "Notification" number from the same row AND then paste it into a new row of a table (table2) in another sheet (sheet2) under column "Notification".
if it is not empty, go to the next row in table1
I have the following code so far:
For Each TCell in Range ("Table1").ListObject.ListColumns("Order").DataBodyRange.Cells
If TCell.Value="" then
Table2.ListRows.Add AlwaysInsert:=True
Range(TCell.Row, "Notification").Copy Range("Table2") .ListObject. ListColumns ("Notification"
.DataBodyRange.End(xlDown).Offset (1,0)
End if
Next TCell
Any help would be greatly appreciated!
Thanks.
Append Table's Column to Another Table's Column
This is a basic solution that 'literally' does what is required (slow). By using object variables, it illustrates their application.
You could increase efficiency by introducing arrays, or especially by using AutoFilter.
Option Explicit
Sub AppendNotifications()
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim sws As Worksheet: Set sws = wb.Worksheets("Sheet1")
Dim stbl As ListObject: Set stbl = sws.ListObjects("Table1")
Dim slcl As ListColumn: Set slcl = stbl.ListColumns("Order")
Dim svcl As ListColumn: Set svcl = stbl.ListColumns("Notification")
Dim scOffset As Long: scOffset = svcl.Index - slcl.Index
Dim dws As Worksheet: Set dws = wb.Worksheets("Sheet2")
Dim dtbl As ListObject: Set dtbl = dws.ListObjects("Table2")
Dim dvcl As ListColumn: Set dvcl = dtbl.ListColumns("Notification")
Dim dvCol As Long: dvCol = dvcl.Index
Dim sCell As Range
Dim dvrw As ListRow
For Each sCell In slcl.DataBodyRange
If Len(sCell.Value) = 0 Then
Set dvrw = dtbl.ListRows.Add
dvrw.Range(dvCol).Value = sCell.Offset(, scOffset).Value
End If
Next sCell
MsgBox "Notifications appended.", vbInformation
End Sub
Could try below codes:
Sub transform()
Dim cell As Range
Set rng1 = Sheet1.Range("Table1[Order]")
Set SheetTwo = ActiveWorkbook.Worksheets("Sheet2")
Set TableTwo = SheetTwo.ListObjects("Table2")
For Each cell In rng1
If Not IsEmpty(cell.Offset(0, 0).Value) Then
Dim newrow As ListRow
Set newrow = TableTwo.ListRows.Add
With newrow
.Range(1) = cell.Offset(0, 1).Value
End With
End If
Next cell
End Sub
Codes are self-explanatory.
Notes: Table2 only has a column.

How do you paste filtered values only in the cells that were affected by such filter using vba?

I'm currently working on a Macro that its currently filtering a table based on a value and then it copies the data under a column after the filters have been applied (got that to work). However, I can't figure out how to paste those values in the same table overwriting the data under the visible cells within a different column. Values highlighted in red (picture) are being copied, now I need to paste them over only in the cells highlighted yellow. Thank you!
Public Sub DxcDateUpdate()
Application.ScreenUpdating = False
Dim Mwb As Workbook
Dim ws As Worksheet
Set Mwb = ThisWorkbook
Set ws = Mwb.Worksheets("Commission")
Set ws2 = Mwb.Worksheets("test")
lr = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
ws.Range("A1").AutoFilter Field:=31, Criteria1:="DXC/TPV.com Enrollment"
ws.Range("AG2:AG" & lr).SpecialCells(xlCellTypeVisible).Copy Destination:=ws.Range '''here is where idk what to do?'''
Application.ScreenUpdating = True
End Sub
You cannot paste a discontinuous range as discontinuous. You should iterate between each range cell and copy it using offset, or building the range to Paste using c.row. Please, try the next adapted code:
Sub DxcDateUpdate()
Dim Mwb As Workbook, ws As Worksheet, rngVis As Range, c As Range, LR As Long
Set Mwb = ThisWorkbook
Set ws = Mwb.Worksheets("Commission")
Set ws2 = Mwb.Worksheets("test")
LR = ws.cells(ws.rows.Count, 1).End(xlUp).row
ws.Range("A1").AutoFilter field:=31, Criteria1:="DXC/TPV.com Enrollment"
Set rngVis = ws.Range("AG2:AG" & LR).SpecialCells(xlCellTypeVisible)
For Each c In rngVis.cells
c.Offset(0, -28).value = c.value
Next
End Sub
In order to make the code faster, of course, you should use some optimization lines (ScreenUpdating = False, EnableEvents = False, Calculation = xlCalculationManual, followed after by True, True, xlCalculationAutomatic).
Copy 'Filtered' Values Using Arrays
The following will loop through the criteria column to find the criteria (string). When found, in the same row, the value from the source column will be copied to the destination column.
The columns' values are written to arrays to speed up the process (the loop).
Option Explicit
Sub DxcDateUpdate()
Const wsName As String = "Commission"
Const fRow As Long = 2
Const cCol As String = "AE" ' Criteria
Const sCol As String = "AG" ' Source
Const dCol As String = "E" ' Destination
Const Criteria As String = "DXC/TPV.com Enrollment"
Dim wb As Workbook: Set wb = ThisWorkbook
Dim ws As Worksheet: Set ws = wb.Worksheets(wsName)
Dim rg As Range: Set rg = ws.Range("A1").CurrentRegion
Dim lRow As Long: lRow = rg.Rows.Count
Dim cData As Variant: cData = rg.Columns(cCol).Value
Dim sData As Variant: sData = rg.Columns(sCol).Value
With rg.Columns(dCol)
Dim dData As Variant: dData = .Value
Dim r As Long
For r = fRow To lRow
If cData(r, 1) = Criteria Then
dData(r, 1) = sData(r, 1)
End If
Next r
.Value = dData
End With
End Sub

Resources