I need my code to copy and paste values from only 2 specific sheets "Pro Rate" & "Weekly Labor" These two sheets have the same 9 columns that I want copied over.
The problem is my code is copying all 20+ sheets and pasting with formulas so essentially I get all NAs
I've tried using a code:
Public Sub CombineDataFromAllSheets()
Dim wksSrc As Worksheet, wksDst As Worksheet
Dim rngSrc As Range, rngDst As Range
Dim lngLastCol As Long, lngSrcLastRow As Long, lngDstLastRow As Long
'Notes: "Src" is short for "Source", "Dst" is short for "Destination"
Set wksDst = ThisWorkbook.Worksheets("Import")
lngDstLastRow = LastOccupiedRowNum(wksDst)
Set rngDst = wksDst.Cells(2, 1)
For Each wksSrc In ThisWorkbook.Worksheets
If wksSrc.Name <> "Import" Then
lngSrcLastRow = LastOccupiedRowNum(wksSrc)
With wksSrc
Set rngSrc = .Range(.Cells(2, 1), .Cells(lngSrcLastRow, 9))
rngSrc.Copy Destination:=rngDst
End With
lngDstLastRow = LastOccupiedRowNum(wksDst)
Set rngDst = wksDst.Cells(lngDstLastRow + 1, 1)
End If
Next wksSrc
End Sub
Public Function LastOccupiedRowNum(Sheet As Worksheet) As Long
Dim lng As Long
If Application.WorksheetFunction.CountA(Sheet.Cells) <> 0 Then
With Sheet
lng = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
End With
Else
lng = 1
End If
LastOccupiedRowNum = lng
End Function
First, you need to run a check to make sure that the sheet names match the ones you want to copy.
Second you need to use .PasteSpecial to ensure only values are pasted.
I have updated only the above 2 things in your code below...
Public Sub CombineDataFromAllSheets()
Dim wksSrc As Worksheet, wksDst As Worksheet
Dim rngSrc As Range, rngDst As Range
Dim lngLastCol As Long, lngSrcLastRow As Long, lngDstLastRow As Long
'Notes: "Src" is short for "Source", "Dst" is short for "Destination"
Set wksDst = ThisWorkbook.Worksheets("Import")
lngDstLastRow = LastOccupiedRowNum(wksDst)
Set rngDst = wksDst.Cells(2, 1)
For Each wksSrc In ThisWorkbook.Worksheets
'first change here**
If wksSrc.Name = "Pro Rate" Or wksSrc.Name = "Weekly Labor" Then
lngSrcLastRow = LastOccupiedRowNum(wksSrc)
With wksSrc
Set rngSrc = .Range(.Cells(2, 1), .Cells(lngSrcLastRow, 9))
'second change here**
rngSrc.Copy
rngDst.PasteSpecial Paste:=xlPasteValues
End With
lngDstLastRow = LastOccupiedRowNum(wksDst)
Set rngDst = wksDst.Cells(lngDstLastRow + 1, 1)
End If
Next wksSrc
End Sub
Public Function LastOccupiedRowNum(Sheet As Worksheet) As Long
Dim lng As Long
If Application.WorksheetFunction.CountA(Sheet.Cells) <> 0 Then
With Sheet
lng = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
End With
Else
lng = 1
End If
LastOccupiedRowNum = lng
End Function
Related
I have a number of sheets with VBA macros which transfer data after autofiltering.
When a sheet has no data after autofiltering, the macro brings up runtime error 1004 on the line
Workbooks("Predictology-Reports.xlsx").Sheets("FAL") _
.Range("A" & Rows.Count).End(xlUp).Offset(1).PasteSpecial xlPasteValues
Here is the full macro of one of them
Sub FALAYS()
Dim arr, ws As Worksheet, lc As Long, lr As Long
arr = Array("L.FAL_19_New_Summer2", "L.FA_FAL_3", "L.FAL_19_New_Summer")
Set ws = ActiveSheet
'range from A1 to last column header and last row
lc = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
lr = ws.Cells.Find("*", after:=ws.Range("A1"), LookAt:=xlPart, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
With ws.Range("A1", ws.Cells(lr, lc))
.HorizontalAlignment = xlCenter
.AutoFilter Field:=1, Criteria1:=arr, Operator:=xlFilterValues
If .Rows.Count - 1 > 0 Then
On Error Resume Next
.Offset(1, 0).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible).Copy
On Error GoTo 0
Else
Exit Sub
End If
End With
Workbooks("Predictology-Reports.xlsx").Sheets("FAL") _
.Range("A" & Rows.Count).End(xlUp).Offset(1).PasteSpecial xlPasteValues
Application.CutCopyMode = False
End Sub
Try to set a range variable to the visible rows, then check to see if that got set before you copy/paste.
Sub FALAYS()
Dim arr, ws As Worksheet, lc As Long, lr As Long, rngCopy As Range
arr = Array("L.FAL_19_New_Summer2", "L.FA_FAL_3", "L.FAL_19_New_Summer")
Set ws = ActiveSheet
'range from A1 to last column header and last row
lc = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
lr = ws.Cells.Find("*", after:=ws.Range("A1"), LookAt:=xlPart, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
With ws.Range("A1", ws.Cells(lr, lc))
.HorizontalAlignment = xlCenter
.AutoFilter Field:=1, Criteria1:=arr, Operator:=xlFilterValues
If .Rows.Count > 1 Then
On Error Resume Next
Set rngCopy = .Offset(1, 0).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If Not rngCopy Is Nothing Then
rngCopy.Copy
Workbooks("Predictology-Reports.xlsx").Sheets("FAL") _
.Range("A" & Rows.Count).End(xlUp).Offset(1).PasteSpecial xlPasteValues
Application.CutCopyMode = False
End If 'have anything to copy
End If
End With
End Sub
If this is a common task then pull it out into its own Sub:
'given a [filtered] table rngTable, copy visible data rows as values to rngDestination
Sub CopyVisibleRows(rngTable As Range, rngDestination As Range)
Dim rngVis As Range
If rngTable.Rows.Count > 1 Then
On Error Resume Next
Set rngVis = rngTable.Offset(1, 0).Resize(rngTable.Rows.Count - 1).SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If Not rngVis Is Nothing Then
rngVis.Copy
rngDestination.PasteSpecial xlPasteValues
End If
End If 'any source rows
End Sub
which reduces your original code to something like:
Sub FALAYS()
Dim arr, ws As Worksheet, lc As Long, lr As Long, tbl As Range
arr = Array("L.FAL_19_New_Summer2", "L.FA_FAL_3", "L.FAL_19_New_Summer")
Set ws = ActiveSheet
'range from A1 to last column header and last row
lc = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
lr = ws.Cells.Find("*", after:=ws.Range("A1"), LookAt:=xlPart, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
Set tbl = ws.Range("A1", ws.Cells(lr, lc))
With tbl
.HorizontalAlignment = xlCenter
.AutoFilter Field:=1, Criteria1:=arr, Operator:=xlFilterValues
If .Rows.Count > 1 Then
CopyVisibleRows tbl, Workbooks("Predictology-Reports.xlsx").Sheets("FAL") _
.Range("A" & Rows.Count).End(xlUp).Offset(1)
End If
End With
End Sub
There are a lot of improvements you could make to this code, but it should give you a starting point.
I'm assigning the filtered cells to a range, if there are cells, the range is "something".
Then copying the range to the sheet directly (you could skip the copy, paste method by transferring the values to the cells).
Tip: Try to avoid On Error Resume Next unless you know what you're doing and it is strictly necessary.
Read the comments and adjust the code to your needs.
EDIT: Added OERN as per Tim's suggestion
Code
Public Sub FALAYS()
Dim arrValues As Variant
arrValues = Array("L.FAL_19_New_Summer2", "L.FA_FAL_3", "L.FAL_19_New_Summer")
' Set the target workbook and sheet
Dim targetWorkbook As Workbook
Dim targetSheet As Worksheet
Set targetWorkbook = Workbooks("Predictology-Reports.xlsx")
Set targetSheet = targetWorkbook.Worksheets("FAL")
' Set the source sheet and range
Dim sourceSheet As Worksheet
Dim sourceRange As Range
Dim sourceColumn As Long
Dim sourceRow As Long
Set sourceSheet = ActiveSheet
'range from A1 to last column header and last row
sourceColumn = sourceSheet.Cells(1, sourceSheet.Columns.Count).End(xlToLeft).Column
sourceRow = sourceSheet.Cells.Find("*", after:=sourceSheet.Range("A1"), LookAt:=xlPart, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
With sourceSheet.Range("A1", sourceSheet.Cells(sourceRow, sourceColumn))
.HorizontalAlignment = xlCenter
.AutoFilter Field:=1, Criteria1:=arrValues, Operator:=xlFilterValues
If .Rows.Count - 1 > 0 Then
' Set the cells to the source range
On Error Resume Next
Set sourceRange = .Offset(1, 0).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible)
On Error Goto 0
' Validate if the source range has cells
If Not sourceRange Is Nothing Then
sourceRange.Copy targetSheet.Range("A" & Rows.Count).End(xlUp).Offset(1)
Else
Exit Sub
End If
End If
End With
End Sub
Let me know if it works.
I have a code on the internet that collects data from sheet 3 to the very end one, then combines data to sheet "STOCK DETAILS".
The problem is I just want to paste all the data as value.
I'm new to excel VBA so please help me! Many thanks!
So this is the code:
Sub Combine2()
Dim J As Long, lstrw As Long, lstco As Long, lstrw2 As Long
Dim sTableName As String
'Define Variables
sTableName = "Table1"
Application.ScreenUpdating = False
On Error Resume Next
Sheets("STOCK DETAILS").ListObjects(sTableName).Delete
Sheets(3).Rows(1).Copy Destination:=Sheets("STOCK DETAILS").Range("A4")
For J = 3 To Sheets.Count ' from sheet 2 to last sheet
If WorksheetFunction.CountA(Sheets(J).Cells) > 0 Then
With Sheets(J)
lstrw = .Cells.Find(what:="*", SearchOrder:=xlRows, SearchDirection:=xlPrevious, LookIn:=xlValues).Row
lstco = .Cells.Find(what:="*", SearchOrder:=xlColumns, SearchDirection:=xlPrevious, LookIn:=xlValues).Column
lstrw2 = Sheets("STOCK DETAILS").Cells.Find(what:="*", SearchOrder:=xlRows, SearchDirection:=xlPrevious, LookIn:=xlValues).Row
.Range(.Cells(2, "A"), .Cells(lstrw, lstco)).Copy Sheets("STOCK DETAILS").Range("A" & lstrw2)(2)
End With
End If
Next
With Sheets("STOCK DETAILS").ListObjects.Add(xlSrcRange, Sheets("STOCK DETAILS").Range("A$4:$L$9999"), , xlYes)
.Name = "Table1"
.TableStyle = "TableStyleLight21"
End With
End Sub
To paste just the values you use
.Range(...).PasteSpecial xlPasteValues
I am trying to put together a macro that finds a name in a column header then copies that column if the header matches.
It works the first time I run it but when the pasted column is deleted and the macro is run again I get a
Run-time Error 2004 Application-defined or Object-defined error
on this line:
Set sRange = Sheets("Data").Range("C1", Cells(1, LastCol))
Full code:
Sub Copy()
Dim Cell As Range, sRange As Range, Rng As Range
LastCol = Sheets("Data").Cells(1, Columns.Count).End(xlToLeft).Column
Set sRange = Sheets("Data").Range("C1", Cells(1, LastCol))
With sRange
Set Rng = .Find(What:="Chennai", _
After:=.Cells(1), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False)
If Not Rng Is Nothing Then
Lastrow = Sheets("Data").Cells(Rows.Count, Rng.Column).End(xlUp).Row
Sheets("Data").Range(Rng, Cells(Lastrow, Rng.Column)).Copy _
Destination:=Sheets("Summary").Range("A7")
End If
End With
End Sub
Can anyone see the issue?
Cells(1, LastCol)) has no worksheet specified. Therfore it is the same as ActiveSheet.Cells(1, LastCol)) and if Sheets("Data") is not the ActiveSheet this fails.
It should be
Set sRange = Worksheets("Data").Range("C1", Worksheets("Data").Cells(1, LastCol))
or even better
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Data")
Set sRange = ws.Range("C1", ws.Cells(1, LastCol))
Also I recommend to use Worksheets for worksheets as Sheets can also contain chart sheets etc.
Same problem in the end where Cells(Lastrow, Rng.Column) has no worksheet specified:
ws.Range(Rng, ws.Cells(Lastrow, Rng.Column)).Copy _
Destination:=Worksheets("Summary").Range("A7")
Make sure you never have a Cells, Range, Rows or Columns object without a worksheet specified. Or Excel might take the wrong worksheet.
In the end I would do something like (note that all variables should be declared, use Option Explicit:
Option Explicit
Public Sub Copy()
Dim wsSrc As Worksheet 'source worksheet
Set wsSrc = ThisWorkbook.Worksheets("Data")
Dim wsDest As Worksheet 'destination worksheet
Set wsDest = ThisWorkbook.Worksheets("Summary")
Dim LastCol As Long
LastCol = wsSrc.Cells(1, wsSrc.Columns.Count).End(xlToLeft).Column
Dim sRange As Range
Set sRange = wsSrc.Range("C1", wsSrc.Cells(1, LastCol))
Dim Rng As Range
Set Rng = sRange.Find(What:="Chennai", _
After:=sRange.Cells(1), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False)
If Not Rng Is Nothing Then
Dim LastRow As Long
LastRow = wsSrc .Cells(wsSrc.Rows.Count, Rng.Column).End(xlUp).Row
wsSrc.Range(Rng, wsSrc.Cells(LastRow, Rng.Column)).Copy _
Destination:=wsDest.Range("A7")
End If
End Sub
I received data in column F,G,H, and I. I need to get that all into column E and take out the duplicates and the blank cells. The code i have so far works but it puts them all in the same row and doesn't keep them on their appropriate lines. I need them to stay on the same line they are currently in but to just transcribe over into the other column. This is what I have so far:
Sub Sample()
Dim ws As Worksheet
Dim LastRow As Long, lastCol As Long, i As Long
Dim Rng As Range, aCell As Range, delRange As Range '<~~ Added This
Dim MyCol As New Collection
~~> Change this to the relevant sheet name
Set ws = Sheets("Sheet1")
With ws
'~~> Get all the blank cells
Set delRange = .Cells.SpecialCells(xlCellTypeBlanks) '<~~ Added This
'~~> Delete the blank cells
If Not delRange Is Nothing Then delRange.Delete '<~~ Added This
LastRow = .Cells.Find(What:="*", After:=.Range("A1"), _
Lookat:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, MatchCase:=False).Row
lastCol = .Cells.Find(What:="*", After:=.Range("A1"), _
Lookat:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, MatchCase:=False).Column
Set Rng = .Range("A1:" & Split(.Cells(, lastCol).Address, "$")(1) & LastRow)
'Debug.Print Rng.Address
For Each aCell In Rng
If Not Len(Trim(aCell.Value)) = 0 Then
On Error Resume Next
MyCol.Add aCell.Value, """" & aCell.Value & """"
On Error GoTo 0
End If
Next
.Cells.ClearContents
For i = 1 To MyCol.Count
.Range("A" & i).Value = MyCol.Item(i)
Next i
'~~> OPTIONAL (In Case you want to sort the data)
.Columns(1).Sort Key1:=.Range("A1"), Order1:=xlAscending, Header:=xlGuess, _
OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _
DataOption1:=xlSortNormal
End With
End Sub
Try this.
Sub CopyThingy()
Dim wb As Workbook
Dim ws As Worksheet
Dim lCount As Long
Dim lCountMax As Long
Dim lECol As Long
Dim lsourceCol As Long
lECol = 5 '* E column
Set wb = ThisWorkbook
Set ws = wb.Sheets(1) '*Your Sheet
lCountMax = ws.Cells(ws.Rows.Count, "F").End(xlUp).Row
lsourceCol = 6
lCount = lCountMax
Do While lCount > 1
If ws.Cells(lCount, lsourceCol) <> "" Then
ws.Cells(lCount, lECol).Value = ws.Cells(lCount, lsourceCol).Value
End If
lCount = lCount - 1
Loop
lCountMax = ws.Cells(ws.Rows.Count, "G").End(xlUp).Row
lsourceCol = 7
lCount = lCountMax
Do While lCount > 1
If ws.Cells(lCount, lsourceCol) <> "" Then
ws.Cells(lCount, lECol).Value = ws.Cells(lCount, lsourceCol).Value
End If
lCount = lCount - 1
Loop
lCountMax = ws.Cells(ws.Rows.Count, "H").End(xlUp).Row
lsourceCol = 8
lCount = lCountMax
Do While lCount > 1
If ws.Cells(lCount, lsourceCol) <> "" Then
ws.Cells(lCount, lECol).Value = ws.Cells(lCount, lsourceCol).Value
End If
lCount = lCount - 1
Loop
End Sub
The following Macro was intended to get specific data for a date range. While it does this, I wanted it displayed within the same workbook on another sheet, instead a new workbook is created. Any idea on how I can fix this?
Public Sub PromptUserForInputDates()
Dim strStart As String, strEnd As String, strPromptMessage As String
strStart = InputBox("Please enter the start date")
If Not IsDate(strStart) Then
strPromptMessage = "Not Valid Date"
MsgBox strPromptMessage
Exit Sub
End If
strEnd = InputBox("Please enter the end date")
If Not IsDate(strStart) Then
strPromptMessage = "Not Valid Date"
MsgBox strPromptMessage
Exit Sub
End If
Call CreateSubsetWorkbook(strStart, strEnd)
End Sub
Public Sub CreateSubsetWorkbook(StartDate As String, EndDate As String)
Dim wbkOutput As Workbook
Dim wksOutput As Worksheet, wks As Worksheet
Dim lngLastRow As Long, lngLastCol As Long, lngDateCol As Long
Dim rngFull As Range, rngResult As Range, rngTarget As Range
lngDateCol = 4
Set wbkOutput = Workbooks.Add
For Each wks In ThisWorkbook.Worksheets
With wks
Set wksOutput = wbkOutput.Sheets.Add
wksOutput.Name = wks.Name
Set rngTarget = wksOutput.Cells(1, 1)
lngLastRow = .Cells.Find(What:="*", LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious).Row
lngLastCol = .Cells.Find(What:="*", LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious).Column
Set rngFull = .Range(.Cells(1, 1), .Cells(lngLastRow, lngLastCol))
With rngFull
.AutoFilter Field:=lngDateCol, _
Criteria1:=">=" & StartDate, _
Criteria2:="<=" & EndDate
Set rngResult = rngFull.SpecialCells(xlCellTypeVisible)
rngResult.Copy Destination:=rngTarget
End With
.AutoFilterMode = False
If .FilterMode = True Then
.ShowAllData
End If
End With
Next wks
MsgBox "Data Transferred!"
End Sub
You're defining Set wbkOutput = Workbooks.Add which will always create a new workbook. Instead, Set wbkOutput = the workbook where you want the output to be.
Note that your assignment of wksOutput.Name = wks.Name will fail (two worksheets cannot have same name), so I've commented it out for now and you can revise that statement as needed.
Replace all references to wbkOutput with ThisWorkbook
Public Sub CreateSubsetWorkbook(StartDate As String, EndDate As String)
Dim wksOutput As Worksheet, wks As Worksheet
Dim lngLastRow As Long, lngLastCol As Long, lngDateCol As Long
Dim rngFull As Range, rngResult As Range, rngTarget As Range
lngDateCol = 4
For Each wks In ThisWorkbook.Worksheets
With wks
Set wksOutput = ThisWorkbook.Sheets.Add
' This is not allowed, you can make some change to the name but it cannot be the same name worksheet
' >>> wksOutput.Name = wks.Name
Set rngTarget = wksOutput.Cells(1, 1)
lngLastRow = .Cells.Find(What:="*", LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious).Row
lngLastCol = .Cells.Find(What:="*", LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious).Column
Set rngFull = .Range(.Cells(1, 1), .Cells(lngLastRow, lngLastCol))
With rngFull
.AutoFilter Field:=lngDateCol, _
Criteria1:=">=" & StartDate, _
Criteria2:="<=" & EndDate
Set rngResult = rngFull.SpecialCells(xlCellTypeVisible)
rngResult.Copy Destination:=rngTarget
End With
.AutoFilterMode = False
If .FilterMode = True Then
.ShowAllData
End If
End With
Next wks
End Sub