Find last column & sort on last column - vba

I need Excel to detect the last column I have and sort on that column. I have a macro that generates a new column every time it is used so I cannot use a constant.
Sub sortyness()
Dim sortdata(A1 & ":", Cells(LastRow, LastColumn)) As Range
ActiveWorkbook.Worksheets("Compiled").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Compiled").Sort.SortFields.Add _
Key:=Range(Sorton), Sorton:=xlSortOnValues, Order:=xlAscending, _
DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Compiled").Sort
.SetRange Range(sortdata)
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
Here's a screenshot of the sheet:
I am having trouble with getting it to sort by the last column. Can I define the column by looking for the first cell in row 1 that has no data and then use that as a basis to sort? How should I modify my VBA?
Thank you.
I don't know how to edit this thing to get it to not appear as a duplicate, but it's obviously not a duplicate. Mine is more concerned with running a macro on the last column than it is finding the last column.

An vba sort operation actually requires much less code than you get from a recording.
Dim sortdata As Range, LastRow as long, LastColumn as long
With ActiveWorkbook.Worksheets("Compiled")
LastRow = .cells(.rows.count, "A").end(xlup).row
LastColumn = .cells(1, .columns.count).end(xltoleft).column
with .range(.cells(1, 1), .Cells(LastRow, LastColumn))
.Cells.Sort Key1:=.Columns(.columns.count), Order1:=xlAscending, _
Orientation:=xlTopToBottom, Header:=xlyes
end with
end with

Concerning the second line here:
Dim sortdata(A1 & ":", Cells(LastRow, LastColumn)) As Range
This is not how you assign range. If you want to assign a range, starting on A1 and ending on lastRow, lastColumn, consider this:
Public Sub TestMe()
Dim lastRow As Long: lastRow = 5
Dim lastCol As Long: lastCol = 10
Dim sortData As Range
Set sortData = Range("A1:" & Cells(lastRow, lastCol).Address)
Debug.Print sortData.Address
End Sub
In the above case the range is assigned to the ActiveSheet, which is not always what you may need. If you want to avoid assigning to the ActiveSheet, you should specify the worksheet as well:
With Worksheets("Compiled")
Set sortData = .Range("A1:" & .Cells(lastRow, lastCol).Address)
End With
The two dots in the code above .Range and .Cells will make sure that you refer to the Worksheets("Compiled") and thus will save some problems in the future.

Sort the "last" column with a single line of VBA:
Columns(ActiveSheet.UsedRange.Columns(ActiveSheet.UsedRange.Columns.Count)._
Column).Sort key1:=Columns(ActiveSheet._
UsedRange.Columns(ActiveSheet.UsedRange.Columns.Count).Column)
...which is exactly the same as:
Sub SortLastColumn()
With ActiveSheet.UsedRange
Columns(.Columns(.Columns.Count).Column).Sort key1:=Columns(.Columns(.Columns.Count).Column)
End With
End Sub

Related

Excel VBA - Correct way to sort on two unnamed columns

I want to do an expanded sort on 2 columns (the columns never change). The code should first sort column D, and then sort column E. While I can use the macro recorder to accomplish this, it produces about 20 lines of code, when in reality, I know it can be done in about 5 or 6.
The below piece of code works for one column, although I think even it can be cleaned up some.
Worksheets("CSAT Details").Sort.SortFields.Add Key:=Range("E1"), SortOn:=xlSortOnValues, Order:=xlAscending
lastRow = Cells(Rows.Count, "A").End(xlUp).Row
With Worksheets("CSAT Details").Sort
.SetRange Range("A2:F" & lastRow)
.Orientation = xlTopToBottom
.Apply
End With
I have created the below code to attempt to sort on the 2 columns, but it does not yet work, giving a
448 named argument not found error
lastRow = Cells(Rows.Count, "A").End(xlUp).Row
Worksheets("CSAT Details").Sort.SortFields.Add _
Key1:=Range("D1"), SortOn1:=xlSortOnValues, Order1:=xlAscending, _
Key2:=Range("E1"), SortOn2:=xlSortOnValues, Order2:=xlAscending
With Worksheets("CSAT Details").Sort
.SetRange Range("A2:F" & lastRow)
.Orientation = xlTopToBottom
.Apply
End With
I am not sure what I am missing.
Something like the following (simply specify your range before running):
Sub foo()
Range("A1:G6").Sort Key1:=Range("E1"), Order1:=xlAscending, Key2:=Range("D1") _
, Order2:=xlAscending, Header:=xlGuess, OrderCustom:=1, MatchCase:= _
False, Orientation:=xlTopToBottom
End Sub
Something like this should work for you:
Sub tgr()
Dim ws As Worksheet
Dim LastRow As Long
Set ws = ActiveWorkbook.Sheets("CSAT Details")
LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
With ws.Range("D1:E" & LastRow)
.Sort .Resize(, 1), xlAscending, .Offset(, 1).Resize(, 1), , xlAscending, Header:=xlYes
End With
End Sub

