Using VBA to sort by multiple column headers - excel

I am looking to sort columns by header names since the column where the header is may change using VBA. I found the below code but couldnt figure out how to make it sort by more than one field. Basically i am looking to have this filter by KEY, Status, Enrolled on, Completed on in that order.
Dim sortAdd As String
Dim sortRange As Range
'Find which column "KEY" appears in
On Error GoTo err_chk
Rows("1:1").Find(What:="KEY", After:=Range("A1"), LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Activate
On Error GoTo 0
sortAdd = ActiveCell.Address(0, 0)
'Set sort range by using current region
Range("A1").CurrentRegion.Sort _
key1:=Range(sortAdd), order1:=xlAscending, Header:=xlYes
Exit Sub
'Error handling
err_chk:
If Err.Number = 91 Then
MsgBox "No header row with title of KEY", vbOKOnly, "ERROR!"
Else
MsgBox Err.Number & ": " & Err.Description
End If
Recorded macro seemed to work but not really ideal.
Range("Deduped[[#Headers],[Key]]").Select
ActiveWorkbook.Worksheets("Deduped").ListObjects("Deduped").sort.SortFields. _
Clear
ActiveWorkbook.Worksheets("Deduped").ListObjects("Deduped").sort.SortFields. _
Add2 Key:=Range("Deduped[Key]"), SortOn:=xlSortOnValues, Order:= _
xlAscending, DataOption:=xlSortNormal
ActiveWorkbook.Worksheets("Deduped").ListObjects("Deduped").sort.SortFields. _
Add2 Key:=Range("Deduped[HR Status]"), SortOn:=xlSortOnValues, Order:= _
xlAscending, DataOption:=xlSortNormal
ActiveWorkbook.Worksheets("Deduped").ListObjects("Deduped").sort.SortFields. _
Add2 Key:=Range("Deduped[Enrolled on Date]"), SortOn:=xlSortOnValues, _
Order:=xlAscending, DataOption:=xlSortNormal
ActiveWorkbook.Worksheets("Deduped").ListObjects("Deduped").sort.SortFields. _
Add2 Key:=Range("Deduped[Completion Date]"), SortOn:=xlSortOnValues, Order _
:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Deduped").ListObjects("Deduped").sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With

Sort On Multiple Columns
Option Explicit
Sub SortOnMultipleColumns()
Dim SortHeaders As Variant
SortHeaders = Array("KEY", "Status", "Enrolled on", "Completed")
Dim SortOrders As Variant
SortOrders = Array(xlAscending, xlAscending, xlAscending, xlAscending)
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
Dim srg As Range: Set srg = ws.Range("A1").CurrentRegion
Dim shrg As Range: Set shrg = srg.Rows(1)
Application.ScreenUpdating = False
Dim ColumnIndex As Variant
Dim n As Long
For n = LBound(SortHeaders) To UBound(SortHeaders)
ColumnIndex = Application.Match(SortHeaders(n), shrg, 0)
If IsNumeric(ColumnIndex) Then
srg.Sort srg.Columns(ColumnIndex), SortOrders(n), , , , , , xlYes
Else
MsgBox "Could not find the header '" & SortHeaders(n) & "'.", _
vbCritical
End If
Next n
Application.ScreenUpdating = True
End Sub

Related

Error on defining Key:=Range("") for table filter

I'm working on a code to filter my table, but I'm struggling to set the 'Key:=Range("")' , so far I've tested a code without setting Dims and it works, but I want a more pratical approach, so the code will work in all worksheets (active worksheet) in my workbook.
Error im getting: Method range of object _'Global' failed. Error 1004
So on resume, im new on VBA and dont now how to set MyTable(Tbl) on the 'Key:=Range("Tbl[[#All],[Column1]]")
Sub MAKE_FILTER()
Dim ws As Worksheet
Dim wb As Workbook
Set wb = ThisWorkbook
Set ws = ActiveSheet
Dim Tbl As Object
Set Tbl = ws.ListObjects(1)
Tbl.Range.AutoFilter Field:=1, Criteria1:=RGB(255, 255, 0), Operator:=xlFilterCellColor
Tbl.Sort.SortFields.Clear
Tbl.Sort.SortFields.Add2 Key:=Range("Tbl[[#All],[DANFE]]"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With Tbl.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Tbl.Sort.SortFields.Clear
Tbl.Sort.SortFields.Add2 Key:=Range("Tbl[[#All],[Nº NF-e]]"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With Tbl.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
Without DIMs:
Sub Macro1() 'without DIMs
ActiveSheet.ListObjects("Tabela14212255").Range.AutoFilter Field:=1, _
Criteria1:=RGB(255, 255, 0), Operator:=xlFilterCellColor 'ok
ActiveWorkbook.Worksheets("NOVEMBRO 2022").ListObjects("Tabela14212255").Sort. _
SortFields.Clear 'ok
ActiveWorkbook.Worksheets("NOVEMBRO 2022").ListObjects("Tabela14212255").Sort. _
SortFields.Add2 Key:=Range("Tabela14212255[[#All],[DANFE]]"), SortOn:= _
xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("NOVEMBRO 2022").ListObjects("Tabela14212255"). _
Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
ActiveWorkbook.Worksheets("NOVEMBRO 2022").ListObjects("Tabela14212255").Sort. _
SortFields.Clear
ActiveWorkbook.Worksheets("NOVEMBRO 2022").ListObjects("Tabela14212255").Sort. _
SortFields.Add2 Key:=Range("Tabela14212255[[#All],[Nº NF-e]]"), SortOn:= _
xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("NOVEMBRO 2022").ListObjects("Tabela14212255"). _
Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
You can use:
Key:=tbl.Listcolumns("Nº NF-e").Range
to refer to the column by name.
Filter and Sort Tables (ListObjects)
Sub ApplyFilter()
Dim Headers() As Variant: Headers = Array("DANFE", "No NF-e")
' Reference 'ActiveSheet' and 'ThisWorkbook'.
If ActiveSheet Is Nothing Then Exit Sub ' no visible workbooks open
If Not TypeOf ActiveSheet Is Worksheet Then Exit Sub ' not a worksheet
Dim ws As Worksheet: Set ws = ActiveSheet
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
If Not ws.Parent Is wb Then
MsgBox "This only works for worksheets in the workbook containing " _
& "this code named '" & wb.Name & "' and located in '" _
& wb.Path & "'.", vbCritical
Exit Sub
End If
' Reference the table.
Dim lo As ListObject
On Error Resume Next
Set lo = ws.ListObjects(1) ' the first table
On Error GoTo 0
If lo Is Nothing Then
MsgBox "The worksheet '" & ws.Name & "' doesn't contain any tables.", _
vbExclamation
Exit Sub
End If
' Filter and sort.
With lo
If .ShowAutoFilter Then ' autofilter arrows are turned on
' Clear all filters.
If .AutoFilter.FilterMode Then .AutoFilter.ShowAllData
End If
.Range.AutoFilter 1, RGB(255, 255, 0), xlFilterCellColor
Dim lc As ListColumn, n As Long
For n = LBound(Headers) To UBound(Headers)
' Reference the column.
On Error Resume Next
Set lc = .ListColumns(Headers(n))
On Error GoTo 0
If lc Is Nothing Then
MsgBox "No column named '" & Headers(n) & "' in the table " _
& "named '" & .Name & "' of worksheet '" _
& .Parent.Name & "'.", vbCritical
Exit Sub
End If
' Sort by the column.
With .Sort
With .SortFields
.Clear
.Add2 lc.Range, xlSortOnValues, xlAscending, , xlSortNormal
End With
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Set lc = Nothing ' reset for the next iteration
Next n
End With
End Sub

Sort and ignore leading quotation marks

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

VBA Getting Runtime Error 91, but not every time? [duplicate]

This question already has answers here:
Cells.Find() Raises "Runtime Error 91: Object Variable or With Block Not Set"
(5 answers)
Closed 2 years ago.
When I skip through the sub where it shows the problem, this happens:
I can't seem to resolve the Issue since i use the code snippet from this sub multiple times and it works fine.. Code here
Private Sub UserForm_Initialize()
Sheets("Data").Range("A:T").AutoFilter Field:=18
Sheets("Data").Range("A:T").AutoFilter Field:=18, Criteria1:="FALSCH"
ActiveWorkbook.Worksheets("Data").AutoFilter.Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Data").AutoFilter.Sort.SortFields.Add Key:=Range( _
"I:I"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortTextAsNumbers
With ActiveWorkbook.Worksheets("Data").AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Dim lRow As Long
Dim lCol As Long
lRow = Cells.Find(What:="*", _
After:=Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
The structure should be something like this:
Dim ws As Worksheet
Set ws = Worksheets("Data")
Dim FoundAt As Range
Set FoundAt = ws.Cells.Find(What:="*", _
After:=ws.Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False)
If Not FoundAt Is Nothing Then
Dim lRow As Long
lRow = FoundAt.Row
'your other code here …
Else
MsgBox "Find failed!"
End If
First try to find the cell with ws.Cells.Find and reference it to a range variable FoundAt so you can test if a cell was found or not using If Not FoundAt Is Nothing Then, because .Row only exists if a cell was found. If you found nothing then nothing has no row (obviously).

Search and replace with 3 variables from one range with 3 cols

I am stuck with a rather complex search and replace function, which is made up of one large table with 3 columns defining the 1) sheet where to SnR, 2) search term, 3) replacement word. I am testing on a Test sheet with 10 entries, but cant get the script to process any of the cells in range...
Sub FART_find_and_replace_text()
Dim sheet_name, label_old, label_new As String
Dim i As Integer
Dim sheetrng As Range
Set sheet_rng = Range(Sheets("Test").Range("B65536").End(xlUp), Range(Sheets("Test").Range("B5"))) 'all queries
'Set sheet_name = Range(Sheets("Test").Range("B65536").End(xlUp), Range(Sheets("Test").Range("B5"))) 'sheet name
'Set label_old = Range(Sheets("Test").Range("D65536").End(xlUp), Range(Sheets("Test").Range("D5"))) 'search term
'Set label_new = Range(Sheets("Test").Range("E65536").End(xlUp), Range(Sheets("Test").Range("E5"))) 'replacement
i = 0 'set counter to 0
For Each cell In sheet_rng
With ThisWorkbook.Sheets(cell.Value)
.Activate
.Cells.Find(What:=cell.Offset(0, 2).Value, After:=ActiveCell, LookIn:=xlFormulas, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False _
, SearchFormat:=False).Activate
.ActiveCell.Replace What:=cell.Offset(0, 2), Replacement:=cell.Offset(0, 3), LookAt:=xlPart, SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:=False, _
ReplaceFormat:=False
End With
i = i + 1
Next
End Sub
I am running Office on MAC which I have been told is not great with VBA, but I sadly have no choice and appreciate all the help I can get.
Thank you
EDIT:
here a screenshot of my sample data
Note: my sheet name is in A, search value in C and replace value in D
Looks like your data is in B, D and E columns.
Following is the macro for A, C and D columns.
Sub FindReplaceMutiValMutiSheet()
Dim FRdata As Range, TempSh As Worksheet, TempData As Range, FRarr
LRow = Range("A" & Rows.Count).End(xlUp).Row
Set FRdata = Sheets("Sheet1").Range("A1:D" & LRow)
'Copy FRData to temporary sheet and assign it to TempData
Set TempSh = Sheets.Add(, Sheets(Sheets.Count))
TempSh.Range("A1").Resize(FRdata.Rows.Count, FRdata.Columns.Count).Value = FRdata.Value
Set TempData = TempSh.Range("A1:D" & LRow)
'Sort TempData on "Sheet Name" (A) and "Search" (C) Columns
With TempSh.Sort
.SortFields.Add2 Key:=Range(TempData(1, 1), TempData(TempData.Rows.Count, 1)) _
, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.SortFields.Add2 Key:=Range(TempData(1, 3), TempData(TempData.Rows.Count, 3)) _
, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.SetRange Range(TempData.Address)
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
'Array of TempData Values
FRarr = TempData.Value
'Delete TempSh as no longer required.
Application.DisplayAlerts = False
TempSh.Delete
Application.DisplayAlerts = True
'Find and replace loop basded on FRarr array
For i = 2 To UBound(FRarr, 1)
If ActiveSheet.Name <> FRarr(i, 1) Then Sheets(FRarr(i, 1)).Activate
Range("A1").Activate
Cells.Replace What:=FRarr(i, 3), Replacement:=FRarr(i, 4), LookAt:= _
xlPart, SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:=False, _
ReplaceFormat:=False
Next
End Sub
Tested it on following sample data; works fine.

do while loop based on a value available in cell (find) in vba

Hi i am writing a code in vb to check a particular value in a sheet, if the value is not available then it should go back to another sheet to take new value to find, if the value is found i have to do some operation on that sheet i have the below code to find the value in the sheet but if i pass the same in a DO WHILE loop as condition it gives a compile error
find vaue code
Selection.Find(What:=last_received, After:=ActiveCell, LookIn:= _
xlFormulas, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:= _
xlNext, MatchCase:=False, SearchFormat:=False).Activate
could some one please help me to write a code of DO WHILE with the above find in the loop condition so that if the condition gives false (i,e if the value is not found in the sheet) then i should use some other value to find
this is the code that i am going to put in loop
Sheets("Extract -prev").Select
Application.CutCopyMode = False
ActiveWorkbook.Worksheets("Extract -prev").Sort.SortFields.Clear 'sorting with tickets
ActiveWorkbook.Worksheets("Extract -prev").Sort.SortFields.Add Key:=Range( _
"C2:C2147"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
With ActiveWorkbook.Worksheets("Extract -prev").Sort
.SetRange Range("A1:AB2147")
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Application.Goto Reference:="R1C3" 'taking last received ticket
Selection.End(xlDown).Select
Selection.Copy
Sheets("Calc").Select
Application.Goto Reference:="Yesterday_last_received"
ActiveSheet.Paste
this code takes the last ticket but based on it's availablity in next sheet "extract" i have to take one ticket previous to the last one (and on).
Try below code :
Sub test()
Dim lastRow As Long
Dim rng As Range
Dim firstCell As String
lastRow = Sheets("sheet1").Range("A" & Rows.Count).End(xlUp).Row
For i = 1 To lastRow
Set rng = Sheets("sheet2").Range("A:A").Find(What:=Cells(i, 1), LookIn:=xlValues, lookAt:=xlPart, SearchOrder:=xlByRows)
If Not rng Is Nothing Then firstCell = rng.Address
Do While Not rng Is Nothing
rng.Offset(0, 1) = "found"
Set rng = Sheets("sheet2").Range("A:A").FindNext(rng)
If Not rng Is Nothing Then
If rng.Address = firstCell Then Exit Do
End If
Loop
Next
End Sub

Resources