How to do a sorting in VBA? - vba

I have a table with header and data in columns from A to D with changing row number (number of rows is more than 66800). I'd like to sort data from Z to A order by column C.
There are a lot of different solutions in VBA on internet, but none worked correctly for me.
My code gives me an error:
Sub SortDescending()
Dim lRow As Long
Dim lCol As Long
lRow = Sheets("atm_hh").Cells(Rows.Count, 1).End(xlUp).Row
lCol = Sheets("atm_hh").Cells(1, Columns.Count).End(xlToLeft).Column
With Sheets("atm_hh")
.Select
.Range("A2:" & Cells(lRow, lCol).Address).Sort Key1:=Range("C2"), _
Order1:=xlDescending, _
Header:=xlGuess, _
OrderCustom:=1, _
MatchCase:=False, _
Orientation:=xlTopToBottom
End With
End Sub

try
Sub SortDescending()
Dim sh As Worksheet
Set sh = Sheets("atm_hh")
Dim lRow As Long
Dim lCol As Long
lRow = sh.Cells(Rows.Count, 1).End(xlUp).Row
lCol = sh.Cells(1, Columns.Count).End(xlToLeft).Column
Dim cell As Range
For Each cell In sh.Range("C2:C" & lRow)
cell = WorksheetFunction.Substitute(cell, ",", ".")
Next
Columns("C:C").NumberFormat = "0.0"
sh.Cells.AutoFilter
sh.AutoFilter.Sort.SortFields.Clear
sh.AutoFilter.Sort.SortFields.Add Key:=Range("C1:C" & lRow), _
SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:=xlSortNormal
With sh.AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
sh.AutoFilterMode = False
End Sub

Related

run-time error 424 while checking change events

I have created a program which looks for items in Column A or Column I to change. If column I changes, it deletes and moves the row to a new sheet. If Column A changes, it should sort all of the data. However, when the second Application.Intersect(KeyCells2, Range(Target.Address)) is called, it errors out telling me I have a run-time error 424. Why is this happening? It seems to have both a key cells range and a target.address.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
Dim KeyCells2 As Range
Dim LastRowCompleted As Long
Dim RowToDelete As Long
Dim CurCell As String
RowToDelete = 0
LastRow = Sheets("Current").Cells(Sheets("Current").Rows.Count, "A").End(xlUp).Row
LastRowCompleted = Sheets("completed").Cells(Sheets("completed").Rows.Count, "A").End(xlUp).Row
LastRowCompleted = LastRowCompleted + 1 'Next row after last row
Set KeyCells = Range("I3:I16384")
Set KeyCells2 = Range("A3:A16384")
CurCell = ActiveCell.Address
If Not Application.Intersect(KeyCells, Range(Target.Address)) Is Nothing Then
Application.EnableEvents = False
'Cut and Paste Row
Target.EntireRow.Copy Sheets("completed").Range(LastRowCompleted & ":" & LastRowCompleted)
'Mark to delete row
RowToDelete = Target.EntireRow.Row
Call DeleteRow(RowToDelete)
Application.EnableEvents = True
End If
Range(CurCell).Select
If Not Application.Intersect(KeyCells2, Range(Target.Address)) Is Nothing Then
Application.EnableEvents = False
'Sort
MsgBox "lastrow completed: " & LastRow
Range("A3:Z" & LastRow).Select
ActiveWorkbook.Worksheets("current").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("current").Sort.SortFields.Add Key:=Range("A3:A" & LastRow) _
, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
ActiveWorkbook.Worksheets("current").Sort.SortFields.Add Key:=Range("B3:B" & LastRow) _
, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
ActiveWorkbook.Worksheets("current").Sort.SortFields.Add Key:=Range("E3:E" & LastRow) _
, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("current").Sort
.SetRange Range("A3:J" & LastRow)
.Header = xlGuess
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Range(CurCell).Select
Application.EnableEvents = True
End If
End Sub
Sub DeleteRow(Row As Long)
If Row > 0 Then
Rows(Row).EntireRow.Delete Shift:=xlUp
End If
End Sub
If you delete the row in the first If block then Target no longer exists, so you can't use it in your second If block.
As a fix you could exit the Sub after deleting the row.
P.S. - that "auto-sort" seems like it would be pretty annoying if you're trying to edit data...

Excel VBA auto-sort by date

