I found and modified a code that works pretty well but I am struggling with Set CopyRng = sh.Range("A11:AI15") . What I want to do is to create a dynamic range that will copy values from first table from each worksheet between header called Language and last row in table called Total. Some cells are merged in the table (default template) and there are empty columns in the table (so .CurrentRegion doesn't work).
Function LastRow(sh As Worksheet)
On Error Resume Next
LastRow = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
On Error GoTo 0
End Function
Function LastCol(sh As Worksheet)
On Error Resume Next
LastCol = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
On Error GoTo 0
End Function
Sub CopyRangeFromMultiWorksheets()
Dim sh As Worksheet
Dim DestSh As Worksheet
Dim Last As Long
Dim CopyRng As Range
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
Set DestSh = ActiveWorkbook.Worksheets("test")
' Loop through all worksheets and copy the data to the
' summary worksheet.
For Each sh In ActiveWorkbook.Worksheets
If sh.Name Like "test*" Then
Last = LastRow(DestSh)
' Specify the range to place the data.
Set CopyRng = sh.Range("A11:AI15")
CopyRng.Copy
With DestSh.Cells(Last + 1, "A")
.PasteSpecial
Application.CutCopyMode = False
End With
DestSh.Cells(Last + 1, "A").Resize(CopyRng.Rows.Count).Value = sh.Range("F8")
DestSh.Cells(Last + 1, "AK").Resize(CopyRng.Rows.Count).Formula = "=AG10*3%"
DestSh.Cells(Last + 1, "AL").Resize(CopyRng.Rows.Count).Formula = "=AG10+AK10"
End If
Next
ExitTheSub:
Application.Goto DestSh.Cells(1)
DestSh.Columns.AutoFit
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
Regards,
Here is a function that will return you the range. Parameters explained:
oW = The worksheet you want to get the range from
sStartColHeader = Holds the name of the header column you want to start the range from (i.e. in your example this would be "Language")
Function GetRange(ByVal oW As Worksheet, ByVal sStartColHeader As String) As Range
Dim oTotRng As Range: Set oTotRng = oW.Cells.Find("total", oW.Cells(1, 1), xlValues, xlPart, xlByRows, xlNext, False, , False)
Dim oLan As Range
Set oLan = oW.Cells.Find(sStartColHeader, oW.Cells(1, 1), xlValues, xlPart, xlByRows, xlNext, False, , False)
If oLan Is Nothing Then
Set GetRange = Nothing
Else
Set GetRange = Range(oLan.Offset(1, 0), oTotRng.Offset(0, 1))
End If
End Function
How to use this function:
In CopyRangeFromMultiWorksheets function, change Set CopyRng = sh.Range("A11:AI15") to Set CopyRng = GetRange(sh, "Language"). Then have an If condition to check if a range was returned. For example:
Set CopyRng = GetRange(sh, "Language")
If CopyRng Is Nothing Then
' your exception code here as range was not returned
Else
' rest of your code here as a range was returned
End If
NOTE: Presumption is that the actual total amount in the sheet is in the cell to the right of the cell that contains the text Total. So if "H10" has text Total, actual total is held in cell "I10"
I have found a solution that works great. See the code below:
Sub CopyRangeFromMultiWorksheets()
Dim sh As Worksheet
Dim DestSh As Worksheet
Dim Last As Long
Dim CopyRng As Range
Dim findrow As Long, findrow2 As Long
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
Set DestSh = ThisWorkbook.Worksheets("Summary")
For Each sh In ActiveWorkbook.Worksheets
If sh.Name <> DestSh.Name Then
Last = LastRow(DestSh)
'this method doesn't work with merged cells thhat is why I have to unmerge them first.
sh.Range("B10:B200").UnMerge
findrow = sh.Range("B:B").Find("Language Pair", sh.Range("B1")).Row
findrow2 = sh.Range("B:B").Find("Total", sh.Range("B" & findrow)).Row
Set CopyRng = sh.Range("A" & findrow + 1 & ":AJ" & findrow2 - 1)
CopyRng.Copy
With DestSh.Cells(Last + 1, "B")
.PasteSpecial
Application.CutCopyMode = False
End With
DestSh.Cells(Last + 1, "A").Resize(CopyRng.Rows.Count).Value = sh.Range("F8")
DestSh.Cells(Last + 1, "AK").Resize(CopyRng.Rows.Count).Formula = "=AG10*3%"
DestSh.Cells(Last + 1, "AL").Resize(CopyRng.Rows.Count).Formula = "=AG10+AK10"
End If
Next
ExitTheSub:
Application.Goto DestSh.Cells(1)
DestSh.Columns.AutoFit
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
Function LastRow(sh As Worksheet)
On Error Resume Next
LastRow = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
On Error GoTo 0
End Function
Function LastCol(sh As Worksheet)
On Error Resume Next
LastCol = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
On Error GoTo 0
End Function
Related
I want to loop or find multiple value in another sheets. My code doesn't work even after I do..loop the code.
For i = 1 To lastrowBAU
Worksheets(fname).Range("A1:A" & lastrowsheet).Select
Do Until Cell Is Nothing
Set Cell = Selection.find(What:=ThisWorkbook.Worksheets("BAU").Range("A" & i).Value, After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByRows, _
MatchCase:=False)
If Not Cell Is Nothing Then
Cell.Activate
ActiveCell.Copy
ActiveCell.Insert Shift:=xlShiftDown
ActiveCell.Offset(1, 0).Select
Selection.Replace What:=ThisWorkbook.Worksheets("BAU").Range("A" & i).Value, _
replacement:=ThisWorkbook.Worksheets("BAU").Range("B" & i).Value, _
LookAt:=xlPart, SearchOrder:=xlByRows, MatchCase:=False
Set Cell = Worksheets(fname).Range("A1:A" & lastrowsheet).FindNext(Cell)
End If
Loop
Next i
You need to set the cell before entering the loop
Set cell = rngSrc.Find(sA, LookIn:=xlFormulas, LookAt:=xlPart, _
After:=rngSrc.Cells(rngSrc.Cells.Count), SearchOrder:=xlByRows, MatchCase:=False)
If Not cell Is Nothing Then
however you also need to avoid an endless loop by checking if the search has returned to the first one found.
Option Explicit
Sub macro1()
Dim ws As Worksheet, wsBAU As Worksheet
Dim cell As Range, rngSrc As Range
Dim fname As String, lastrow As Long, lastrowBAU As Long
Dim i As Long, n As Long, first As String
Dim sA As String, sB As String
fname = "Sheet1"
With ThisWorkbook
Set ws = .Sheets(fname)
Set wsBAU = .Sheets("BAU")
End With
With ws
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
Set rngSrc = .Range("A1:A" & lastrow)
End With
With wsBAU
lastrowBAU = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
With ws
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
Set rngSrc = .Range("A1:A" & lastrow)
End With
' search and replace
Application.ScreenUpdating = False
For i = 1 To lastrowBAU
sA = wsBAU.Cells(i, "A")
sB = wsBAU.Cells(i, "B")
Set cell = rngSrc.Find(sA, LookIn:=xlFormulas, LookAt:=xlPart, _
After:=rngSrc.Cells(rngSrc.Cells.Count), SearchOrder:=xlByRows, MatchCase:=False)
If Not cell Is Nothing Then
first = cell.Address
Do
' insert cell above
cell.Insert xlDown
cell.Offset(-1).Value2 = cell.Value2
cell.Value2 = Replace(cell.Value2, sA, sB)
' expand search range
n = n + 1
Set rngSrc = ws.Range("A1:A" & lastrow + n)
' find next
Set cell = rngSrc.FindNext(cell)
Loop While cell.Address <> first
End If
Next
Application.ScreenUpdating = True
MsgBox n & " replacements", vbInformation
End Sub
I am making a macro that Optimizes the sheet by deleting unused ranges that create very large file sizes. It does this by finding the last used row (column), selecting a range from that last used row (column) to the very bottom-right) of the sheet, and clearing formats and deleting those cells, to delete the unused range that is taking up space.
E.g. if last used row is 50, select range A50 to Bottom right of sheet (aka XFD104873, clear those formats and delete range
I have been able to do this with rows, but not with columns. In the below code, I get a syntax error (shown as 'SYNTAX ERROR' below) when case 2 runs, and I can't for the life of me figure out why.
I need to use R1C1 notation but for some reason the range(cells(#,#)) aren't picking it up properly.
I think it has to do with the second part in which I do range(cells(#,#)).End(xlDown).end(Toright)
Let me know if i can provide any additional information!
Nick
'Option Explicit
Sub Optimize()
'Call OptimizeSheet(1, "HR_Data")
Call OptimizeSheet(2, "DomesticAsset_Data")
'Call OptimizeSheet(3, "InternationalAsset_Data")
End Sub
Sub OptimizeSheet(ByVal choice As Long, ByVal sht As String)
' 1 = Rows
' 2 = Columns
' 3 = Both
If WorksheetExists(sht) = False Then
MsgBox "Worksheet doesn't exist, check macro code"
Exit Sub
End If
'Workbook
Dim wb As Workbook
'Last Row and Column Variables
Dim lr As Long
Dim lc As Long
'File Size variables
Dim aFileSize As Long
Dim bFileSize As Long
Dim chngFileSize As Long
Set wb = Application.ActiveWorkbook
On Error GoTo errHandler
'Get file size before optimizing
aFileSize = FileLen(Application.ActiveWorkbook.FullName)
Select Case choice
'Rows
Case 1:
lr = Worksheets(sht).Cells.Find(What:="*", _
After:=Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
With Worksheets(sht).Range("A" & lr, Range("A" & lr).End(xlDown).End(xlToRight))
.ClearFormats
.Delete
End With
'Columns
Case 2:
lc = Worksheets(sht).Cells.Find(What:="*", _
After:=Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
SYNTAX ERROR HERE
With Worksheets(sht).Range(Cells(1, lc), RangeCells(1, lc).End(xlDown).End(xlToRight))
.ClearFormats
.Delete
End With
'Both
Case 3:
lr = Worksheets(sht).Cells.Find(What:="*", _
After:=Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
With Worksheets(sht).Range("A" & lr, Range("A" & lr).End(xlDown).End(xlToRight))
.ClearFormats
.Delete
End With
'chnge
lc = Worksheets(sht).Cells.Find(What:="*", _
After:=Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
With Worksheets(sht).Range(Cells(1, lc).Address(), Range(Cells(1, lc).Address()).End(xlDown).End(xlToRight))
.ClearFormats
.Delete
End With
Case Else:
MsgBox "Wrong Choice, check macro code"
End Select
Application.ThisWorkbook.Save
bFileSize = FileLen(Application.ActiveWorkbook.FullName)
If aFileSize + bFileSize = 0 Then
MsgBox "error in filesize"
End If
chngFileSize = bFileSize - aFileSize
If chngFileSize = 0 Then
MsgBox (sht & " already optimized")
Else
MsgBox ("Done. " & (chngFileSize / 1000) & "MB Saved")
End If
Exit Sub
errHandler:
MsgBox "error on line" & Erl
End Sub
Function WorksheetExists(shtName As String, Optional wb As Workbook) As
Boolean
Dim sht As Worksheet
If wb Is Nothing Then Set wb = ThisWorkbook
On Error Resume Next
Set sht = wb.Sheets(shtName)
On Error GoTo 0
WorksheetExists = Not sht Is Nothing
End Function
To delete columns:
With Worksheets(sht)
.Range(.Cells(1, lc + 1), _
.Cells(1, lc +1 ).End(xlToRight)).EntireColumn.Delete
End With
Trying to paste underneath all the data on a different workbook but am getting application defined error
Edit:
Sub Matt_Liam()
Dim Rng As Range
Set Rng = Workbooks("orders (3).xlsx").Worksheets("Sheet3").Range("A1:AY300")
Rng.Copy
Dim i As Long
Dim s11 As Workbook
Set s11 = Workbooks("11 Production")
Set s11 = ActiveWorkbook
With Sheets("Sheet1")
If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
lastRow = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
Else
lastRow = 1
End If
End With
Rng.PasteSpecial
End Sub
The errors are here
Range(Selection, Selection.End(xlDown)).Select
And Here
Workbooks("11 Production.xlsx").Worksheets("Sheet1").Range("A" & i).Select
I am trying to accelerate my Excel VB Macro.
I have tried the 5 alternatives below.
But I wonder if I could shorten the execution further.
I found 2 alternatives in User Blogs which I could not get to work.
One alternative is also found in a User Blog but do not understand.
Sub AccelerateMacro()
'
' v1 052817 by eb+mb
' Macro to copy as fast as possible sheet from one workbook into another workbooks
' Declarations for variables are not shown to make code example more legible
' Macro is stored in and run from "DestinationWorkBook.xlsm"
StartTime = Timer
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Alternative = "First"
If Alternative = "First" Then
Workbooks.Open Filename:="SourceWorkBook.xls"
Cells.Select
Selection.Copy
Windows("DestinationWorkBook.xlsm").Activate
Sheets("DestinationSheet").Select
Range("A1").Select
ActiveSheet.Paste
Windows("SourceWorkBook.xls").Activate
ActiveWorkbook.Close
End If
If Alternative = "Second" Then
Workbooks.Open Filename:="SourceWorkBook.xls", ReadOnly:=True
Cells.Select
Selection.Copy
Windows("DestinationWorkBook.xlsm").Activate
Sheets("DestinationSheet").Select
Range("A1").Select
ActiveSheet.Paste
Workbooks("SourceWorkBook.xls").Close SaveChanges:=False
End If
If Alternative = "Third" Then
' I could not get this alternative to work
Workbooks.Open("SourceWorkBook.xls").Worksheets("SourceSheet").Copy
Workbooks.Open("DestinationWorkBook.xlsm").Worksheets("DestinationSheet").Range("A1").PasteSpecial
End If
If Alternative = "Fourth" Then
' I could not get this alternative to work
Workbooks.Open("DestinationWorkBook.xlsm").Worksheets("DestinationSheet").Range("A1") = Workbooks.Open("SourceWorkBook.xls").Worksheets("SourceSheet")
End If
If Alternative = "Fifth" Then
' I don't understand the code in this alternative
Dim wbIn As Workbook
Dim wbOut As Workbook
Dim rSource As Range
Dim rDest As Range
Set wbOut = Application.Workbooks.Open("DestinationWorkBook.xlsm")
Set wbIn = Application.Workbooks.Open("SourceWorkBook.xls")
With wbIn.Sheets("SourceSheet").UsedRange
wbOut.Sheets("DestinationSheet").Range("A1").Resize(.Rows.Count, .Columns.Count) = .Value
End With
SecondsElapsed = Round(Timer - StartTime, 2)
MsgBox "This code ran successfully in " & SecondsElapsed & " seconds", vbInformation
End Sub
Instead of using UsedRange, find the actual Last Row and Last Column and use that range. UsedRange may not be the range that you think it is :). You may want to see THIS for an explanation.
See this example (UNTESTED)
Sub Sample()
Dim wbIn As Workbook, wbOut As Workbook
Dim rSource As Range
Dim lRow As Long, LCol As Long
Dim LastCol As String
Set wbOut = Workbooks.Open("DestinationWorkBook.xlsm")
Set wbIn = Workbooks.Open("SourceWorkBook.xls")
With wbIn.Sheets("SourceSheet")
'~~> Find Last Row
lRow = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
'~~> Find Last Column
LCol = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
'~~> Column Number to Column Name
LastCol = Split(Cells(, LCol).Address, "$")(1)
'~~> This is the range you want
Set rSource = .Range("A1:" & LastCol & lRow)
'~~> Get the values across
wbOut.Sheets("DestinationSheet").Range("A1:" & LastCol & lRow).Value = _
rSource.Value
End With
End Sub
In my workbook I have a FrontPage sheet with a button. This button imports csv files. Each csv file is imported/copied to its own sheet (let's call them data sheets). This part is complete. In the second part I want to combine all these sheets into one summary sheet and then delete all the data sheets. The second part is almost completed. I only need to figure out how to delete the data sheets once they have been combined into the summary sheet.
Thanks!
This is the code so far:
Function LastRow(sh As Worksheet)
On Error Resume Next
LastRow = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
On Error GoTo 0
End Function
Function LastCol(sh As Worksheet)
On Error Resume Next
LastCol = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
On Error GoTo 0
End Function
Sub CopyDataWithoutHeaders()
Dim sh As Worksheet
Dim DestSh As Worksheet
Dim Last As Long
Dim shLast As Long
Dim CopyRng As Range
Dim StartRow As Long
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
Application.DisplayAlerts = False
On Error Resume Next
ActiveWorkbook.Worksheets("RDBMergeSheet").Delete
On Error GoTo 0
Application.DisplayAlerts = True
Set DestSh = ActiveWorkbook.Worksheets.Add
DestSh.Name = "RDBMergeSheet"
StartRow = 2
For Each sh In ActiveWorkbook.Worksheets
If sh.Name <> DestSh.Name Then
Last = LastRow(DestSh)
shLast = LastRow(sh)
If shLast > 0 And shLast >= StartRow Then
Set CopyRng = sh.Range(sh.Rows(StartRow), sh.Rows(shLast))
If Last + CopyRng.Rows.Count > DestSh.Rows.Count Then
MsgBox "There are not enough rows in the " & _
"summary worksheet to place the data."
GoTo ExitTheSub
End If
CopyRng.Copy
With DestSh.Cells(Last + 1, "A")
.PasteSpecial xlPasteValues
.PasteSpecial xlPasteFormats
Application.CutCopyMode = False
End With
End If
End If
Next
ExitTheSub:
Application.Goto DestSh.Cells(1)
DestSh.Columns.AutoFit
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
If you give the sheets convenient names, you can simply loop through all the sheets and delete those called Data[something] for example.
For i = 1 To ActiveWorkbook.Worksheets.Count
If Left(Worksheets(i).Name, 4) = "Data" Then
Application.DisplayAlerts = False
Worksheets(i).Delete
Application.DisplayAlerts = True
End If
Next
Looks like you already have 3/4 of the code (the loop, and name check) going on.
After copying what you need to copy, just add:
Application.DisplayAlerts = False
sh.Delete
Application.DisplayAlerts = True
This deletes the worksheet and removes the requirement for the user to accept / decline the deletion.
Looks like this would go immediately after this block:
CopyRng.Copy
With DestSh.Cells(Last + 1, "A")
.PasteSpecial xlPasteValues
.PasteSpecial xlPasteFormats
Application.CutCopyMode = False
End With