sort data left to right in excel vba

I want to sort complete sheet data by column header alphabetically.
Below code works fine but i have to manually enter data range in variables(keyrange and datarange) every time, Since number of columns/rows varies in every file. I tried different ways in below code. Can you advise Is there a way that the last column automatically selected ??like in below W is last column with data in file and code should pick up last column.
Similarly last row of columns should pick up into range (like 485 is last row of file in below code), IS it possible ?
Sub sortfile2()
Dim keyrange As String
Dim DataRange As String
keyrange = "A1:W1"
DataRange = "A1:W485"
Range("A1").Select
Range(Selection, Selection.End(xlToRight)).Select
Range(Selection, Selection.End(xlDown)).Select
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Add Key:=Range(keyrange), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Sheet1").Sort
.SetRange Range(DataRange)
.Header = xlYes
.MatchCase = False
.Orientation = xlLeftToRight
.SortMethod = xlPinYin
.Apply
End With
End Sub
If the source rane is dynamic, you can go with
bottom= Range("A1").End(xlDown).Row
Set DataRange = Range("A1").CurrentRegion.Resize(bottom - 1).Offset(1)
Note that CurrentRegion itself is not enough. you should combine it with Resize and Offset. İf you try with only CurrentRegion and go with F8, you can see why.
Yes, determining last column and last row is possible.
If you want to determine last column in first (1) row, use the code:
Cells(1, Columns.Count).End(xlToLeft).Column
If you want to get last row in first column, use following:
Cells(Rows.Count, 1).End(xlUp).Row
This is for the first column / row, so you can change it as you want.
This is range of data.
Sub test()
Dim rngDB As Range
Dim Ws As Worksheet
Dim r As Long, c As Long
Set Ws = ActiveSheet
With Ws
r = .Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
c = .Cells.Find("*", SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column
Set rngDB = .Range("a1", .Cells(r, c))
rngDB.Select
End With
End Sub
Or
range("a1").CurrentRegion
yes, Michal answer and other source helped to find exact required output
'Find the last non-blank cell in row 1
lCol = Cells(1, Columns.Count).End(xlToLeft).Column
d = Replace(Cells(1, lCol).Address(True, False), "$1", "")
'Find the last non-blank cell in column 1
lRow = Cells(rows.Count, 1).End(xlUp).row
keyrange = "A1:" & d & 1
DataRange = "A1:" & d & lRow
'below line is to print (for debugging) the calculated range
MsgBox (keyrange)
MsgBox (DataRange)

Excel 2013: Sorting columns based on first row value using VBA

I would like to implement an Excel macro that sorts all columns from column "C" to the last column containing data (columns A and B shall not be affected).
The columns shall be sorted from A->Z based on the cell value of their first row (which is a string).
So far, I came up with the following code which I do not like that much because it contains hardcoded numbers for the Sort range making the code not really robust.
Sub SortAllColumns()
Application.ScreenUpdating = False
'Sort columns
With ActiveWorkbook.Worksheets("mySheet").Sort
.SetRange Range("C1:ZZ1000")
.Header = xlYes
.MatchCase = False
.Orientation = xlLeftToRight
.Apply
End With
Application.ScreenUpdating = True
End Sub
Searching the internet, one may find tons of suggestions getting the last used column or row. However most of them will blow up the code more than I expected.
I am not a VBA expert and it would be great if someone could make a suggestion how this problem can be solved in an elegant and efficient way.
If this is important: We will definitely not have more that 1000 rows and 1000 columns.
Any suggestion is highly appreciated.
edited:
changed temporary sheet adding statement to have it always as the last one
revised its deletion statement accordingly
should your need be to sort columns by moving them so as to have their headers sorted from left to right, then try this code
Option Explicit
Sub main()
Dim lastCol As Long
With Sheets("mySheet")
lastCol = .cells(1, .Columns.Count).End(xlToLeft).Column
Call OrderColumns(Range(.Columns(3), Columns(lastCol)))
End With
End Sub
Sub OrderColumns(columnsRng As Range)
Dim LastRow As Long
With columnsRng
LastRow = GetColumnsLastRow(columnsRng)
With .Resize(LastRow)
.Copy
With Worksheets.Add(after:=Worksheets(Worksheets.Count)).cells(1, 1).Resize(.Columns.Count, .Rows.Count) 'this will add a "helper" sheet: it'll be removed
.PasteSpecial Paste:=xlPasteAll, Transpose:=True
.Sort key1:=.Columns(1), Order1:=xlAscending, Orientation:=xlTopToBottom, Header:=xlNo
.Copy
End With
.PasteSpecial Paste:=xlPasteAll, Transpose:=True
Application.DisplayAlerts = False: Worksheets(Worksheets.Count).Delete: Application.DisplayAlerts = True 'remove the "helper" sheet (it's the (n-1)th sheet)
End With
End With
End Sub
Function GetColumnsLastRow(rng As Range) As Long
Dim i As Long
'gets last row of the given columns range
GetColumnsLastRow = -1
With rng
For i = 1 To .Columns.Count
GetColumnsLastRow = WorksheetFunction.Max(GetColumnsLastRow, .Parent.cells(.Parent.Rows.Count, .Columns(i).Column).End(xlUp).row)
Next i
End With
End Function
it makes use of a "helper" temporary (it gets deleted by the end) sheet.
Thanks to the suggestions and revisions of #SiddharthRout I got this:
Sub SortAllColumns()
Application.ScreenUpdating = False
Dim ws As Worksheet
Dim LastRow As Long
Dim LastColumn As Long
Dim LastColumnLetter As String
Set ws = ThisWorkbook.Sheets("mySheet")
'Get range
With ws
LastRow = .Range("A" & .Rows.Count).End(xlUp).Row
LastColumn = .Cells(1, .Columns.Count).End(xlToLeft).Column
LastColumnLetter = Split(.Cells(, LastColumn).Address, "$")(1)
'Sort columns
Range("C1:" & LastColumnLetter & LastRow).Select
.Sort.SortFields.Clear
.Sort.SortFields.Add Key:=Range("C1:" & LastColumnLetter & 1), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With .Sort
.SetRange ws.Range("C1:" & LastColumnLetter & LastRow)
.Header = xlYes
.MatchCase = False
.Orientation = xlLeftToRight
.Apply
End With
End With
Application.ScreenUpdating = True
End Sub

