How to combine reverse-chronological sheets while rearranging cells, preventing stacking? - vba

OK, I will try to explain this well. An odd problem to solve, and it's WAY beyond my skill level (novice).
Excel 2011 for Mac.
The workbook has 11 sheets.
Sheet names are all 'Month Year'. (Eg: sheet 1 is titled "June 2013")
Sheet names are reverse chronological. (June 2013, May 2013, April 2013 etc)
Each sheet has data in the same layout:
A1 is the sheet's name. B1 through to a varying endpoint on B hold dates. (approx two weeks but varies greatly between sheets)
A2 and downward in A is all names, as "last,first".
The remaining columns below B1 and outward are either blank, 0's, or 1's (attendance for date at row 1).
What I need to do:
Get ALL of the data in one sheet, with dates in chronological order along row 1 and names in alphabetical order down column A. Without messing up the associations between the names and the 0/1/blank values that existed on the original sheet.
What I have done:
I did this manually using the Excel GUI on a very similar sheet. It took forever!
I also have been writing or sourcing Subs to do some of the other work needed to these sheets to get them ready for this big rearranging. But I am already at my limits writing super simple "find rows with the word 'total' in them" sorts of stuff.
I know WHAT to do here, but have no clue HOW.
Start with the oldest sheet, copy into a new sheet(YearSheet).
Take names from 2ndOldest, paste into A under names already there.
Take dates and the cells beneath them into YearSheet, but staggered out on the columns so they begin where the first sheet left off.
Repeat again with nextYoungest, same deal. Names under names, dates and data shoved out along the letter axis to prevent overlap with prior dates.
Eventually it's a long list of names in A, and a descending step-pattern of data blocks in the remainder.
Sort it all by A, alphabetically.
Find and compress identical names into one row, without losing the data along the way (Why does Excel only keep top left? Aaargh!)
So, I know that's a lot to ask here.
Have no idea if this is too much or over the top for a question, but I am just stumped so am posting it in hopes somebody can make sense of the VBA to do it.

I created a workbook based on your description to use as sample data.
I wrote this macro
Sub Main()
Dim CombinedData As Variant
Dim TotalCols As Integer
Dim TotalRows As Long
Dim PasteCol As Integer
Dim PasteRow As Long
Dim i As Integer
Dim PivSheet As Worksheet
ThisWorkbook.Sheets.Add Sheet1
On Error GoTo SheetExists
ActiveSheet.Name = "Combined"
On Error GoTo 0
Range("A1").Value = "Name"
For i = ThisWorkbook.Sheets.Count To 1 Step -1
If Sheets(i).Name <> "Combined" Then
Sheets(i).Select
TotalCols = Sheets(i).Columns(Columns.Count).End(xlToLeft).Column
TotalRows = Sheets(i).Rows(Rows.Count).End(xlUp).Row
PasteCol = PasteCol + TotalCols - 1
If PasteRow = 0 Then
PasteRow = 2
Else
PasteRow = PasteRow + TotalRows - 1
End If
'Copy Date Headers
Range(Cells(1, 2), Cells(1, TotalCols)).Copy Destination:=Sheets("Combined").Cells(1, PasteCol)
'Copy Names
Range(Cells(2, 1), Cells(TotalRows, 1)).Copy Destination:=Sheets("Combined").Cells(PasteRow, 1)
'Copy Data
Range(Cells(2, 2), Cells(TotalRows, TotalCols)).Copy Destination:=Sheets("Combined").Cells(PasteRow, PasteCol)
End If
Next
Sheets("Combined").Select
ActiveSheet.Columns.AutoFit
With ActiveSheet.Sort
.SortFields.Clear
.SortFields.Add Key:=Range("A1"), _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortNormal
.SetRange ActiveSheet.UsedRange
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Set PivSheet = Sheets.Add
ThisWorkbook.PivotCaches.Create(SourceType:=xlDatabase, _
SourceData:=Sheets("Combined").UsedRange, _
Version:=xlPivotTableVersion14).CreatePivotTable _
TableDestination:=PivSheet.Range("A1"), _
TableName:="PivotTable1", _
DefaultVersion:=xlPivotTableVersion14
For i = 1 To PivSheet.PivotTables("PivotTable1").PivotFields.Count
With ActiveSheet.PivotTables("PivotTable1")
If i = 1 Then
.PivotFields(i).Orientation = xlRowField
.PivotFields(i).Position = 1
Else
ActiveSheet.PivotTables("PivotTable1").AddDataField .PivotFields(i), _
"Sum of " & .PivotFields(i).Name, _
xlSum
End If
End With
Next
Application.DisplayAlerts = False
Sheets("Combined").Delete
Application.DisplayAlerts = True
PivSheet.Name = "Combined"
CombinedData = ActiveSheet.UsedRange
Cells.Delete
Range(Cells(1, 1), Cells(UBound(CombinedData), UBound(CombinedData, 2))).Value = CombinedData
Range("A1").Value = "Name"
Range(Cells(1, 1), Cells(1, UBound(CombinedData, 2))).Replace "Sum of ", ""
Columns.AutoFit
Exit Sub
SheetExists:
Application.DisplayAlerts = False
Sheets("Combined").Delete
Application.DisplayAlerts = True
Resume
End Sub
Which produces this result:
This was written in Excel 2010 in windows. I don't know what the differences are between the pc and mac versions but this may work for you.

