I have got a problem with my macro and I would like to know if someone could help me.
I am doing a macro in a file, and that macro will go accessing other file and sort the information existing there. Until now I have the following code:
Sub Macro()
Dim xl As New Application
Dim xlw As Workbook
Dim xls As Worksheet
a = ThisWorkbook.Path & "\A.csv"
On Error GoTo bm:
Set xlw = xl.Workbooks.Open(a)
Set xls = xlw.Sheets(1)
' Windows(a).Activate
a = xls.Name
Columns("C:C").Select
xlw.Worksheets(a).Sort.SortFields.Clear
xlw.Worksheets(a).Sort.SortFields.Add Key:=Range("C2"), SortOn _
:=xlSortOnValues, Order:=xlDescending, DataOption:=xlSortNormal
With xlw.Worksheets(a).Sort
.SetRange Range("A2:K297594")
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
bm:
xlw.Saved = True
xlw.Close True
xl.Quit
Set xls = Nothing
Set xlw = Nothing
Set xl = Nothing
End Sub
When put it running , when it reaches the instruction ".SetRange Range("A2:K297594")" it gives "Run-time error 5" and I don't understand why. So could anyone explain me how resolve this or why is giving this error?
Thanks :)
Your range is not referenced and Excel doesNOT know of which sheet you are talking about, it should be .SetRange xlw.Worksheets(a).Range("A2:K297594") :
Sub Macro()
Dim xl As New Application
Dim xlw As Workbook
Dim xls As Worksheet
a = ThisWorkbook.Path & "\A.csv"
On Error GoTo bm:
Set xlw = xl.Workbooks.Open(a, Local:=True)
Set xls = xlw.Sheets(1)
' Windows(a).Activate
a = xls.Name
Columns("C:C").Select
xlw.Worksheets(a).Sort.SortFields.Clear
xlw.Worksheets(a).Sort.SortFields.Add Key:=xlw.Worksheets(a).Range("C2"), SortOn _
:=xlSortOnValues, Order:=xlDescending, DataOption:=xlSortNormal
With xlw.Worksheets(a).Sort
.SetRange xlw.Worksheets(a).Range("A2:K297594")
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
bm:
xlw.Save
xlw.Close True
xl.Quit
Set xls = Nothing
Set xlw = Nothing
Set xl = Nothing
End Sub
You need to qualify all your workbook, worksheet and range references, especially since you are running a macro against another workbook from where the macro runs.
You were really almost there (99%). This will clean it up for you:
Dim wName as String 'since you already use a to get the file name
wName = xls.Name
With xlw.Worksheets(wName).Sort
With .SortFields
.Clear
'note . (period) in front of range and I am pretty sure you need to set the
'whole range reference ... hence the C297597 ... but maybe just C2 is enough
.Add Key:=.Range("C2:C297594"), SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:=xlSortNormal
End With
'note . (period) in front of range
.SetRange .Range("A2:K297594")
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Related
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.
I'm trying to pass a range to a subroutine, but its throwing up a "Method 'Range' of object '_Global' failed" error.
In the main I declare and define the range variable I want to use:
Sub maintest()
Dim ScheduledSort As Range
Set ScheduledSort = Range("F4:F321")
Call test(ScheduledSort)
End Sub
Then in the subroutine test I want it to sort using the range I passed it from the routine above:
Sub test(RangeForSort)
Sheets("SheetTest").Select
' Sort in descending order
ActiveWorkbook.Worksheets("SheetTest").AutoFilter.Sort.SortFields.Add _
Key:=Range("RangeForSort"), SortOn:=xlSortOnValues, Order:=xlDescending, _
DataOption:=xlSortTextAsNumbers
With ActiveWorkbook.Worksheets("SheetTest").AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
I think its going wrong at the Key:=Range("RangeForSort") but I can't work out why and how to fix it.
What is it I'm doing wrong with the Range and how do I fix it such that I can pass it any Range to sort on?
And if you have a better suggestion for what I'm trying to do, feel free to add! :-)
Shorter version would look like this:
Sub test(rng As Range)
' Sort in descending order
Worksheets(rng.Parent.Name).AutoFilter.Sort.SortFields.Add _
Key:=rng, SortOn:=xlSortOnValues, Order:=xlDescending, _
DataOption:=xlSortTextAsNumbers
With Worksheets(rng.Parent.Name).AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
To run:
Call test(Worksheets("YOUR WORKSHEET NAME").Range("YOUR RANGE")).
If you pass a Range object to the sub, you are passing an object that is already associated with some worksheet. The sub selects a potentially different worksheet and then has trouble handling the passed range.
If you want to pass a specific block of cells to a sub that needs to change worksheets, then pass a String variable instead.
UNTESTED
Sub maintest()
Dim ScheduledSort As String
ScheduledSort = "F4:F321"
Call test(ScheduledSort)
End Sub
Sub test(RangeForSort As String)
Sheets("SheetTest").Select
ActiveWorkbook.Worksheets("SheetTest").AutoFilter.Sort.SortFields.Add _
Key:=Range(RangeForSort), SortOn:=xlSortOnValues, Order:=xlDescending, _
DataOption:=xlSortTextAsNumbers
With ActiveWorkbook.Worksheets("SheetTest").AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
I set this up and now I can pass the "ActiveCell/Range" whatever and call the function throughout my project if needed.
Public colLetter As Variant
Sub Test()
Dim rng As Range
Set rng = ActiveWorkbook.ActiveSheet.Range("A1:A1")
Call GetColLet(rng)
End Sub
Public Sub GetColLet(var As Range)
colLetter = Split(var.Address, "$")(1)
MsgBox colLetter
End Sub
I want to sort info on my sheet according to their last name (which is column 1). there's a form control button on the sheet that filters the info in accordance to the name of the button. everytime each of these form control buttons are clicked, i want to sort the info alphabetically. so i've used the record on macro to see what excel does to get familiar with it but i'm quite stuck... the problem is the r and countA it gives me an error of type mismatch
in total i have 17 columns (A to Q - but i don't want to really set a range to the columns, just in case i add more columns later) that contains information related to the last name which is column 1 and starts from row 3
Sub btnPrinceRupert()
Dim ws As Worksheet
Dim r As Range
Set r = ws.Cells(2, 1)
Set ws = Worksheets("Master")
Call filterMyTable(xPrinceRupert)
Call changeTitle("PrinceRupert")
r.Select
ws.Sort.SortFields.Clear
ws.Sort.SortFields.Add Key:=r, _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ws.sort
.SetRange (WorksheetFunction.CountA(Range("A:A")))
'essentially, i want the range to be up to the last entry of the table
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
For the first issue, you need to set the ws before set the r value;
and the Second issue, the result of WorksheetFunction.CountA(Range("A:A")) is a number, not Range. so the code should write like this:
Sub btnPrinceRupert()
Dim ws As Worksheet
Dim r As Range
Set ws = Worksheets("Master")
Set r = ws.Cells(2, 1)
Call filterMyTable(xPrinceRupert)
Call changeTitle("PrinceRupert")
r.Select
ws.Sort.SortFields.Clear
ws.Sort.SortFields.Add Key:=r, _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ws.sort
.SetRange (Range("1:" & WorksheetFunction.CountA(Range("A:A")))
'essentially, i want the range to be up to the last entry of the table
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
I'm trying to combine two functions. I have a VBA script which goes through a set range and sorts all the text column by column alphabetically.
Sub SortIndividualRows()
' Sorts rows within a list from A-Z
' Run Clean all first to avoid sorting blanks
' Set maximum range to avoid sorting too many rows
Dim rngFirstRow As Range
Dim rng As Range
Dim ws As Worksheet
Application.ScreenUpdating = False
Set ws = ActiveSheet
Set rngFirstRow = ws.Range("A1:NS1")
For Each rng In rngFirstRow
With ws.Sort
.SortFields.Clear
.SortFields.Add Key:=rng, Order:=xlAscending
'assuming there are no blank cells..
.SetRange ws.Range(rng, rng.Range("A87").End(xlUp))
.Header = xlYes
.MatchCase = False
.Apply
End With
Next rng
Application.ScreenUpdating = True
End Sub
I'd like to combine this with a script to then sort each column by color. I recorded a macro when I sorted manually and looked at the code the recording generated. I'm trying to figure out how I could take the generated code and combine it with the above function.
Sub sortColor()
'
' sortColor Macro
' Goes through a range of selected cells and sorts by color, setting green cells (matches) above those with no match (red text)
'
'
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Add(Range("F4:F88"), _
xlSortOnCellColor, xlAscending, , xlSortNormal).SortOnValue.Color = RGB(198, _
239, 206)
With ActiveWorkbook.Worksheets("Sheet1").Sort
.SetRange Range("F3:F88")
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
Just to clarify, you want to run one module then the other straight away afterwards? or do you want the action of the second module to run each time the for loop completes?
To run one directly after the other:
Sub SortIndividualRows()
' Sorts rows within a list from A-Z
' Run Clean all first to avoid sorting blanks
' Set maximum range to avoid sorting too many rows
Dim rngFirstRow As Range
Dim rng As Range
Dim ws As Worksheet
Application.ScreenUpdating = False
Set ws = ActiveSheet
Set rngFirstRow = ws.Range("A1:NS1")
For Each rng In rngFirstRow
With ws.Sort
.SortFields.Clear
.SortFields.Add Key:=rng, Order:=xlAscending
'assuming there are no blank cells..
.SetRange ws.Range(rng, rng.Range("A87").End(xlUp))
.Header = xlYes
.MatchCase = False
.Apply
End With
Next rng
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Add(Range("F4:F88"), _
xlSortOnCellColor, xlAscending, , xlSortNormal).SortOnValue.Color = RGB(198, _
239, 206)
With ActiveWorkbook.Worksheets("Sheet1").Sort
.SetRange Range("F3:F88")
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Application.ScreenUpdating = True
End Sub
To run the second module each time the for loop completes:
Sub SortIndividualRows()
' Sorts rows within a list from A-Z
' Run Clean all first to avoid sorting blanks
' Set maximum range to avoid sorting too many rows
Dim rngFirstRow As Range
Dim rng As Range
Dim ws As Worksheet
Application.ScreenUpdating = False
Set ws = ActiveSheet
Set rngFirstRow = ws.Range("A1:NS1")
For Each rng In rngFirstRow
With ws.Sort
.SortFields.Clear
.SortFields.Add Key:=rng, Order:=xlAscending
'assuming there are no blank cells..
.SetRange ws.Range(rng, rng.Range("A87").End(xlUp))
.Header = xlYes
.MatchCase = False
.Apply
End With
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Add(Range("F4:F88"), _
xlSortOnCellColor, xlAscending, , xlSortNormal).SortOnValue.Color = RGB(198, _
239, 206)
With ActiveWorkbook.Worksheets("Sheet1").Sort
.SetRange Range("F3:F88")
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Next rng
Application.ScreenUpdating = True
End Sub
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