Custom sorting with custom ordering - vba

I want to sort my rows based on the values in column G. There are 3 possible values: Green, Red and Yellow. I want the rows sorted with Green on top, then Yellow, then Red.
Everything I try results in the sort order being alphabetical: Green, Red then Yellow. There is a secondary sort on column R, but that is working fine.
My latest code is below. rr is the number of the last row. I have tried it with and without Order1:=xlAscending.
sCustomList = "Green" "Yellow" "Red"
Application.AddCustomList ListArray:=sCustomList
Range("A3:T" & rr).Sort Key1:=Range("G3:G" & rr), Order1:=xlAscending, _
OrderCustom:=Application.CustomListCount + 1, MatchCase:=False, _
DataOption1:=xlSortNormal, Key2:=Range("R3:R" & rr), Order2:=xlAscending

Looking at your code, the sCustomList looks like a string type variable and not a variant array. My success with custom sort lists has been to create a new one every time and use the highest index number to reference it.
Sub custom_sort()
Dim vCustom_Sort As Variant, rr As Long
vCustom_Sort = Array("green", "yellow", "red", Chr(42))
Application.AddCustomList ListArray:=vCustom_Sort
With Worksheets("Sheet2") '<~~ set this properly!
.Sort.SortFields.Clear
rr = .Cells(Rows.Count, "G").End(xlUp).Row
With .Range("A3:T" & rr)
'use this to presort one or more secondary fields before the primary custom sort
'.Cells.Sort Key1:=.Columns(18), Order1:=xlAscending, _
Key2:=.Columns(1), Order2:=xlDescending, _
Orientation:=xlTopToBottom, Header:=xlYes
.Cells.Sort Key1:=.Columns(7), Order1:=xlAscending, _
Orientation:=xlTopToBottom, Header:=xlYes, MatchCase:=False, _
OrderCustom:=Application.CustomListCount + 1
End With
.Sort.SortFields.Clear
End With
End Sub
There is a twist between .Cells.Sort.SortFields.Add and .Cells.Sort that usually generates some confusion. The .SortFields.Add method uses a CustomOrder:= parameter and the Range.Sort method uses a OrderCustom:= parameter. The two are most definitely NOT the same but often get used interchangeably with disastrous results.

If you use the SortFields object, you don't have to refer to custom lists:
It should be obvious below where to change the various range references. I also added an alphabetical sort on one of the columns other than G
Option Explicit
Sub TrafficLightSort()
Dim WS As Worksheet
Dim rSortRange As Range, rSortKey As Range
Const sSortOrder As String = "Green,Yellow,Red"
Set WS = Worksheets("sheet1")
With WS
Set rSortRange = Range("E1", .Cells(.Rows.Count, "E").End(xlUp)).Resize(columnsize:=3)
Set rSortKey = rSortRange.Columns(3)
With .Sort.SortFields
.Clear
.Add Key:=rSortKey, _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
CustomOrder:=sSortOrder
.Add Key:=rSortRange.Columns(1), _
SortOn:=xlSortOnValues, _
Order:=xlAscending
End With
With .Sort
.SetRange rSortRange
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.Apply
End With
End With
End Sub

I would recommend adding a table with an explicit sorting order to your worksheet (green = 1, yellow = 2, etc). Then add a column to your sorting range that uses a lookup function to return the sorting value and use VBA to run a standard sort based on that column. This will leave you with VBA code that is easier for developers to read, allows Excel users who don't read VBA to see your sort order, and avoids burying hardcoded values deep in you macro.

Related

Sort from Largest to Smallest