Related

Combine Print Ranges from Multiple Worksheets VBA EXCEL

I am trying to figure out how to print the "ActiveSheet" or Sheet1 along with "Sheet5" (rows 1-6, A:M) being displayed at the bottom with a 2 row space in between the end of Sheet1 and the beginning of data from Sheet5. I've been trying to look up similar questions and read something about a "Union" but I wasn't sure how it would fit here.
Private Sub CommandButton1_Click()
Dim Sel_Manager As String
'Headers repeated at the top
Application.PrintCommunication = False
With ActiveSheet.PageSetup
.PrintTitleRows = "$2:$2"
.PrintTitleColumns = "$B:$M"
.Orientation = xlLandscape
.Zoom = False
.FitToPagesWide = 1
.FitToPagesTall = 1
End With
'Manager selection through ComboBox dropdown
Sel_Manager = ComboBox1
'Inserting autofilters for worksheet
Cells.Select
Selection.AutoFilter
'Manager defined in the dropdown ComboBox
ActiveSheet.Range("B2", Range("M2").End(xlDown)).AutoFilter Field:=1, Criteria1:=Sel_Manager
ActiveSheet.Range("B2", Range("M2").End(xlDown)).AutoFilter Field:=2, Criteria1:="A"
'Here I select range to be printed and specify manager in filename
ActiveSheet.Range("B2", Range("M2").End(xlDown)).Select
Selection.ExportAsFixedFormat Type:=xlTypePDF, Filename:= _
Sel_Manager + ".pdf", Quality:=xlQualityStandard, _
IncludeDocProperties:=True, IgnorePrintAreas:=False, OpenAfterPublish:=True
ActiveSheet.ShowAllData
Application.PrintCommunication = True
End Sub
This may give you some ideas.
I created two worksheets. One, called "Main", contains data that has "names" in column B and some As in column C. The other, called "Extra" contains the six rows to appear at the bottom of the filtered data.
I do not use Excel's identifiers when referencing worksheets. The first sheet will have an identifier of Sheet1 and a name of "Sheet1". If you immediately create another sheet it will have an identifier of Sheet2 and a name of "Sheet2". However, if Sheet1 is renamed before the second sheet is created, it will have an identifier of Sheet2 and a name of "Sheet1". It can all get very confusing.
I have hardcoded the selected manager as "Aaaaa" rather than make it a user entered parameter. I have prepared worksheet "Main" for printing but have not output it.
Option Explicit
Sub Test()
Dim Manager As String
Dim RngFiltered As Range
Dim RowSht1Last As Long
Manager = "Aaaaa"
With Worksheets("Main")
.AutoFilterMode = False ' Switch off auto filtering if on
' Find last row containing a value
RowSht1Last = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByRows, xlPrevious).Row
With .Range("B2:M2")
.AutoFilter Field:=1, Criteria1:=Manager
.AutoFilter Field:=2, Criteria1:="A"
End With
Set RngFiltered = .Cells.SpecialCells(xlCellTypeVisible)
' Just to show the filtered range. Note, I cannot find a documented limit
' on the number of sub-ranges within a range. If there is a limit, I have
' never managed to reach it. However Range.Address has a limit which is a
' little under 255.
Debug.Print Replace(RngFiltered.Address, "$", "")
Worksheets("Extra").Range("A1:M6").Copy Destination:=.Cells(RowSht1Last + 2, "A")
' ###### Output visible rows
' Delete rows copied from Sheet5
.Rows(RowSht1Last + 2 & ":" & RowSht1Last + 8).Delete
End With
End Sub

Excel/VBA - Extracting a range of rows from a selected sheet to a new book

