How can I speed up this loop - vba

I have a Vba code that is very slow on 25 sheets, I am wondering if this code can be speeded up in any way
Sub Obracun_place_OLP_NEAKTIVNO()
'
' Obracun_place_NOVI Makronaredba
'
Call Refresh_neto_TM
Application.ScreenUpdating = False
Sheets("PODUZEĆE_PLAĆA").Select
Range("B7:H7").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.ClearContents
Sheets("Neto plaća").Select
ActiveSheet.ListObjects("Tablica_Upit_iz_MS_Access_Database_14").Range. _
AutoFilter Field:=204, Criteria1:=Range("A2")
ActiveSheet.ListObjects("Tablica_Upit_iz_MS_Access_Database_14").Range. _
AutoFilter Field:=207, Criteria1:="<>"
Range("GV11:GZ11").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Sheets("PODUZEĆE_PLAĆA").Select
Range("B6:F6").Select
ActiveSheet.Paste
Sheets("Neto plaća").Select
Range("E11:F11").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Sheets("PODUZEĆE_PLAĆA").Select
Range("G6:H6").Select
ActiveSheet.Paste
Columns("B:H").Select
Columns("B:H").EntireColumn.AutoFit
Range("A2").Select
Sheets("Neto plaća").Select
ActiveSheet.ListObjects("Tablica_Upit_iz_MS_Access_Database_14").Range. _
AutoFilter Field:=207
ActiveSheet.ListObjects("Tablica_Upit_iz_MS_Access_Database_14").Range. _
AutoFilter Field:=204
Sheets("PODUZEĆE_PLAĆA").Select
Range("B5").Select
ActiveCell.FormulaR1C1 = "=COUNTIF((R[2]C:R[100]C),R[-4]C[-1])"
Range("E5").Select
ActiveCell.FormulaR1C1 = "=SUM(R[2]C:R[100]C)"
Range("E5").Select
Selection.AutoFill Destination:=Range("E5:F5"), Type:=xlFillDefault
Range("E5:F5").Select
Range("B6:H6").Select
Range(Selection, Selection.End(xlDown)).Select
ActiveWorkbook.Worksheets("PODUZEĆE_PLAĆA").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("PODUZEĆE_PLAĆA").Sort.SortFields.Add Key:=Range( _
"C7:C129"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
With ActiveWorkbook.Worksheets("PODUZEĆE_PLAĆA").Sort
.SetRange Range("B6:H129")
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Range("B7:H7").Select
Range(Selection, Selection.End(xlDown)).Select
With Selection.Interior
.Pattern = xlNone
.TintAndShade = 0
.PatternTintAndShade = 0
End With
Sheets("PLAĆA_SPISAK").Select
ActiveSheet.Range("$C$10:$G$60").AutoFilter Field:=1, Criteria1:="<>"
Sheets("PODUZEĆE_PLAĆA").Select
Range("B5").Select
Sheets("2001").Select
Range("A1").Select
Application.ScreenUpdating = True
End Sub

Getting Rid of Active and Select (Translating Macro-Recorder Code)
Not tested.
There is still much room for improvement but it should illustrate what it could look like.
It compiles but that doesn't mean it's gonna work. Give it a try and share some feedback.
Issues
If there is no match in the table, the code will fail.
If the data isn't 'nice' and has empty rows, the xlDown lines will fail.
Maybe it would be preferable to write the formulas in A1 style.
The Code
Option Explicit
Sub Obracun_place_OLP_NEAKTIVNO()
Application.ScreenUpdating = False
'Refresh_neto_TM '?
' Reference the workbook ('wb').
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Reference the source worksheet ('sws').
Dim sws As Worksheet: Set sws = wb.Worksheets("Neto plaća")
Dim stbl As ListObject
Set stbl = sws.ListObjects("Tablica_Upit_iz_MS_Access_Database_14")
' Reference the destination worksheet ('dws').
Dim dws As Worksheet: Set dws = wb.Worksheets("PODUZEĆE_PLAĆA")
' Clear the (old) destination data range (headers are in row 6).
With dws.Range("B7:H7")
.Range(.Cells, .End(xlDown)).ClearContents
End With
' Filter the source table.
With stbl
' Clear possible existing filters.
If .ShowAutoFilter Then
If .AutoFilter.FilterMode Then .AutoFilter.ShowAllData
Else
.ShowAutoFilter = True
End If
' Filter.
.Range.AutoFilter Field:=204, Criteria1:=CStr(sws.Range("A2").Value)
.Range.AutoFilter Field:=207, Criteria1:="<>"
End With
' Copy the data from the source to the destination worksheet.
With sws
With .Range("GV11:GZ11")
.Range(.Cells, .End(xlDown)).Copy dws.Range("B6:F6")
End With
With .Range("E11:F11")
.Range(.Cells, .End(xlDown)).Copy dws.Range("G6:H6")
End With
sws.Columns("B:H").EntireColumn.AutoFit
'Application.Goto sws.Range("A2") ' reset to initial selection
End With
' Clear the table filters.
stbl.AutoFilter.ShowAllData
With dws
' Reference the (new) destination range ('drg').
Dim drg As Range
With dws.Range("B6:H6")
Set drg = .Range(.Cells, .End(xlDown))
End With
' Write formulas.
Dim lfRow As Long: lfRow = drg.Rows.Count ' last formula row
.Range("B5").FormulaR1C1 _
= "=COUNTIF((R[2]C:R[" & lfRow & "]C),R[-4]C[-1])"
.Range("E5:F5").FormulaR1C1 = "=SUM(R[2]C:R[" & lfRow & "]C)"
' Sort by the 2nd column ('C').
With .Sort
.SortFields.Clear
.SortFields.Add _
Key:=drg.Columns(2), _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortNormal
.SetRange drg
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
' Apply formatting.
With drg.Resize(drg.Rows.Count - 1).Offset(1) ' 'drg' without headers
With .Interior
.Pattern = xlNone
.TintAndShade = 0
.PatternTintAndShade = 0
End With
End With
'Application.Goto .Range("B5") ' reset to initial selection
End With
' These are irrelevant, the second one probably not necessary!?
wb.Worksheets("PLAĆA_SPISAK").Range("C10:G60").AutoFilter 1, "<>"
'Application.Goto wb.Worksheets("2001").Range("A1")
Application.ScreenUpdating = True
End Sub

Related

Copying the first 100 resulted rows after applying a specific criteria to my table

Does anyone know how to take only the first 100 resulted rows after this:
=COUNTIFS(R2C9:R50000C9,RC[-1])>30?
Here is my code, which I recorded it.
Thank you in advance.
I hope someone to help me.
Sub philoly_3()
'
' philoly_3 Macro
'
Sheets("Graph data").Select
Range("J1").Select
Selection.NumberFormat = "0.00"
ActiveCell.FormulaR1C1 = "Criteria"
Range("J2").Select
ActiveCell.FormulaR1C1 = "=COUNTIFS(R2C9:R50000C9,RC[-1])>30"
Range("J2").Select
Selection.AutoFill Destination:=Range("J2:J50000")
Range("J2:J50000").Select
Range("A1").Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.Style = "Input"
Range("F2").Select
With ActiveWindow
.SplitColumn = 0
.SplitRow = 1
End With
ActiveWindow.FreezePanes = True
Selection.AutoFilter
ActiveSheet.Range("$A$1:$J$50000").AutoFilter Field:=10, Criteria1:="TRUE"
Range("A1").Select
Range(Selection, Selection.End(xlToRight)).Select
Range("A1:I1").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Sheets("All moments").Select
ActiveSheet.Paste
Cells.Select
Cells.EntireColumn.AutoFit
Range("F2").Select
With ActiveWindow
.SplitColumn = 0
.SplitRow = 1
End With
ActiveWindow.FreezePanes = True
Sheets("All moments").Select
Application.CutCopyMode = False
Sheets("All moments").Move Before:=Sheets(1)
Sheets("Graph data").Select
ActiveSheet.Range("$A$1:$J$50000").AutoFilter Field:=10
Range("I50000").Select
Selection.End(xlUp).Select
Range("I2").Select
Selection.AutoFilter
Columns("J:J").Select
Selection.Delete Shift:=xlToLeft
Range("B2").Select
philoly_11
End Sub
Maybe this can help you. Just modify to your needs and implement it in your code. For this, my data range was =B2:E481. My criteria YES/NO is in column E. I copied the first 100 ranges (B:D) that were yes in column E, and pasted them into Z2.
Sub COPY_100_FIRST_YES()
Dim i As Byte
Dim MyRanges As String
Dim MyYesRanges() As String
Dim MyFinalSelection As Range
Range("E2").Select 'first row of my YES/NO column
i = 0
Do Until ActiveCell.Value = "" Or i = 100 'Loop until you have 100 yes or there is no more data
If ActiveCell.Value = "yes" Then
i = i + 1
If MyRanges = "" Then
MyRanges = ActiveCell.Row
Else
MyRanges = MyRanges & "||" & ActiveCell.Row
End If
End If
ActiveCell.Offset(1, 0).Select
Loop
'Now I have all rows that are YES and I know the columns i want to copy, in my case is columns B to D, so
MyYesRanges() = Split(MyRanges, "||")
For i = 0 To UBound(MyYesRanges) Step 1
If i = 0 Then
Set MyFinalSelection = Range("B" & MyYesRanges(i) & ":D" & MyYesRanges(i))
Else
Set MyFinalSelection = Application.Union(MyFinalSelection, Range("B" & MyYesRanges(i) & ":D" & MyYesRanges(i)))
End If
Next i
MyFinalSelection.Copy
'Paste where you want them
Range("Z2").PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
End Sub

VBA Excel Unfiltering but not unhiding rows

I am writing a code to filter certain data and copy it. After which, I want to unfilter it to its original state. I am using the ActiveSheet.ShowAllData statement but that unhides all the hidden rows as well. Is there a set of code that allows me to unfilter my filtered data but not unhide any rows that were previously hidden?
Thanks for answering
EDIT: This is the code if it helps.
Sub CopyToAmortizing()
Dim tbl As Range
Dim VisibleCells As Integer
Dim lr As Long
Sheets("Template").Select
Columns("A:AZ").EntireColumn.Hidden = False
If Not ActiveSheet.AutoFilter Is Nothing Then Cells.AutoFilter
Range("A5:AB5").Select
Range(Selection, Selection.End(xlDown)).Select
Set tbl = Selection
ActiveSheet.Range("$A$3:$N$9999").AutoFilter Field:=1, Criteria1:= _
"Amortizing Item"
On Error GoTo Point2
VisibleCells = tbl.SpecialCells(xlCellTypeVisible).Rows.Count
If VisibleCells >= 1 Then
Range("A3").Select
Selection.End(xlDown).Activate
lr = ActiveCell.Row
Range("B3", Cells(lr, 12)).Select
Selection.Copy
Sheets("AmortizingItems").Select
Range("A2").Select
Selection.PasteSpecial Paste:=xlPasteValuesAndNumberFormats, Operation:= _
xlNone, SkipBlanks:=False, Transpose:=False
Application.CutCopyMode = False
Rows(2).EntireRow.Delete
Range("A2").Select
Sheets("Template").Select
End If
Point2:
ActiveSheet.ShowAllData
Columns("A:AZ").EntireColumn.Hidden = False
ActiveSheet.Outline.ShowLevels RowLevels:=0, ColumnLevels:=1
Range("A5").Select
End Sub
I use filter:
ActiveSheet.Range("$A$3:$N$9999").AutoFilter Field:=1, Criteria1:= _
"Amortizing Item"
And then: ActiveSheet.ShowAllData
The easy answer is to turn off AutoFilter by using
Sheets("YourSheetName").AutoFilterMode = False
Here is a sample where I add the hidden rows to an array, then re-hide after I am done with them...
Sub SampleHiddenRows()
Set hidrows = New Collection
Set Rng = Range(Cells(5, 4), Cells(13, 5))
For Each cll In Rng
If cll.EntireRow.Hidden = True Then
hidrows.Add cll.Row
End If
Next cll
Rng.AutoFilter field:=1, Criteria1:="one"
Rng.SpecialCells(xlCellTypeVisible).Copy
ActiveSheet.AutoFilterMode = False
For t = 1 To hidrows.Count
Rows(hidrows(t)).Hidden = True
Next t
End Sub

Runtime Error 91 when sorting

I am writing a subroutine to dynamically copy 2 columns from one sheet to another. These column lengths might change from one report to another.
Here is the code:
Sub getAnalystsCount()
Dim rng As Range
Dim dict As Object
Set dict = CreateObject("scripting.dictionary")
Dim varray As Variant, element As Variant
Set ws = ThisWorkbook.Worksheets("ReportData")
With ws
Worksheets("ReportData").Activate
Columns("E:E").Select
ActiveWorkbook.Worksheets("ReportData").AutoFilter.Sort.SortFields.Clear
ActiveWorkbook.Worksheets("ReportData").AutoFilter.Sort.SortFields.Add Key:= _
Range("E1"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
With ActiveWorkbook.Worksheets("ReportData").AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
lastrow = .Range("A" & .Rows.Count).End(xlUp).Row
'~~> Set First row
firstrow = 2
'~~> Set your range
Set rng = .Range("E" & firstrow & ":E" & lastrow)
varray = rng.Value
'Generate unique list and count
For Each element In varray
If dict.Exists(element) Then
dict.Item(element) = dict.Item(element) + 1
Else
dict.Add element, 1
End If
Next
End With
Set ws = ThisWorkbook.Worksheets("Analysts")
With ws
Worksheets("Analysts").Activate
'Paste report somewhere
ws.Range("A3").Resize(dict.Count, 1).Value = _
WorksheetFunction.Transpose(dict.Keys)
ws.Range("B3").Resize(dict.Count, 1).Value = _
WorksheetFunction.Transpose(dict.Items)
......
the error is in this line:
ActiveWorkbook.Worksheets("ReportData").AutoFilter.Sort.SortFields.Clear
Replace your below code
Columns("E:E").Select
ActiveWorkbook.Worksheets("ReportData").AutoFilter.Sort.SortFields.Clear
ActiveWorkbook.Worksheets("ReportData").AutoFilter.Sort.SortFields.Add Key:= _
Range("E1"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
With ActiveWorkbook.Worksheets("ReportData").AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
With the below code
Columns("E:E").Select
lastrow1 = .Range("E" & .Rows.Count).End(xlUp).Row
ActiveWorkbook.Worksheets("ReportData").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("ReportData").Sort.SortFields.Add Key:=Range("E1") _
, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("ReportData").Sort
.SetRange Range("E2:E" & lastrow1)
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
startCell = Range("A1").Address
endCell = Range("E100000").End(xlUp).Address
ActiveWorkbook.Worksheets("ReportData").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("ReportData").Sort.SortFields.Add Key:=Range("E1"), SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:=xlSortTextAsNumbers
With ActiveWorkbook.Worksheets("ReportData").Sort
.SetRange Range(startCell,endCell)
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Obviously this is rough, you will need to make it your own, but it will allow you to sort the E column which is what your initial code looks like it was trying to do.
The Range.Sort method can be used for a quick one column sort and discards much of the verbose code produced when recording a worksheet sort operation. Without an active AutoFilter, this is the better way to go.
Sub getAnalystsCount()
Dim el As Long, ws As Worksheet
Dim dict As Object
Dim varray As Variant
Set dict = CreateObject("scripting.dictionary")
'don't know what is in column E but this might be helpful
'dict.comparemode = vbTextCompare 'non-case-sensitive
Set ws = ThisWorkbook.Worksheets("ReportData")
With ws
'this is not necessary inside a With ... End With block
'Worksheets("ReportData").Activate
With .Range("A1").CurrentRegion
'this quick code line is all you need
.Cells.Sort Key1:=.Columns(5), Order1:=xlAscending, _
Orientation:=xlTopToBottom, Header:=xlYes
'resize to # of rows -1 × 1 column and shift 1 row down and over to column E
With .Resize(.Rows.Count - 1, 1).Offset(1, 4)
'store the raw values
varray = .Value2
End With
End With
End With 'done with the ReportData worksheet
'Generate unique list and count
'I prefer to work with LBound and UBound
For el = LBound(varray, 1) To UBound(varray, 1)
If dict.Exists(varray(el, 1)) Then
dict.Item(varray(el, 1)) = dict.Item(varray(el, 1)) + 1
Else
dict.Add Key:=varray(el, 1), Item:=1
End If
Next el
Set ws = ThisWorkbook.Worksheets("Analysts")
With ws
'this is not necessary inside a With ... End With block
'Worksheets("Analysts").Activate
'might want to clear the destination cell contents first if there is something there
if application.counta(.Range("A3:B3") = 2 then _
.Range("A3:B" & .Cells(Rows.Count, "B").End(xlUp).Row).ClearContents
'Paste report somewhere
.Range("A3").Resize(dict.Count, 1).Value = _
WorksheetFunction.Transpose(dict.Keys)
.Range("B3").Resize(dict.Count, 1).Value = _
WorksheetFunction.Transpose(dict.Items)
End With 'done with the Analysts worksheet
End Sub
I prefer to work with the LBound and UBound functions to determine the scope of an array.
When you are inside a With ... End With statement, use the . to note the parent worksheet and discard the Range .Activate method and ws variable.

Delete rows that DON'T meet VBA criteria

I have many worksheets with sequential linear xy data that vary in length. The objective is to delete all rows where x data is not divisible by 50. Below is the generated macro that uses a helper column to search for integers to be deleted.
Sub Divis50()
Sheets("VERT SCALES").Select
Range("C2").Select
ActiveCell.FormulaR1C1 = _
"=IF((OR((RIGHT(RC[-2],2)=""50""),(RIGHT(RC[-2],2)=""00""))),""YES"",""NO"")"
Range("C2").Select
Selection.AutoFill Destination:=Range("C2:C6062")
'sort filtered results
Range("C2").Select
ActiveWorkbook.Worksheets("VERT SCALES").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("VERT SCALES").Sort.SortFields.Add Key:=Range("C2") _
, SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("VERT SCALES").Sort
.SetRange Range("A2:C6062")
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
' scroll to first no and delete rows
Rows("123:123").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.ClearContents
'sort "A" back to consecutive numbers
Range("A2").Select
ActiveWorkbook.Worksheets("VERT SCALES").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("VERT SCALES").Sort.SortFields.Add Key:=Range("A2") _
, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("VERT SCALES").Sort
.SetRange Range("A2:C122")
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
'delete filtered column
Columns("C:C").Select
Selection.Delete Shift:=xlToLeft
End Sub
This will delete rows that don't equal a whole number when divided by 50
Sub Button1_Click()
Dim FrstRng As Range, Lrw As Long
Dim UnionRng As Range
Dim c As Range
Lrw = Cells(Rows.Count, "A").End(xlUp).Row
Set FrstRng = Range("A2:A" & Lrw)
For Each c In FrstRng.Cells
If Int(c / 50) / (c / 50) <> 1 Then
If Not UnionRng Is Nothing Then
Set UnionRng = Union(UnionRng, c) 'adds to the range
Else
Set UnionRng = c
End If
End If
Next c
UnionRng.EntireRow.Delete
End Sub
I would recommend a helper column that flags your data appropriately. Either through formula or VB.
Then use an Autofilter to select the the flags and then delete.
try here for sample code that will delete filtered data.
http://www.mrexcel.com/forum/excel-questions/460513-visual-basic-applications-code-delete-only-rows-filtered.html

Iteration in Excel VBA

It's been a while since I have used VBA on Excel.
I want to alphabetize the contents of each column on the sheet.
This is what I have:
Range("A1").Select
Range("A1:A19").Select
ActiveWorkbook.Worksheets("Sheet2").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet2").Sort.SortFields.Add Key:=Range("A1"), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Sheet2").Sort
.SetRange Range("A1:A19")
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Range("B1").Select
End Sub
How can I make this into a for loop that keeps going as long as the range is active?
Like this?
Option Explicit
Sub sample()
Dim i As Long
With Sheets("Sheet1")
For i = 1 To .UsedRange.Columns.Count
.Columns(i).Sort Key1:=.Cells(1, i), Order1:=xlAscending, _
Header:=xlGuess, OrderCustom:=1, MatchCase:=False, _
Orientation:=xlTopToBottom, DataOption1:=xlSortNormal
Next i
End With
End Sub
Here you go. This code assumes your data is laid out in some type of table format. Also, it assumes you want the entire column sorted (including blanks and such). If you want to make the range more specific or just set it with a hard reference adjust the code where I commented.
Sub sortRange()
Dim wks As Worksheet
Dim loopRange As Range, sortRange As Range
Set wks = Worksheets("Sheet1")
With wks
'can change the range to be looped, but if you do, only include 1 row of the range
Set loopRange = Intersect(.UsedRange, .UsedRange.Rows(1))
For Each cel In loopRange
Set sortRange = Intersect(cel.EntireColumn, .UsedRange)
With .Sort
.SortFields.Clear
.SortFields.Add Key:=sortRange
.SetRange sortRange
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Next
End With
End Sub