I need to sort the table from Largest to Smallest as column Q.
codes gives no errors but doesnt work as well. It firstly filters well and sorts with no success:
introws = Range("A1").End(xlDown).Row
ActiveSheet.Range("$A$1:$q$" & introws).AutoFilter Field:=17, Criteria1:=">4", Operator:=xlFilterValues
Range("$A$1:$q$" & introws).Sort Key1:=Range("q:q"), Order1:=xlDescending
Range("q:q").Sort (xlDescending)
Both lines dont work, it just stays unsorted....
How to write this?
To sort a range value with filter, you need to apply specific method in a sequence, it involve clear sortfields > add key > sort.apply, the following is a simple sort for the range with data filter:
With Sheet1
.AutoFilter.Sort.SortFields.Clear
.AutoFilter.Sort.SortFields.Add2 Key:= _
Range("B1"), SortOn:=xlSortOnValues, Order:=xlDescending
.AutoFilter.Sort.Apply
End With
To apply the sort method in your case, you may try to following code modication, it should be work as expected, please perform a test for both case:
introws = Range("A1").End(xlDown).Row
ActiveSheet.Range("$A$1:$q$" & introws).AutoFilter Field:=17, Criteria1:=">4", Operator:=xlFilterValues
With ActiveSheet
.AutoFilter.Sort.SortFields.Clear
.AutoFilter.Sort.SortFields.Add2 Key:= _
Range("Q1"), SortOn:=xlSortOnValues, Order:=xlDescending
.AutoFilter.Sort.Apply
End With

excel vba: Can someone help me understand how to use a cusom sort order?

I've been working on a code to sort a block of data using Range.Sort using a custom sort order. I've tried to record macros and look online but came up more confused about this problem.
For the key1:= argument; is it a single cell (e.g. Range("A1")) or a whole column?
How exactly can I use a custom sort order in OrderCustom:=?
In the case I'm going about this all wrong; the Range in Range.Sort can be anywhere as long as its in one continuous block, correct?
Here is the code I'm working with:
Sub Test()
Dim quantity As Variant
quantity = 2 + WorkshetFunction.Count(Range("A" & 3, "K" & 900))
With Range("A" & 3, "K" & quantity)
.Sort key1:=Range("A" & 3)
Order:=xlAscending
Header:=xlNo
OrderCustom:="VALID, GOOD, DUE, OVERDUE, WAY OVERDUE, MISSING"
'> This is the order in which I want the items on this list sorted by.
End With
End Sub
The Range("A" & 3, "K" & quantity) refers to a block of data containing on "A" the "status" of some items determined by their calibration expiration dates, amongst other data which is irrelevant for this purpose, and I'm not in the liberty to share. All I'm asking is help understanding the inner workings of the .Sort method. Thanks!
Give'r
Sub SortItOut()
Dim rng As Range, sh As Worksheet
Set sh = Sheets("Sheet1")
With sh
Set rng = .Range("A3:K" & .Cells(.Rows.Count, "K").End(xlUp).Row)
With rng
sh.Sort.SortFields.Clear
sh.Sort.SortFields.Add Key:=Range("A3") _
, SortOn:=xlSortOnValues, Order:=xlAscending, CustomOrder:= _
"VALID,GOOD,DUE,OVERDUE,WAY OVERDUE,MISSING", DataOption:=xlSortNormal
End With
With .Sort
.SetRange rng
.Orientation = xlTopToBottom
.Apply
End With
End With
End Sub

In VBA (Excel) how can Column A insert an ID header followed by 1-X UNTIL it reaches the empty cell representing the highest row with data?

I am trying to create a macro that will insert a column, which is to be the first column in the spreadsheet (A) while shifting all original columns over 1 column to the right.
I then need this first column to create the header "ID" with each one numerically counting the rows:
[A]
ID
1
2
3
I only want the numbering to stop once it has reached the last relevant row in the spreadsheet. I was able to generate the following VBA by doing what I would normally do to accomplish this task while recording the macro and ended up with this:
Sub InsertID()
'
' InsertID Macro
' Add first column to be 1-##
'
' Keyboard Shortcut: Ctrl+Shift+N
'
Columns("A:A").Select
Selection.Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Range("A1").Select
ActiveCell.FormulaR1C1 = "ID"
Range("A2").Select
ActiveCell.FormulaR1C1 = "1"
Range("A3").Select
ActiveCell.FormulaR1C1 = "2"
Range("A2:A3").Select
Selection.AutoFill Destination:=Range("A2:A522")
Range("A2:A522").Select
Range("A1").Select
End Sub
Obvoiusly, this doesn't work for my situation. The template I was using only had 521 rows. This number is going to be a variable which can usually be determined by the number of rows in the original column A (Which is now column be after running this macro).
I have extensively looked into how to create a variable for number of rows in a specific column but have been unable to find a question that has similar enough parameters even though it seems so simple.
Thanks in advance
Try this...
Sub CreateIDColumn()
lr = ActiveSheet.UsedRange.Rows.Count
Columns(1).Insert
Range("A1").Value = "ID"
Range("A2:A" & lr).Formula = "=ROW()-1"
Range("A2:A" & lr).Value = Range("A2:A" & lr).Value
End Sub
I believe the following code will do what you want to do. It declares variables (in case Option Explicit is declared - which it should be), inserts the column, finds the last row (if there is one), and inserts relevant data.
Private Sub InsertID()
Dim lastrow, i As Integer 'declaring variables
'adding column
Range("A1").EntireColumn.Insert
'getting last row #
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
'setting value of cell A1
Cells(1, 1) = "ID"
'setting value for the rest of the cells in column A
For i = 2 To lastrow
Cells(i, 1) = i - 1
Next
End Sub