I'm trying to build a new VBA function for Excel. I've got a book of sheets with a front page that always loads first, on this page I've got a combo box that lists all the other sheets in the book and a nice extract button that will pull out the chosen sheet to a new book. (Thanks to those here who helped with that). Now I need a new function that will use the same combo box, but instead only extract a small subset of the chosen sheet.
Unfortunately, that subset isn't on the same rows for every sheet, nor is the number of rows the same (so one sheet, the subset might be 10 rows, on another it might be 12, on another it might be 20, etc etc etc).
On the plus side, there are merged rows (from column A to G) at the start and end of each subset - with specific text, which could be used to search for.
After some back and forth, I've got a better bit of code that I think is almost working:
Sub ZCPS_Extract()
Dim StartRow
Dim EndRow
Dim Zws As Worksheet
Dim wbkOriginal As Workbook
Set wbkOriginal = ActiveWorkbook
StartRow = 1
EndRow = 1
'sets site details into the header of the ZCPS checksheet
Worksheets(Sheet1.CmbSheet.Value).Range("B3").Value = Worksheets("front page").Range("E6")
Worksheets(Sheet1.CmbSheet.Value).Range("D3").Value = Worksheets("front page").Range("N6")
Worksheets(Sheet1.CmbSheet.Value).Range("F3").Value = Worksheets("front page").Range("K6")
Set Zws = Sheets(Sheet1.CmbSheet.Value)
'selects ZCPS block from select estate sheet
StartRow = (Zws.Cells.Find("**** ZCPS Installation").Row) + 1
EndRow = (Zws.Cells.Find("**** Aztec Hotfixes").Row) - 1
'copy above block and paste into Z-MISC starting at row 5
Worksheets(Sheet1.CmbSheet.Value).Range(Cells(StartRow, 1), Cells(EndRow, 7)).Copy Worksheets("Z-MISC").Range("A5")
With ActiveWorkbook.Sheets("Z-MISC")
.Copy
ActiveWorkbook.SaveAs _
"C:\temp\" _
& ActiveWorkbook.Sheets("Z-MISC").Cells(3, 2).Text _
& " ZCPS CheckSheet " _
& Format(Now(), "DD-MM-YY") _
& ".xlsm", _
xlOpenXMLWorkbookMacroEnabled, , , , False
End With
'code to close the original workbook to prevent accidental changes etc
Application.DisplayAlerts = False
wbkOriginal.Close
Application.DisplayAlerts = True
End Sub
It's error on the line for copying, I'm getting a runtime error of "Application-defined or object-defined error" which to my limited knowledge isn't helping me. Any assistance/pointers/suggestions are welcomed.
Sub ismerged()
Dim start As Integer, finish As Integer
For i = 1 To Range("A655").End(3).Row + 1
If Cells(i, "A").MergeCells = True Then
start = i
Exit For
End If
Next
For i = start To Range("A655").End(3).Row + 1
If Cells(i, "A").MergeCells = True Then
finish = i
End If
Next
MsgBox start
MsgBox finish
End Sub
Then I guess you can select your data as you wish.
I'm not sure about the way you reference your sheet. I will assume 'comboboxvalue' contains the name or the number of the sheet you are selecting. Your code should be something like the following.
Sub Z_Extract()
Dim StartRow
Dim EndRow
Dim ws As Worksheet
Set ws = Sheets(comboboxvalue)
StartRow = ws.Cells.Find("**** ZC").Row
EndRow = ws.Cells.Find("****").Row
'Im assuming you have values up to column G
ws.Range(ws.Cells(StartRow, 1), Cells(EndRow, 7)).Copy
'Now that you have the correct Range selected you can copy it to your new workbook
'SelectedRange.Copy Etc.....
'Cleanup
Set ws = Nothing
End Sub
Got it working.
Set Zws = Sheets(Sheet1.CmbSheet.Value)
'selects ZCPS block from selected estate sheet
StartRow = (Zws.Cells.Find("**** ZCPS Installation").Row)
EndRow = (Zws.Cells.Find("**** Aztec Hotfixes").Row) - 1
'copy above block and paste into Z-MISC starting at row 10
Sheets(Sheet1.CmbSheet.Value).Activate
ActiveSheet.Range(Cells(StartRow, 1), Cells(EndRow, 7)).Select
Selection.Copy
Sheets("Z-MISC").Select
Range("A10").Select
ActiveSheet.Paste

How do I extract data from a cell and order the cells alphabetically?

