Sort Column by Specific Row - vba

I would like to sort Columns E though "lastColumn" in ascending order.
The values to be used for sorting are in Row 14.
The data set is located in cells E8 to "lastColumn" "lastRow".
Below is what I have thus far, but I am getting an error that the reference is not valid. I'm guessing I am not using &lastRow& correctly, let alone trying to plug in the value for "lastColumn".
I am using lastColumn and lastRow as a means to ignore blank cells.
Sub SortColumns()
Dim lastColumn As Long
Dim lastRow As Long
lastColumn = Sheet1.Cells(8, Columns.Count).End(xlToLeft).Column
lastRow = Sheet1.Cells(Rows.Count, 1).End(xlUp).Row
ActiveWorkbook.ActiveSheet.Sort.SortFields.Clear
ActiveWorkbook.ActiveSheet.Sort.SortFields.Add Key:=Range( _
"E14:Z" & lastRow&), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.ActiveSheet.Sort
.SetRange Range("E8:I" & lastRow)
.Header = xlGuess
.MatchCase = False
.Orientation = xlLeftToRight
.SortMethod = xlPinYin
.Apply
End With
End Sub

I think you only had a small mistake. I added a little more code as you should always be specific on which workbook and on which sheet you are working. Try this:
Dim lastColumn As Long
Dim lastRow As Long
Dim Sht1 As Worksheet
Sht1 = ActiveWorkbook.Sheets("Sheet1")
lastColumn = Sht1.Cells(8, Sht1.Columns.Count).End(xlToLeft).Column
lastRow = Sht1.Cells(Sht1.Rows.Count, 1).End(xlUp).Row
Sht1.Sort.SortFields.Clear
Sht1.Sort.SortFields.Add Key:=Range( _
"E14:Z" & lastRow), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With Sht1.Sort
.SetRange Range("E8:I" & lastRow)
.Header = xlGuess
.MatchCase = False
.Orientation = xlLeftToRight
.SortMethod = xlPinYin
.Apply
End With

Related

why AutoFill failed?

Here I tried to random the colons, however, it tell me cannot autofill, and same thing works for my randomRow()
Sub randomCol()
Dim Line As Integer
Line = LastRow + 1
Range("N154").Select
ActiveCell.FormulaR1C1 = "=RAND()"
Dim randomRange As String
randomRange = "N" & Line & ":BF" & Line
MsgBox randomRange
Selection.AutoFill Destination:=Range(randomRange), Type:=xlFillDefault
Range("N2:BF" & Line).Select
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Add key:=Range("N" & LastRow + 1), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Sheet1").Sort
.SetRange Range("N1:BF" & Line)
.Header = xlGuess
.MatchCase = False
.Orientation = xlLeftToRight
.Apply
End With
End Sub
AutoFill method requires that the destination must include the source range. Proper Syntax for autofill as follows for example only.
Set sourceRange = Worksheets("Sheet1").Range("A1:A2")
Set fillRange = Worksheets("Sheet1").Range("A1:A20")
sourceRange.AutoFill Destination:=fillRange
Accordingly I have modified your program and hope it should work fine.
Sub randomCol()
Dim Line As Integer
Line = LastRow + 1
Range("N154").Select
ActiveCell.FormulaR1C1 = "=RAND()"
Set SourceRange = Worksheets("Sheet1").Range("N154") 'Changed you can set the range required by you.
Dim randomRange As String
randomRange = "N" & Line & ":BF" & Line
Set fillRange = Worksheets("Sheet1").Range("n1:BF154") 'Set fill range appropriately but should include source range
MsgBox randomRange
'changed
'Selection.AutoFill Destination:=Range(randomRange), Type:=xlFillDefault
SourceRange.AutoFill Destination:=fillRange
Range("N2:BF" & Line).Select
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Add Key:=Range("N" & LastRow + 1), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Sheet1").Sort
.SetRange Range("N1:BF" & Line)
.Header = xlGuess
.MatchCase = False
.Orientation = xlLeftToRight
.Apply
End With
End Sub
edit after OP's specification that LastRow is a function returning ActiveSheet.Cells(Rows.Count, "L").End(xlUp).Row
the destination must include the source range, while the latter is "N154" and the former are cells in row Line between columns "N" and "BF", so it'd work only if Line is 154.
moreover it's best to define both Line and LastRow() as of Long type instead of Integer
so that your code could change like follows:
Sub randomCol()
Dim Line As Long
Line = Lastrow + 1
Range("N" & Line).Select
ActiveCell.FormulaR1C1 = "=RAND()"
Dim randomRange As String
randomRange = "N" & Line & ":BF" & Line
MsgBox randomRange
Selection.AutoFill Destination:=Range(randomRange), Type:=xlFillDefault
Range("N2:BF" & Line).Select
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Add Key:=Range("N" & Line), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Sheet1").Sort
.SetRange Range("N1:BF" & Line)
.Header = xlGuess
.MatchCase = False
.Orientation = xlLeftToRight
.Apply
End With
End Sub
Function LastRow() As Long
LastRow = ActiveSheet.Cells(Rows.count, "L").End(xlUp).Row
End Function
but you may also consider:
to avoid selections and activation
there's no need for Autofill actually, since you're simply pasting a formula
so your code can become:
Sub randomCol2()
Dim Line As Long
With ActiveWorkbook.Worksheets("Sheet1")
Line = .Cells(.Rows.count, "L").End(xlUp).Row + 1
.Columns("N:BF").Rows(Line).FormulaR1C1 = "=RAND()"
With .Sort
.SortFields.Clear
.SortFields.Add Key:=Range("N" & Line), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.SetRange Range("N1:BF" & Line)
.Header = xlGuess
.MatchCase = False
.Orientation = xlLeftToRight
.Apply
End With
End With
End Sub