EXCEL VBA Sort after Populating Data

I am new to VBA, and excel macros, but not basic programming. I have a few dozen excel files, that I am taking data from, cleaning it, and populating it into one file. After the data is populated, I'd like to sort it according to Column A. After an 2 hours of playing with it, I just recorded a macro and cut and pasted it into my ButtonCall sub. But I'd like to know why its working and why the solutions I found here, and online would not work for me...
Why does this simple code NOT work:
Set q = ThisWorkbook.Worksheets(2)
LastRow = q.UsedRange.rows.Count 'q.UsedRange.Row ' - 1 + q.UsedRange.rows.Count
LastCol = q.UsedRange.Columns.Count
q.Range("A6:AAA" & LastRow).Sort Key:=q.Columns("A"), Order:=xlDescending
While this modified recorded Macro does?
Set q = ThisWorkbook.Worksheets(2)
LastRow = q.UsedRange.rows.Count 'q.UsedRange.Row ' - 1 + q.UsedRange.rows.Count
LastCol = q.UsedRange.Columns.Count
q.Sort.SortFields.Clear
q.Sort.SortFields.Add Key:=Range("A6:A" & LastRow), SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:=xlSortNormal
With q.Sort
.SetRange Range("A6:AAA" & LastRow)
.Header = xlGuess
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Any thoughts? Thanks.
Your code is using the range.sort method, while the original code is employing the sort object - two different things.
This code will sort "A6" to end of data by column A, using the Range.Sort method.
Sub MySort()
Dim q As Worksheet
Dim r As Range
Set q = ThisWorkbook.Worksheets(2)
' specify data range from "A6" to end of data
Set r = q.Range("A6", q.Cells.SpecialCells(xlCellTypeLastCell))
' Header:=xlNo assumes A6 row is included in data to be sorted
r.Sort key1:=r(1, 1), Order1:=xlDescending, Header:=xlNo
End Sub

Sort rows by a specific column containing numbers

How to sort rows by a column containing numbers in ascending or descending order?
I know how to sort using Filters and Using Sort function in VBA. But it sorts in alphabetical order only not by numbers.
This is so far I have done.But still the sorting is coming alphabetically.
Sub sortdata()
Dim LastRow As Integer
NoOfRows = Sheets("RawData").Range("A" & Rows.Count).End(xlUp).Row
Sheets("RawData").Rows("2:" & NoOfRows).NumberFormat = "0"
Sheets("RawData").Sort.SortFields.Add Key:=Range("A1"), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Sheets("RawData").Sort
.SetRange Range("A1:B" & NoOfRows)
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
I prefer the Range.Sort method:
[EDIT 2]:
I have added the .TextToColumns line to programmatically address the numbers stored as text issue
Sub sortdata()
Dim ws As Worksheet
Set ws = Sheets("RawData")
With ws.Range("A1:B" & ws.Cells(Rows.Count, "A").End(xlUp).Row)
Intersect(.Cells, ws.Columns("A")).TextToColumns
.Sort Intersect(.Cells, ws.Columns("A")), xlAscending, Header:=xlGuess
End With
End Sub
[EDIT]:
After reading asker's comment:
I will provide an example . If the column A contains this
values:1,2,55,12,14,5343,22222,9 Then after sorting using filter or
inbuilt sort method. The values are sorted as
follows:1,12,14,2,22222,5343,9. But I need the result as follows:
1,2,9,12,14,5334,22222. Is there any in-built function for this?
The problem is that your numbers are stored as text. You'll need to convert them to numbers.
Select column A -> Data -> Text to Columns -> Finish
Now the numbers should sort correctly.