I'm trying to automate excel modificaions.
The process works like this:
The Excel list gets created.
It needs to be manually processed by an employee (removing images, sorted alphabetically, etc.)
The list gets converted into a csv file.
CSV gets uploaded and processed.
Now I would like to automate this process as much as possible. I don't have any experience working with VBA or Excel macros.
So far i've been able to scramble a few different scripts together to get halfway, but I haven't been able to get these two functions working.
I've been able to remove all the bloat at the top (not at the bottom yet), remove empty rows and remove unused columns.
I can't post the contents of the sheet itself because of privacy reasons, but the structure of the sheet looks like this:
| Name | Cost |
| Mark Renner (mare) | €200,- |
Question
I want to extract the 4 letter codes and replace them for the full names so only the 4 letter code remains in the cell.
Also I would like the list to be sorted alphabetically. The range of the sheet differs per day so there is no fixed ammount of cells.
There is nothing else on the sheet you need to worry about. I can provide more information if necessary.
It would be tremendous if someone is able to help me with this.
Thanks in advance!
Edit:
Here is some more requested information.
Table example after current script
This is the script I am currently using to remove all the bloat. I'm sure it's not perfect but it does the job for now.
Sub run()
Call testvba
Call DeleteRowWithContents
Call usedR
End Sub
Sub testvba()
Dim i As Integer
For i = 1 To 21
Rows(1).EntireRow.Delete
Next i
For i = 1 To 10
Columns(4).EntireColumn.Delete
Next i
Dim shape As Excel.shape
For Each shape In ActiveSheet.Shapes
shape.Delete
Next
End Sub
Sub DeleteRowWithContents()
Last = Cells(Rows.Count, "A").End(xlUp).Row
For i = Last To 1 Step -1
If (Cells(i, "A").Value) = "User" Then
Cells(i, "A").EntireRow.Delete
End If
Next i
End Sub
Sub usedR()
ActiveSheet.UsedRange.Select
'Deletes the entire row within the selection if the ENTIRE row contains no data.
Dim i As Long
'Turn off calculation and screenupdating to speed up the macro.
With Application
.Calculation = xlCalculationManual
.ScreenUpdating = False
'Work backwards because we are deleting rows.
For i = Selection.Rows.Count To 1 Step -1
If WorksheetFunction.CountA(Selection.Rows(i)) = 0 Then
Selection.Rows(i).EntireRow.Delete
End If
Next i
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub `
Here is the table before the script:
Before
SOLUTION:
I used Schalton's code for extracting the 4 letter code.
I ended up using this line of code to alphabetize the records:
Sub Alpha()
Dim fromRow As Integer
Dim toRow As Integer
fromRow = 1
toRow = ActiveSheet.UsedRange.Rows.Count
ActiveSheet.Rows(fromRow & ":" & toRow).Sort Key1:=ActiveSheet.Range("A:A"), _
Order1:=xlAscending, Header:=xlNo, OrderCustom:=1, _
MatchCase:=False, Orientation:=xlTopToBottom
End Sub
To get the 4 letter code you can search for the "(" and cut the string down
Something like this will get you to the code, you could use regedit but that seems like overkill
Sub ReplaceName()
LastRow = Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
For r = 2 to LastRow 'assumes data starts in row 2 with header in row 1
if Cells(r,1).value = "" then goto Nextr 'skips blanks
CurrentString = Cells(r,1).value 'assumes the names are in column 1
'At this point CurrentString = "Mark Renner (mare)"
CurrentString = Right(CurrentString,len(CurrentString)-instr(1,CurrentString,"("))
'At this point CurrentString = "mare)"
CurrentString = left(CurrentString,instr(1,CurrentString,")")-1)
'At this point CurrentString = "mare"
Cells(r,1).value = CurrentString
Nextr:
Next r
End Sub
As far as putting it in alphabetical order, there are two ways that come to mind
Move all of the values into an array then iterate through the array and sort them
Create a filtered range and filter is
the second option is MUCH easier, and for what you're doing I think it's probably fine. It'll look something like this:
With data that looks like this (in cells A1 to B6):
Name Cost
Tom 149
Dick 272
Harry 186
Moe 292
Larry 377
I'd do something like this:
Sub SortAlpha()
LastRow = Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
Range(Cells(1,1),Cells(LastRow,2)).select 'selects the data and headers
Selection.AutoFilter 'Adds Filter
ActiveWorkbook.ActiveSheet.AutoFilter.Sort.SortFields.Clear
Range(Cells(1, 1), Cells(LastRow, 1)).Select 'selects name column
'filters alpha
ActiveWorkbook.ActiveSheet.AutoFilter.Sort.SortFields.Add Key:=Selection _
, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
With ActiveWorkbook.ActiveSheet.AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Range(Cells(1,1),Cells(LastRow,2)).select 'selects the data and headers
Selection.AutoFilter 'Removes Filter
End Sub
That'll Give you this:
Name Cost
Dick 272
Harry 186
Lary 377
Moe 292
Tom 149
As Far as cleaning the data I usually do a couple of things when I have data tables that are really messy
Start Here:
1. iterate through the range and remove all merges
2. unwrap all of the text
3. delete all pictures
4. Delete any blank Rows or columns
I like this code for finding the last row:
LastRow = Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
You can modify it to find the last column
LastCol = Cells.Find("*", SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column
Then you can loop through your whole sheet cell by cell, or as a range
Cell by Cell: (I use this to unmerge the cells - can be slow)
For r = 1 to LastRow
For c = 1 to LastCol
'Do Stuff
Cells(r,c).UnMerge 'or Cells(r,c).MergeCells = False
Next c
Next r
or as a range: I use this for unwrapping the text
Range(Cells(1,1),Cells(LastRow,LastCol)).WrapText = False
To delete the pictures I use this code:
Deleting pictures with Excel VBA
Dim shape As Excel.shape
For Each shape In ActiveSheet.Shapes
shape.Delete
Next
This seems like it would work for saving the csv if you wanted to automate that also:
Saving excel worksheet to CSV files with filename+worksheet name using VB
I'd re-resize all of your rows and columns, rows to their default and columns to the fit:
Unfortunately I haven't found a great way to do the rows without a range string so my code is a little messy:
RowRange = "1:" & LastRow
Rows(RowRange).RowHeight = 12.75
Columns are about the same, but worse because they're not numbered
ColStart = Cells(1,1).Address
ColEnd = Cells(1,LastCol).Address
ColStart = left(ColStart,len(ColStart)-1)
ColEnd = left(ColEnd,len(ColEnd)-1)
ColStart = Replace(ColStart,"$","")
ColEnd = Replace(ColEnd,"$","")
ColRange = ColStart & ":" & ColEnd
Columns(ColRange).EntireColumn.AutoFit
you could alternatively just make it significantly large, but where's the fun in that?
Columns("A:ZZ").EntireColumn.AutoFit

VBA sort code works for one sheet only but not multiple ones

When I run this code over multiple sheets I get a "Run-Time Error 1004:The sort reference is not valid. Make sure that it's within the data that you want to sort, and the first Sort By box isn't the same or blank":
Dim i As Long
For i = 6 To Worksheets.Count
'more code here
Dim ranged As range
Dim lRow As Long
With ThisWorkbook.Sheets(i)
lRow = .range("AJ" & .Rows.Count).End(xlUp).Row
Set ranged = .range("AJ2:AJ" & lRow)
.Sort.SortFields.Add Key:=ranged, _
SortOn:=xlSortOnValues, _
Order:=xlDescending, _
DataOption:=xlSortNormal
With .Sort
.SetRange ranged
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End With
'more code here
next i
I'm not sure why this doesn't work, but what is particularly baffling is that when I try to run it on one sheet only by changing With ThisWorkbook.Sheets(i) to With ThisWorkbook.Sheets("Sheetname") it works fine, but when I put it into the above structure to run over multiple ones it doesn't.The idea is to reverse the order of column AJ starting from AJ2 to the last row with data in it and the length of AJ is different in different sheets. Any ideas?
The way you are going about getting your sheetnames is the problem.
You are starting to iterate through i from 6 to Worksheets.count.. With this, assuming you have 10 sheets, you will only iterate through 4 i's before being complete.
The main issue though is that you probably don't have sheetnames that are Sheets(i). i is a variable here and furthermore, it isn't a string. So you need to have a string at the other end of the variable you are placing inside Sheets().
I think what you are trying to accomplish is using a LIST of sheet names that are Strings. Using the variable i as a Long or Integer to iterate through them. "i" being the NUMBER and name(i) being a String.
'This example assumes you have 6 sheets. Declare this at the top with your initial variable declarations.
Dim name(1 to 6) As String
name(1) = "Sheet1"
name(2) = "TotalSales"
name(3) = "MonthlyRevenue"
name(4) = "Sheet 4 Name"
name(5) = "Whatever This sheet is called"
name(6) = "Last Sheet Name"
or whatever your sheets are named. Then when you call Sheets you will use something more like this.
For i = 1 to 6
'YOUR CODE THAT YOU ALREADY ENTERED THAT WORKS WHEN THE SHEET NAME IS CORRECT
With ThisWorkbook.Sheets(name(i))
Next i

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