Runtime Error 91 when sorting

I am writing a subroutine to dynamically copy 2 columns from one sheet to another. These column lengths might change from one report to another.
Here is the code:
Sub getAnalystsCount()
Dim rng As Range
Dim dict As Object
Set dict = CreateObject("scripting.dictionary")
Dim varray As Variant, element As Variant
Set ws = ThisWorkbook.Worksheets("ReportData")
With ws
Worksheets("ReportData").Activate
Columns("E:E").Select
ActiveWorkbook.Worksheets("ReportData").AutoFilter.Sort.SortFields.Clear
ActiveWorkbook.Worksheets("ReportData").AutoFilter.Sort.SortFields.Add Key:= _
Range("E1"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
With ActiveWorkbook.Worksheets("ReportData").AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
lastrow = .Range("A" & .Rows.Count).End(xlUp).Row
'~~> Set First row
firstrow = 2
'~~> Set your range
Set rng = .Range("E" & firstrow & ":E" & lastrow)
varray = rng.Value
'Generate unique list and count
For Each element In varray
If dict.Exists(element) Then
dict.Item(element) = dict.Item(element) + 1
Else
dict.Add element, 1
End If
Next
End With
Set ws = ThisWorkbook.Worksheets("Analysts")
With ws
Worksheets("Analysts").Activate
'Paste report somewhere
ws.Range("A3").Resize(dict.Count, 1).Value = _
WorksheetFunction.Transpose(dict.Keys)
ws.Range("B3").Resize(dict.Count, 1).Value = _
WorksheetFunction.Transpose(dict.Items)
......
the error is in this line:
ActiveWorkbook.Worksheets("ReportData").AutoFilter.Sort.SortFields.Clear
Replace your below code
Columns("E:E").Select
ActiveWorkbook.Worksheets("ReportData").AutoFilter.Sort.SortFields.Clear
ActiveWorkbook.Worksheets("ReportData").AutoFilter.Sort.SortFields.Add Key:= _
Range("E1"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
With ActiveWorkbook.Worksheets("ReportData").AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
With the below code
Columns("E:E").Select
lastrow1 = .Range("E" & .Rows.Count).End(xlUp).Row
ActiveWorkbook.Worksheets("ReportData").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("ReportData").Sort.SortFields.Add Key:=Range("E1") _
, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("ReportData").Sort
.SetRange Range("E2:E" & lastrow1)
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
startCell = Range("A1").Address
endCell = Range("E100000").End(xlUp).Address
ActiveWorkbook.Worksheets("ReportData").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("ReportData").Sort.SortFields.Add Key:=Range("E1"), SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:=xlSortTextAsNumbers
With ActiveWorkbook.Worksheets("ReportData").Sort
.SetRange Range(startCell,endCell)
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Obviously this is rough, you will need to make it your own, but it will allow you to sort the E column which is what your initial code looks like it was trying to do.
The Range.Sort method can be used for a quick one column sort and discards much of the verbose code produced when recording a worksheet sort operation. Without an active AutoFilter, this is the better way to go.
Sub getAnalystsCount()
Dim el As Long, ws As Worksheet
Dim dict As Object
Dim varray As Variant
Set dict = CreateObject("scripting.dictionary")
'don't know what is in column E but this might be helpful
'dict.comparemode = vbTextCompare 'non-case-sensitive
Set ws = ThisWorkbook.Worksheets("ReportData")
With ws
'this is not necessary inside a With ... End With block
'Worksheets("ReportData").Activate
With .Range("A1").CurrentRegion
'this quick code line is all you need
.Cells.Sort Key1:=.Columns(5), Order1:=xlAscending, _
Orientation:=xlTopToBottom, Header:=xlYes
'resize to # of rows -1 × 1 column and shift 1 row down and over to column E
With .Resize(.Rows.Count - 1, 1).Offset(1, 4)
'store the raw values
varray = .Value2
End With
End With
End With 'done with the ReportData worksheet
'Generate unique list and count
'I prefer to work with LBound and UBound
For el = LBound(varray, 1) To UBound(varray, 1)
If dict.Exists(varray(el, 1)) Then
dict.Item(varray(el, 1)) = dict.Item(varray(el, 1)) + 1
Else
dict.Add Key:=varray(el, 1), Item:=1
End If
Next el
Set ws = ThisWorkbook.Worksheets("Analysts")
With ws
'this is not necessary inside a With ... End With block
'Worksheets("Analysts").Activate
'might want to clear the destination cell contents first if there is something there
if application.counta(.Range("A3:B3") = 2 then _
.Range("A3:B" & .Cells(Rows.Count, "B").End(xlUp).Row).ClearContents
'Paste report somewhere
.Range("A3").Resize(dict.Count, 1).Value = _
WorksheetFunction.Transpose(dict.Keys)
.Range("B3").Resize(dict.Count, 1).Value = _
WorksheetFunction.Transpose(dict.Items)
End With 'done with the Analysts worksheet
End Sub
I prefer to work with the LBound and UBound functions to determine the scope of an array.
When you are inside a With ... End With statement, use the . to note the parent worksheet and discard the Range .Activate method and ws variable.

Sorting data using VBA and data in cell

I'm quite new to VBA and macros, and was looking for a bit of help sorting data using a macro. I've recorded doing what I was, and it has produced this:
Macro1 Macro
Range("A19:L28").Select
ActiveWorkbook.Worksheets("EuropeanStocks").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("EuropeanStocks").Sort.SortFields.Add Key:=Range( _
"h20:h28"), SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:= _
xlSortNormal
With ActiveWorkbook.Worksheets("EuropeanStocks").Sort
.SetRange Range("A19:L28")
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
My question is how do I make is so that the sort range is defined by the data in a cell. For this example with the sort range of h20:h28, the 20 and 28 parts would be constant, and I'd have a cell in the worksheet
E.g A1, which contains the letter of the column that needs to be sorted, E.g. "i","j", etc..
How would I make it so A1 was read into the sort range?
See the code below. You need to place the value in Range("A1") into a variable and then set your range in the code with that variable.
Dim ws As Worksheet, c As String, rSort As Range, rData As Range
ws = Sheets("EuropeanStock")
With ws
c = .Range("A1").Value
Set rData = .Range("A19:L28")
Set rSort = .Range(c & "20:" & c & "28")
With rData.Sort
With .SortFields
.Clear
.Add Key:=rSort, SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:=xlSortNormal
End With
.SetRange rData
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End With
The most versatile way would be to define a named range for the range you want to sort (or the range containing the range you want to sort), but for this simple requirement, you can simply wrap a Range call to the cell containing the sort range (e.g. A1) in another Range call, thus obtaining the desired range. E.g.:
.SetRange Range(Range("A1"))
EDIT: This assumes that A1 contains a full range reference (e.g. it contains "A19:L28"). If you want to construct the target range from other cells partially, one solution could be to construct the range reference, for example:
.SetRange Range(Range("A1") & "19" & ":" & Range("A2") & "28")
Just place the columns that you want to sort in A1, and separate them with a slash / (i.e. a/f/h/s)
This will sort all of them one by one :
Dim NewRange As String, _
DataRange As String, _
Sp() As String
Sp = Split(Range("A1"), "/")
With ActiveWorkbook.Worksheets("EuropeanStocks")
For i = LBound(Sp) To UBound(Sp)
NewRange = Sp(i) & "19:" & Sp(i) & "28"
DataRange = Sp(i) & "20:" & Sp(i) & "28"
With .Sort
.SortFields.Clear
.SortFields.Add _
Key:=Range(DataRange), _
SortOn:=xlSortOnValues, _
Order:=xlDescending, _
DataOption:=xlSortNormal
.SetRange Range(NewRange)
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Next i
End With

VBA code not sorting by CASE - also can't apply to multiple sheets

EDIT: I just noticed the VBA script isn't working at all, it looks like it is just sorting by the first column as I am getting some funny results :S?
I am using the following VBA to sort by all columns on the sheet.
Sub SortVariableColumns()
Dim strLastCol As String
Dim lngLastCol As Long
Dim sht As Worksheet
Set sht = ActiveSheet
With ActiveSheet
lngLastCol = sht.Cells.Find("*", SearchOrder:=xlByColumns, LookIn:=xlValues, SearchDirection:=xlPrevious).Column
strLastCol = Split(sht.Cells(1, lngLastCol).Address, "$")(1)
sht.Columns("A:" & strLastCol).Select
sht.Sort.SortFields.Clear
sht.Sort.SortFields.Add Key:=Range("A1"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal ', Header:=xlYes
End With
With sht.Sort
.SetRange Columns("A:" & strLastCol)
.Header = xlYes
.MatchCase = True
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Range("A1").Select
End Sub
However, it isn't matching case for some reason. The sort works for everything but the case of the words.
Also, is there anyway to make this then move onto the next sheet (i.e. if I selected activeworksheets) - I tried using
Dim sht As Worksheet
For Each ws In ActiveWindow.SelectedSheets
this but kept failing, I guess it has something to do with having to reset the IngLastCOl/StrLastCol holding from the first part of the VBA?
Many thanks.
What's with this as well?
Dim sht As Worksheet
For Each ws In ActiveWindow.SelectedSheets
Where have you defined ws variable? I am not suprised if this code fails at every line or never run at all. These are some fundamental issues.
#boncoDigo
I think I mispasted the code - the code wasn't actually failing on anything related to that! Sorry.
This can now be closed. I have worked out how to do it.
For those interested, this is the code that I used (can be easily amended to loop through sheets):
Sub SortVariableColumns()
Dim finalcolumn As Integer
Dim FinalRow As Integer
Dim sht As Worksheet
Set sht = ActiveSheet
sht.Sort.SortFields.Clear
With ActiveSheet
finalcolumn = Cells(1, Application.Columns.Count).End(xlToLeft).Column
FinalRow = Cells(Application.Rows.Count, 2).End(xlUp).Row
For N = 1 To finalcolumn Step 1
sht.Sort.SortFields.Add Key:=Range(Cells(2, N), Cells(FinalRow, N)), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
Next N
End With
With sht.Sort
.SetRange Range(Cells(1, 1), Cells(FinalRow, finalcolumn))
.Header = xlYes
.MatchCase = True
.Orientation = xlTopToBottom
.Apply
End With
End Sub

Iteration in Excel VBA

It's been a while since I have used VBA on Excel.
I want to alphabetize the contents of each column on the sheet.
This is what I have:
Range("A1").Select
Range("A1:A19").Select
ActiveWorkbook.Worksheets("Sheet2").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet2").Sort.SortFields.Add Key:=Range("A1"), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Sheet2").Sort
.SetRange Range("A1:A19")
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Range("B1").Select
End Sub
How can I make this into a for loop that keeps going as long as the range is active?
Like this?
Option Explicit
Sub sample()
Dim i As Long
With Sheets("Sheet1")
For i = 1 To .UsedRange.Columns.Count
.Columns(i).Sort Key1:=.Cells(1, i), Order1:=xlAscending, _
Header:=xlGuess, OrderCustom:=1, MatchCase:=False, _
Orientation:=xlTopToBottom, DataOption1:=xlSortNormal
Next i
End With
End Sub
Here you go. This code assumes your data is laid out in some type of table format. Also, it assumes you want the entire column sorted (including blanks and such). If you want to make the range more specific or just set it with a hard reference adjust the code where I commented.
Sub sortRange()
Dim wks As Worksheet
Dim loopRange As Range, sortRange As Range
Set wks = Worksheets("Sheet1")
With wks
'can change the range to be looped, but if you do, only include 1 row of the range
Set loopRange = Intersect(.UsedRange, .UsedRange.Rows(1))
For Each cel In loopRange
Set sortRange = Intersect(cel.EntireColumn, .UsedRange)
With .Sort
.SortFields.Clear
.SortFields.Add Key:=sortRange
.SetRange sortRange
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Next
End With
End Sub