VBA Macro Loop only copies one cell of data instead of many

all
I'm a VBA novice here, and I'm being tasked with developing some macros in my new job. Currently, I am working on a macro that goes though a text file, applies some formatting, isolates required numerical data, copies it, and then outputs the copied information into a new Worksheet.
Here's the code for the formatting, just to make sure I post it:
`Perform Text-To-Columns on Column A. Delimited by the character "#"
Columns("A:A").Select
Selection.TextToColumns Destination:=Range("A1"), DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=False, _
Semicolon:=False, Comma:=False, Space:=False, Other:=True, OtherChar _
:="#", FieldInfo:=Array(1, 1), TrailingMinusNumbers:=True
`Perform Text-To-Columns on Column B. Delimited by the character ")"
Columns("B:B").Select
Selection.TextToColumns Destination:=Range("B1"), DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=False, _
Semicolon:=False, Comma:=False, Space:=False, Other:=True, OtherChar _
:=")", FieldInfo:=Array(1, 1), TrailingMinusNumbers:=True
`Format Column B for Numbers to have zero decimal places
Selection.NumberFormat = "0"
`Filter Column B for all numbers greater than 500
Selection.AutoFilter
ActiveSheet.Range("$B$1:$B$1720").AutoFilter Field:=1, Criteria1:=">500", _
Operator:=xlAnd
`Sort Filtered numbers from lowest to highest
ActiveWorkbook.Worksheets(1).Sort.SortFields.Clear
ActiveWorkbook.Worksheets(1).Sort.SortFields.Add Key:=Range( _
"B1"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
With ActiveWorkbook.Worksheets(1).Sort
.SetRange Range("B1").EntireColumn
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
At this point, I now have column B with an amount of 12 digit numbers that varies from file to file. This next part of the macro is a loop that should now look at this Column B, and begin to inspect the cells of Column B to see if they contain 12 digit numbers, and if they do, begin to copy them as a range. Once all the 12 digit numbers in B are found, it should copy them all, open a new tab, and paste the results:
' Declare loop variables
Dim myLastRow As Long
Dim myRow As Long
Dim i As Long
Dim myValue As String
Dim myStartRow As Long
Dim myEndRow As Long
' Find last row with data in column B
myLastRow = Cells(Rows.Count, "B").End(xlUp).Row
' Loop through all data in column B until you find a 12 order number Number
For myRow = 1 To myLastRow
' If 12 digit entry is found, capture the row number,
' then go down until you find the first entry not 12 digits long
If (Len(Cells(myRow, "B")) = 12) And (IsNumeric(Cells(myRow, "B"))) Then
myStartRow = myRow
i = 1
Do
If Len(Cells(myRow + i, "B")) <> 12 Then
' If found, capture row number of the last 13 digit cell
myEndRow = myRow + i - 1
' Copy the selected data
Range(Cells(myStartRow, "B"), Cells(myEndRow, "B")).Copy
' Add "Results" as a new sheet for the copied Card Numbers to be pasted into
Sheets.Add.Name = "Results"
Sheets("Results").Activate
' Paste clipboard to "Results" and format the results for viewing
Range("A1").Select
ActiveSheet.Paste
Columns("A:A").EntireColumn.AutoFit
Application.CutCopyMode = False
Exit Do
Else
' Otherwise, move row counter down one and continue
i = i + 1
End If
Loop
Exit For
End If
Next myRow
For whatever reason, when I go through the macro, all it does is capture the first value in B1 and then put that into the Results sheet. I cannot for the life of me figure out why. Could it be due to the filtering I've applied? If anyone could give me some insight, I'd be all ears. Thanks very much for any help you can offer.
This is a fairly simple code that seems to work. Hopefully it meets your needs:
Sub test1()
Dim ws As Worksheet
Dim res As Worksheet
Dim val As String
Set ws = ActiveSheet
Sheets.Add
Set res = ActiveSheet
res.Name = "Results"
ws.Select
Range("B1").Select
While ActiveCell.Value <> ""
If Len(ActiveCell.Value) = 12 Then
val = ActiveCell.Value
res.Select
ActiveCell.Value = val
ActiveCell.Offset(1, 0).Select
ws.Select
ActiveCell.Offset(1, 0).Select
Else
ActiveCell.Offset(1, 0).Select
End If
Wend
res.Select
Columns("A:A").EntireColumn.AutoFit
Range("A1").Select
End Sub
I am not sure to understand, but you can try this:
Option Explicit
Sub CopyNumber()
Dim ws1 As Worksheet: Set ws1 = ThisWorkbook.Sheets("Sheet1") 'Change the name of the sheet
Dim Result As Worksheet
Dim ws1Lastrow As Long, LastrowResult As Long
Dim i As Long, Rng As Range
Dim TestLenght, Arr
Sheets.Add.Name = "Results" ' Add your new sheet
Set Result = ThisWorkbook.Sheets("Results")
With ws1
ws1Lastrow = .Range("B" & Rows.Count).End(xlUp).Row 'Find the lastrow in the Source Data Sheet
Set Rng = .Range("B1:B" & ws1Lastrow) 'Set your range to put into your Array
Arr = Rng.Value
For i = LBound(Arr) To UBound(Arr)
TestLenght = Arr(i, 1)
If Len(Trim(TestLenght)) = 12 And IsNumeric(TestLenght) Then ' Test your data
LastrowResult = Result.Range("A" & Rows.Count).End(xlUp).Row + 1
Result.Cells(LastrowResult, "A") = TestLenght ' Past your data from your array to the Result Sheet
End If
Next ' next data of the Array
End With
End Sub
I think the problem may be that formatting the numbers to display 0 decimal places is not the same as truncating them. The Len() function will operate on the actual contents (or true value) of the cell, not the displayed value. So if you do have decimals on those numbers, Len() will return a value greater than 12, as it'll count the decimal place and the decimals.
If that is the issue, you'll need to round to 0 decimal places (or truncate to integer) in order to force the actual cell contents to a length of 12.

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