Sorting a table in Excel using VBA? - vba

I have tried to sort a table using the following VBA code. The code does select the correct column, and the column filter does get a small arrow in it, indicating that it is sorted. But the rows do not sort. They stay unsorted.
What is wrong?
Sub SortTableTest()
Dim tbl As ListObject
Dim columnToSortBy As Range
Set tbl = Sheets("PB").ListObjects("AI")
Set columnToSortBy = tbl.ListColumns(9).Range
'Sort table
With tbl.Sort
.SortFields.Clear
.SortFields.Add columnToSortBy, xlDescending
.Header = xlYes
.MatchCase = False
.Apply
End With
End Sub

First, you missed one parameter in .SortFields.Add method. What you need is:
.SortFields.Add columnToSortBy, xlSortOnValues, xlDescending
Second, question from you comment. Try with this kind of References:
Set columnToSortBy = Range(tbl.Name & "[[#All],[column name here]]")

Related

Table inserted new row still at the top even after sorting (VBA/Excel)

Just interrupting with a small sorting problem I have with a table:
I have a table in which I list some European government bonds:
Only the ISIN is necessary: COUNTRY and MATURITY are retrieved from a Bloomberg API
Say Italy issue a new BOND with Isin code : IT0005402117
I tried a VBA Macro that insert a new line with ISIN = IT0005402117
Then sort the table by 1st: COUNTRY, then 2nd: MATURITY
Here's the code:
Private Sub ButtonAddBondOK_Click()
FormAddBond.Hide
Dim Isin As String
'Declare Table
Dim MyTable As ListObject
Set MyTable = Range("BondBase").ListObject
Isin = FormAddBond.TextBoxIsin.Value
'Test that Isin doesn't exists
Dim r As Range, IsItThere As Range
Set r = MyTable.Range
Set IsItThere = r.Find(What:=Isin, After:=r(1))
If Not IsItThere Is Nothing Then
MsgBox "ISIN already exist, please check."
'Cleaning form
Unload FormAddBond
Exit Sub
End If
'Inserting new row
Dim NewRow As ListRow
Dim IsinCol, FutCol As Long
IsinCol = MyTable.ListColumns("Isin").Range.Column
Set NewRow = MyTable.ListRows.Add(AlwaysInsert:=True)
With NewRow
.Range(IsinCol).Value = Isin
End With
'waiting 3 seconds
Application.Wait (Now + TimeValue("0:00:03"))
'Sorting Table
With MyTable.Sort
.SortFields.Clear
.SortFields.Add Key:=Range("BondBase[[#ALL],[COUNTRY]]"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.SortFields.Add Key:=Range("BondBase[[#ALL],[MATURITY]]"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortTextAsNumbers
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
'Cleaning form
Unload FormAddBond
End Sub
After running the code, the row is properly insert and table is properly sorted, BUT for the new row that appears at the top. this even though I added a 3 seconds wait for Bloomberg API to retrieve the values...
Result:
Any clue what I have done wrong please?

Sort Range by Date Value in Column

The following code below execute fine but the output of the sort function is a mess.
Intention of code is to sort range of values from B2 to last row of L
According to DD/MM/YYYY HH:MM:SS value
Code is used to sort a series of files in a folder, so a simple macro recording won't suffice
Dim Slrow As Long
'Updated last row count to Column B from comment made
Slrow = Cells(Rows.Count, 2).End(xlUp).Row
Range("B2:L" & Slrow).Sort Key1:=Range("B2:L" & Slrow), Order1:=xlAscending, Header:=xlNo
SortWb.Close SaveChanges:=True
Output of Code
File Download link available below ->
Download
Sorting with Custom Sort
The problem is the Key and the range where you apply it, and also the options (looks like your data got headers in row 1). Try something like this:
Range("B1:L" & Slrow).Sort Range("B2:B" & Slrow), xlAscending, , , , , , xlYes, , False, xlSortColumns, xlPinYin, xlSortNormal
Change the Key1 to the column you want to sort at eg Key1:=Range("B1").
use SortOn:=xlSortOnValues to make sure it sorts on the values not text (I think this does the trick here).
make it recognize headers automatically by using Header:=xlYes (easier).
This should work:
Dim ws As Worksheet
Set ws = ActiveSheet 'better specify Worksheets("SheetName")
With ws.Sort
.SortFields.Clear
.SortFields.Add2 Key:=ws.Range("B1"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.SetRange ws.Range("B1:L" & Slrow)
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
If it doesn't work your dates are no date values but text and you need to convert them into date values.
Also it might be a good idea always to use the ISO date format YYYY-MM-DD hh:mm:ss which is the only one that can not be misunderstood by humans and can easily be sorted even as text (eg in file names etc).
Example how it looks sorted with the code above:

Creating Macro button with multiple functions?

I am trying to sort my excel sheet by first and last name and I was wondering if it was possible for me to assign multiple macros to one button? Let's say I click once, it sorts by first name; click again and it sorts by last name. Is this possible? I asked as I couldn't find similar questions.
Here's my code
First Name
Columns ("D:D").Select
ActiveWorkbook.Worksheets(Sheet1") .Sort.SortFields.clear
ActiveWorkbook.Worksheets(Sheet1") .Sort.SortFields.Add Key:=Range("D1"),
Sorton::x=SortOnValues, Order:=xlDesending, DataOption:=xlSortNormal
with ActiveWorkbook.Worksheets("sheet1") .Sort
.SetRange Range("A1:K505")
Header = X1No
.MatchCase = False
.Orientation = x1TopToBottom
.SortMethod = x1PinYin
.Apply
End With
ActiveWindow.SmallScroll Down:=-495
End Sub
Obviously the other code is similar but with descending instead of ascending.
You can just set a global variable for the state and toggle it to have the macro bound to the button do different stuff. (this is assuming you want the function of the button to toggle after every click)
' global state variable
Dim State As Boolean
Private Sub Workbook_Open()
' initialize state
State = True
End Sub
Sub mymacro()
If State Then
' do stuff 1
Else
' do stuff 2
End If
' toggle state
State = Not State
End Sub
One button can only run one macro. However, you might let the macro do different things depending upon the current selection. For example, if the user selects a cell in the Name column the sort is done on the name, in ascending order, and if the selection is in another column it is done on the first name, in descending order.
The code below sets both the sort key and the sort order relative to the selection. Therefore you don't need two different macros.
Sub SortByColumn()
Dim Ws As Worksheet
Dim Clm As Long
Dim SortOrder As XlSortOrder
With Selection
Set Ws = .Worksheet
Clm = .Column
If Clm = 4 Then ' column #4 = column D
SortOrder = xlDescending
Else
' sort on column 1 by default
Clm = 1 ' column #1 = column A
SortOrder = xlAscending
End If
End With
With Ws.Sort
With .SortFields
.Clear
.Add Key:=Ws.Cells(1, Clm), _
Sorton:=xlSortOnValues, _
Order:=SortOrder, _
DataOption:=xlSortNormal
End With
.SetRange Ws.Range("A1:K505")
.Header = X1No
.MatchCase = False
.Orientation = x1TopToBottom
.SortMethod = x1PinYin
.Apply
End With
ActiveWindow.SmallScroll Down:=-495
End Sub
Note that the sorting action doesn't require anything to be selected.

How to use indirect reference to select a single cell or range in vba

I need simply a code for selecting a cell, however that cell to select changes. I have a cell in the workbook that will identify what cell it should be. Cell A1 contains the cell # that should be selected.
In this example cell A1 contains the word "P25", so I want the below code to reference A1 for the indirect cell ref to P25, thus select cell P25.
I tried both of these lines separately:
Sub IndirectCellSelect()
Sheet.Range(INDIRECT(A1)).Select
Range(INDIRECT(A1)).Select
End Sub
I get the error Sub or Function is not defined, when it gets to the word INDIRECT
A slight alteration to the posted code works:
Range([indirect("a1")]).Select
but I would advise to try either of these instead:
Sheet.Range(Sheet.Range("A1").Value).Select
Range(Range("A1")).Select
the first being more explicit and is recommended in production code.
You can do this a different way, but if you want to use a native Excel worksheet function within your VBA code, you need to do this like so (note that I have also adjusted how you reference A1):
Application.WorksheetFunction.Indirect(Sheets(1).Range("A1"))
Edit
Apologies - I hadn't test this. It seems that the Indirect function is not available in this context. Instead, try something like this:
Dim rng as Range
Set rng = sheets(1).Range("A1")
sheets(1).Range(rng.text).Select
Worksheets("list").Sort.SortFields.Add Key:=Range(INDIRECT("I16" & "3" & ":" & "I16" & "5002")) _
, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With Worksheets("list").Sort
.SetRange Range("B2:K5002")
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin

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.