This VBA code sorts my vinyl collection catalogue by any column by double-clicking the column header.
With my classical vinyl, nearly half of the song titles are in quotes and so when that column is sorted, it alphabetically sorts the titles with quotes first, then the titles without quotes.
Is there a way to add a line of code so that it will ignore the leading quotation marks when sorting so that "ac" comes after ab and so on?
My workaround for now uses a hidden helper column to strip the quotes but I am hoping for a cleaner solution.
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim KeyRange As Range
Dim ColumnCount As Integer
'Clear previous sorts
ActiveSheet.Sort.SortFields.Clear
'Clear contents of hidden helper column
Columns("K").ClearContents
'Copy and Paste songname column to helper column
Range("F:F").Copy Range("K:K")
'Strip quotes from helper column
Application.ScreenUpdating = FALSE
ActiveSheet.Columns("K").Replace What:="""", Replacement:="", LookAt:=xlPart, MatchCase:=False
Application.ScreenUpdating = TRUE
'Set range of header columns that will sort on double-click
ColumnCount = Range("A1:J1").Columns.Count
Cancel = FALSE
If Target.Row = 1 And Target.Column <= ColumnCount Then
Cancel = TRUE
'Get cell address of double-clicked header cell
Set SortColumn = Range(Target.Address)
'Set fill color of currently sorted column header
Rows(1).Interior.Color = xlNone
SortColumn.Interior.ColorIndex = 15
With ActiveSheet
'Sort by hidden column if songname column is double-clicked
If SortColumn = Range("F1") Then
Range("K1").Select
Else
SortColumn.Select
End If
'Sort by selected column followed by album, disc, then track
.Sort.SortFields.Add Key:=Selection, _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.Sort.SortFields.Add Key:=Range("E1"), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.Sort.SortFields.Add Key:=Range("B1"), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.Sort.SortFields.Add Key:=Range("C1"), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
End With
With ActiveSheet.Sort
'Set flexible sort range to all data before reaching entirely empty row or column
.SetRange Range("A1").CurrentRegion
.Header = xlYes
.MatchCase = FALSE
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End If
End Sub
Excel has always had this issue when sorting data. This is because it uses ASCII character codes to determine sort order (more details here: https://exceljet.net/excel-functions/excel-char-function). Special characters and punctuation have lower ASCII values than alphabet letters, so they get sorted at the top. This is by design.
There's no real way to "ignore" quotation marks when sorting, but one way to get around this is to remove all the quotes from the cells you are trying to sort. You could try adding this code right after your SET statement:
ActiveSheet.KeyRange.Cells.Replace _
What:="""", _
Replacement:="", _
LookAt:=xlPart, _
MatchCase:=False
WARNING!!! This will actually modify all your song titles (it will remove the double quotes from that column), so please back up your file before you try this.
One way is to Add a dummy Column K with F data, clear ", sort, erase column K...
I fixed the code accordingly
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim KeyRange As Range
Dim ColumnCount As Integer
Dim dstRng as Range ' placeholder 2 add column "K"
ActiveSheet.Sort.SortFields.Clear
ColumnCount = Range("A1:J281").Columns.Count
Cancel = False
If Target.Row = 1 And Target.Column <= ColumnCount Then
Cancel = True
Set KeyRange = Range(Target.Address)
Set dstRng = KeyRange.Resize(, 1).Offset(, KeyRange.Columns.Count - 1) 'Added column K
KeyRange.Resize(,1).Offset(,5).copy ' copy column F
dstRng.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
dstRng.Replace What:="""", Replacement:="", LookAt:=xlPart, _
SearchOrder:=xlByColumns, MatchCase:=False, SearchFormat:=False, _
ReplaceFormat:=False
With ActiveSheet ' columns F,B,C >> K,B,C
.Sort.SortFields.Add Key:=KeyRange, _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.Sort.SortFields.Add Key:=Range("K1"), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.Sort.SortFields.Add Key:=Range("B1"), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.Sort.SortFields.Add Key:=Range("C1"), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
End With
With ActiveSheet.Sort
.SetRange Range("A1:K281")
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
dstRng.ClearContents
End If
End Sub
As Always run 1st on a copy, protecting the original Excel file.
NOTES: I have used KeyRange to work on, but there might be a case that some set SrcRng = Range("A1:J281") is needed
Related
I have an array of data several rows down from the top. I have working VBA code that bounds the data with an Range and stores it in a variable. Then resizes to include a header row, clears the filter and then adds it back per the selected range.
At that point I want to sort the data per 3 rows
I use Cells.Find to find the column title I want and then sort the row and column into variables. I add 1 to the Column variable and store that as a variable for the next sort...
I've tested each of the sort commands and they work independently but I'm not able to get them to work in a combined order Date:, then Priority: then Status:.
Thanks for the help in advance.
Cells.Find(What:="Due:", After:=ActiveCell, LookIn:=xlFormulas, LookAt _
:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False).Activate
Dim DueRowNumber As Long
Dim DueColumnNumber As Long
DueRowNumber = ActiveCell.Row
DueColumnNumber = ActiveCell.Column
Dim PriorityRowNumber As Long
Dim PriorityColumnNumber As Long
PriorityRowNumber = ActiveCell.Row
PriorityColumnNumber = ActiveCell.Column + 1
Dim StatusRowNumber As Long
Dim StatusColumnNumber As Long
StatusRowNumber = ActiveCell.Row
StatusColumnNumber = ActiveCell.Column + 2
ActiveWorkbook.Worksheets("Action Items").AutoFilter.Sort.SortFields.Add Key _
:=Cells(DueRowNumber, DueColumnNumber), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption _
:=xlSortTextAsNumbers
ActiveWorkbook.Worksheets("Action Items").AutoFilter.Sort.SortFields.Add Key _
:=Cells(PriorityRowNumber, PriorityColumnNumber), SortOn:=xlSortOnValues, Order:=xlAscending, _
CustomOrder:="High,High-Med,Med,Med-Low,Low", _
DataOption:=xlSortNormal
ActiveWorkbook.Worksheets("Action Items").AutoFilter.Sort.SortFields.Add Key _
:=Cells(StatusRowNumber, StatusColumnNumber), SortOn:=xlSortOnValues, Order:=xlDescending, _
CustomOrder:="Late,In Work,Not Started,On Hold,Completed", _
DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Action Items").AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
After further investigation with additional data (added several more row with unique Due Dates, Priorities, and Status settings... the above code appears to work. I hope it helps someone with a similar issue.
I'm struggling to figure out how I can select all data, and sort based off of a column header name of "Service Ticket". There are plenty of examples on how to sort off of a "hard-coded" column. My issue is the imported data sheet often might have a different number of columns, causing the column of interest to change.
Sounds like you have already found VBA to sort by a given column. Use Find to locate the header you are interested in, then assign the column number to a variable and use that variable in place of a static column reference.
Sub SortSomeColumn()
Dim SvcTicketRange As Range
Dim SortColumn As Integer
With MyWorksheet
Set SvcTicketRange = .Rows(1).Find(What:="Service Ticket", After:=.Cells(1, 1), LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False)
If Not SvcTicketRange Is Nothing Then
SortColumn = SvcTicketRange.Column
.Sort.SortFields.Clear
.Sort.SortFields.Add Key:=Range(.Cells(1, SortColumn), .Cells(100, SortColumn)), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With .Sort
.SetRange Range("A1:E100")
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Else
'define what you want to do if the Service Ticket header is not found
End If
End With
End Sub
I currently have the following code that works and every time something is typed in, it is automatically sorted by column I
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
If Target.Column = 9 Then
Dim lastRow As Long
lastRow = ActiveSheet.Cells.Find(What:="*", LookIn:=xlValues,
SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
Range("A2:I" & lastRow).Sort Key1:=Range("I2:I" & lastRow),
Order1:=xlAscending, Header:=xlNo
End If
End Sub
I want it so that it is primarily sorted by item I, but also secondarily sorted by column A. Any help please?
The starting point for any request like this should be "Fire up the macro recorder, perform a sort, and see what code it spits out".
If you fire up the recorder and add a multilevel sort like so:
...then the Macro Recorder spits out this:
Sub Macro11()
'
' Macro11 Macro
'
'
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Add Key:=Range("I2:I16") _
, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Add Key:=Range("A2:A16") _
, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Sheet1").Sort
.SetRange Range("A1:I16")
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
This gives you a clue as to the syntax you need to add multiple sort keys. Do you need help amending your original code, or is this enough to get you started?
This macro is based off one I recorded, but have tweaked to cope with possible absence of 3 of the 4 sorting criteria. I can't figure out why my Macro works when the range criteria is specific, but not when I'm referencing a single cell and extrapolating.
With this line the sorting works
.SetRange Range("A1:W162")
With this line it doesn't sort.
.SetRange Range("A1").End(xlDown).End(xlToRight)
I've stepped through and can confirm it's selecting the correct range
I don't want use the line with specific cells because future exports will be different sized.
This is the full subroutine (the relevant bit is near the bottom).
Thanks!
Sub SortByScoreAndCost()
Dim Score As Range
Dim Cost As Range
Dim YN As Range
Dim OriginalScore As Range
Set Score = Cells.Find("Score")
Set Cost = Cells.Find("Cost")
Set YN = Cells.Find("Y/N")
Set OriginalScore = Cells.Find("Original Score")
Range("A1").CurrentRegion.Select
ActiveSheet.Sort.SortFields.Clear
ActiveSheet.Sort.SortFields.Add Key:=Range( _
Score.Offset(1, 0), Score.End(xlDown)), SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:= _
xlSortNormal
If Cells.Find("Cost") Is Nothing _
Then
Else: ActiveSheet.Sort.SortFields.Add Key:=Range _
(Cost.Offset(1, 0), Cost.End(xlDown)), SortOn:=xlSortOnValues, Order:=xlDescending, DataOption _
:=xlSortNormal
End If
If Cells.Find("Y/N") Is Nothing _
Then
Else: ActiveSheet.Sort.SortFields.Add Key:=Range _
(YN.Offset(1, 0), YN.End(xlDown)), SortOn:=xlSortOnValues, Order:=xlDescending, DataOption _
:=xlSortNormal
End If
If Cells.Find("Original Score") Is Nothing _
Then
Else: ActiveSheet.Sort.SortFields.Add Key:=Range _
(OriginalScore.Offset(1, 0), Original.End(xlDown)), SortOn:=xlSortOnValues, Order:=xlDescending, DataOption _
:=xlSortNormal
End If
With ActiveSheet.Sort
.SetRange Range("A1:W162") 'works with this line
.SetRange Range("A1").End(xlDown).End(xlToRight) 'doesn't work if replaced with this line
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
Range("A1").End(xlDown).End(xlToRight) refers to a single cell range, not similar to A1:W162. Assuming A1:W162 is the range where all the data on your sheet is. The line refers to W162 only. The Range.End property is explained here and it shows that it does not retain the starting point.
Try Range("A1:"& Range("A1").End(xlDown).End(xlToRight).Address) instead as this should create a range similar to A1:W162. The first part is the string "A1:" and Range("A1).End(xlDown).end(xlToRight).Address returns the string "$W$162" Together they form "A1:$W$162"
With the comment I made on your question in mind I would suggest the code below.
Dim wsData as Worksheet 'Add worksheet variable
Set wsData = ThisworkBook.Worksheets("Name of your data sheet")
With wsData.Sort 'Instead of using ActiveSheet
.SetRange wsData.Range(wsData.Cells(1, 1), wsData.Cells(1, 1).End(xlDown).End(xlToRight))
I have a range that length keeps changing. I need to redefine the named range each time it changes and then sort it.
I have this so far:
Sub Macro2()
'
' Name and Sort
'
Range("A3").Select
Range(Selection, Selection.End(xlDown)).Select
Range(Selection, Selection.End(xlToRight)).Select
ActiveWorkbook.Names.Add Name:="data4", RefersToR1C1:= _
"='Data Storage'!R3C1:R25C18"
ActiveWorkbook.Names("data4").Comment = ""
Application.Goto Reference:="data4"
ActiveWorkbook.Worksheets("Data Storage").sort.SortFields.Clear
ActiveWorkbook.Worksheets("Data Storage").sort.SortFields.Add Key:=Range( _
"D3:D25"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
With ActiveWorkbook.Worksheets("Data Storage").sort
.SetRange Range("A3:R25")
.Header = xlGuess
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
But the next time the range changes it only sorts on the previous range. I think it has to do with the R3C1:R25C18 reference but I don't know how to change that each time the range changes.
Thanks for any help.
This code checks for the last row with data, names the range, and sorts the named range
Dim lngRowLast As Long
'Find last row
lngRowLast = Cells.Find(What:="*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
'Your range goes here
Range(cells(3,1),cells(lngRowLast,18)).Name = "data4"
Range("data4").Sort Key1:=Cells(3, 1), Order1:=xlAscending, _
Header:=xlYes