I am new to writing macros or using VBA, and I have run into a problem that is hopefully easy to fix. I am currently working on a project in Excel that, after clicking a button with the macro attached to it, will allow me to copy and paste data from one master sheet to two others, based on a value (1a or 1b) in column L. The macro I have so far, which is included below, works well with the copy/paste element, but I would love to have the copied data auto-sort by date and time (column J) from oldest to newest when it is pasted into the destination sheet. The date/time format is MM/DD/YY HH:MM AM or PM.
Sub EGS_CVS_Sorting()
Dim lr As Long, lr2 As Long, r As Long
lr = Sheets("template").Cells(Rows.Count, "L").End(xlUp).Row
For r = lr To 2 Step -1
Select Case Sheets("template").Range("L" & r).Value
Case Is = "1a"
lr2 = Sheets("EGS lines").Cells(Rows.Count, "L").End(xlUp).Row
Sheets("template").Rows(r).Copy Destination:=Sheets("EGS lines").Range("A" & lr2 + 1)
Case Is = "1b"
lr2 = Sheets("CVS lines").Cells(Rows.Count, "L").End(xlUp).Row
Sheets("template").Rows(r).Copy Destination:=Sheets("CVS lines").Range("A" & lr2 + 1)
End Select
Next r
End Sub
Thank you!
did none of the 541000 findings on google for vba excel sort catch your interest?
Check if this leads you in the right direction please but adjust the range for the data you want to sort, check about the headers etc:
Sub EGS_CVS_Sorting()
Dim lr As Long, lr2 As Long, r As Long
lr = Sheets("template").Cells(Rows.Count, "L").End(xlUp).Row
For r = lr To 2 Step -1
Select Case Sheets("template").Range("L" & r).Value
Case Is = "1a"
lr2 = Sheets("EGS lines").Cells(Rows.Count, "L").End(xlUp).Row
Sheets("template").Rows(r).Copy Destination:=Sheets("EGS lines").Range("A" & lr2 + 1)
Case Is = "1b"
lr2 = Sheets("CVS lines").Cells(Rows.Count, "L").End(xlUp).Row
Sheets("template").Rows(r).Copy Destination:=Sheets("CVS lines").Range("A" & lr2 + 1)
End Select
Next r
With Sheets("EGS lines")
lr = .Cells(Rows.Count, "L").End(xlUp).Row
Range("A1:L" & lastrow).Sort key1:=Range("J1:J" & lr), _
order1:=xlAscending, Header:=xlYes
End With
With Sheets("CVS lines")
lr = .Cells(Rows.Count, "L").End(xlUp).Row
Range("A1:L" & lastrow).Sort key1:=Range("J1:J" & lr), _
order1:=xlAscending, Header:=xlYes
End With
End Sub
A good starting point for writing a macro to perform some task you haven't coded before is to simply record a macro doing the task you want done. So if I start with a sample data set that has a date in column J, I start recording a macro, sort the data by column J, stop the recording, and look at the code. I get this:
Sub Sorter()
'
' Sort Macro
'
'
Range("J1").Select
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Add Key:= _
Range("J1"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
With ActiveWorkbook.Worksheets("All Active Clients").Sort
.SetRange Range("F2:J23")
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
This manually codes that I've selected cell J1, then shows me the steps that the sort function goes through. From this, I can narrow down what I actually want to do. For example, selecting J1 is unnecessary, I don't need to worry about the .sortmethod, etc. I can trim the code down to something like the following:
Sub Sorter()
'
' Sort Macro
'
'
Const csDateSt As String = "J1"
Dim shtSort As Worksheet
Dim rngSort As Range
Set shtSort = Sheets("Sheet1")
Set rngSort = shtSort.UsedRange
With shtSort.Sort
.SortFields.Clear
.SortFields.Add Key:= _
Range(csDateSt), _
SortOn:=xlSortOnValues, _
Order:=xlAscending
.SetRange rngSort
.Header = xlNo
.Apply
End With
End Sub
All I have done is reorganise the recorded code, changed a few hard-coded values to constants and variables, and put it all in a With block. I can now use this as a roadmap to put the same kind of structure wherever I need it.
You could even keep your Sorting process as a separate Sub, and just call it when needed, passing it arguments to tell it where the data was, thus:
Sub Sorter(ByVal shtSort As Worksheet, ByVal rngSort As Range, ByVal strKey As String)
With shtSort.Sort
.SortFields.Clear
.SortFields.Add Key:= _
Range(strKey), _
SortOn:=xlSortOnValues, _
Order:=xlAscending
.SetRange rngSort
.Header = xlNo
.Apply
End With
End Sub
Then in your loop you would say something along the lines of:
Select Case Sheets("template").Range("L" & r).Value
Case Is = "1a"
lr2 = Sheets("EGS lines").Cells(Rows.Count, "L").End(xlUp).Row
Sheets("template").Rows(r).Copy Destination:=Sheets("EGS lines").Range("A" & lr2 + 1)
Case Is = "1b"
lr2 = Sheets("CVS lines").Cells(Rows.Count, "L").End(xlUp).Row
Sheets("template").Rows(r).Copy Destination:=Sheets("CVS lines").Range("A" & lr2 + 1)
End Select
Call Sorter(Sheets("EGS Lines"),Sheets("EGS Lines").range("A1").currentregion, "J1")
Call Sorter(Sheets("CVS Lines"),Sheets("CVS Lines").range("A1").currentregion, "J1")

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.

Sort Column by Specific Row

I would like to sort Columns E though "lastColumn" in ascending order.
The values to be used for sorting are in Row 14.
The data set is located in cells E8 to "lastColumn" "lastRow".
Below is what I have thus far, but I am getting an error that the reference is not valid. I'm guessing I am not using &lastRow& correctly, let alone trying to plug in the value for "lastColumn".
I am using lastColumn and lastRow as a means to ignore blank cells.
Sub SortColumns()
Dim lastColumn As Long
Dim lastRow As Long
lastColumn = Sheet1.Cells(8, Columns.Count).End(xlToLeft).Column
lastRow = Sheet1.Cells(Rows.Count, 1).End(xlUp).Row
ActiveWorkbook.ActiveSheet.Sort.SortFields.Clear
ActiveWorkbook.ActiveSheet.Sort.SortFields.Add Key:=Range( _
"E14:Z" & lastRow&), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.ActiveSheet.Sort
.SetRange Range("E8:I" & lastRow)
.Header = xlGuess
.MatchCase = False
.Orientation = xlLeftToRight
.SortMethod = xlPinYin
.Apply
End With
End Sub
I think you only had a small mistake. I added a little more code as you should always be specific on which workbook and on which sheet you are working. Try this:
Dim lastColumn As Long
Dim lastRow As Long
Dim Sht1 As Worksheet
Sht1 = ActiveWorkbook.Sheets("Sheet1")
lastColumn = Sht1.Cells(8, Sht1.Columns.Count).End(xlToLeft).Column
lastRow = Sht1.Cells(Sht1.Rows.Count, 1).End(xlUp).Row
Sht1.Sort.SortFields.Clear
Sht1.Sort.SortFields.Add Key:=Range( _
"E14:Z" & lastRow), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With Sht1.Sort
.SetRange Range("E8:I" & lastRow)
.Header = xlGuess
.MatchCase = False
.Orientation = xlLeftToRight
.SortMethod = xlPinYin
.Apply
End With

VBA code not sorting by CASE - also can't apply to multiple sheets

EDIT: I just noticed the VBA script isn't working at all, it looks like it is just sorting by the first column as I am getting some funny results :S?
I am using the following VBA to sort by all columns on the sheet.
Sub SortVariableColumns()
Dim strLastCol As String
Dim lngLastCol As Long
Dim sht As Worksheet
Set sht = ActiveSheet
With ActiveSheet
lngLastCol = sht.Cells.Find("*", SearchOrder:=xlByColumns, LookIn:=xlValues, SearchDirection:=xlPrevious).Column
strLastCol = Split(sht.Cells(1, lngLastCol).Address, "$")(1)
sht.Columns("A:" & strLastCol).Select
sht.Sort.SortFields.Clear
sht.Sort.SortFields.Add Key:=Range("A1"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal ', Header:=xlYes
End With
With sht.Sort
.SetRange Columns("A:" & strLastCol)
.Header = xlYes
.MatchCase = True
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Range("A1").Select
End Sub
However, it isn't matching case for some reason. The sort works for everything but the case of the words.
Also, is there anyway to make this then move onto the next sheet (i.e. if I selected activeworksheets) - I tried using
Dim sht As Worksheet
For Each ws In ActiveWindow.SelectedSheets
this but kept failing, I guess it has something to do with having to reset the IngLastCOl/StrLastCol holding from the first part of the VBA?
Many thanks.
What's with this as well?
Dim sht As Worksheet
For Each ws In ActiveWindow.SelectedSheets
Where have you defined ws variable? I am not suprised if this code fails at every line or never run at all. These are some fundamental issues.
#boncoDigo
I think I mispasted the code - the code wasn't actually failing on anything related to that! Sorry.
This can now be closed. I have worked out how to do it.
For those interested, this is the code that I used (can be easily amended to loop through sheets):
Sub SortVariableColumns()
Dim finalcolumn As Integer
Dim FinalRow As Integer
Dim sht As Worksheet
Set sht = ActiveSheet
sht.Sort.SortFields.Clear
With ActiveSheet
finalcolumn = Cells(1, Application.Columns.Count).End(xlToLeft).Column
FinalRow = Cells(Application.Rows.Count, 2).End(xlUp).Row
For N = 1 To finalcolumn Step 1
sht.Sort.SortFields.Add Key:=Range(Cells(2, N), Cells(FinalRow, N)), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
Next N
End With
With sht.Sort
.SetRange Range(Cells(1, 1), Cells(FinalRow, finalcolumn))
.Header = xlYes
.MatchCase = True
.Orientation = xlTopToBottom
.Apply
End With
End Sub