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)
Related
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
I've been toying with some code and I can't, for the life of me, get it to do exactly what I want it to do. I would like it to Compare WB1(Master) to WB2(Daily), if new records found in WB2, highlight the entire row and copy(append) all new rows over to WB1. The code I have right now highlights all the new data in WB2 but it will only copy the last new row from WB2 to WB1. I'm sure I'm missing something simple but would appreciate the 2nd set of eyes. Thank you!
Sub compare_files()
Dim c As Range
Dim masterWB, dailyWB As Workbook
Dim mWS, dWS As Worksheet
Dim lRow, lRow2 As Long
Set masterWB = Workbooks.Open("masterFile.xlsx")
Set dailyWB = Workbooks("dailyFile.xlsx")
Set mWS = masterWB.Sheets(1)
Set dWS = dailyWB.Sheets(1)
'remove useless columns first
dWS.Columns("C:D").Delete Shift:=xlToLeft
dWS.Columns("D:F").Delete Shift:=xlToLeft
dWS.Columns("G:K").Delete Shift:=xlToLeft
dWS.Columns("H:I").Delete Shift:=xlToLeft
dWS.Columns("I:K").Delete Shift:=xlToLeft
dWS.Columns("O:AR").Delete Shift:=xlToLeft
dWS.Columns("P:W").Delete Shift:=xlToLeft
dWS.Columns("Q").Delete Shift:=xlToLeft
dWS.Columns("T").Delete Shift:=xlToLeft
dWS.Columns("AB:AI").Delete Shift:=xlToLeft
'sort Certified Timestamp Column TopToBottom
dWS.ListObjects("Table1").Sort. _
SortFields.Clear
dWS.ListObjects("Table1").Sort. _
SortFields.add Key:=Range("Table1[[#All],[Certified Timestamp]]"), SortOn:= _
xlSortOnValues, Order:=xlAscending, DataOption:=xlSortTextAsNumbers
With dWS.ListObjects("Table1").Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
lRow = mWS.Cells(Rows.Count, "A").End(xlUp).Row
lRow1 = dWS.Cells(Rows.Count, "A").End(xlUp).Row
For Each c In dWS.Range("A2", dWS.Range("A" & Rows.Count).End(xlUp))
If Not c.Value = mWS.Cells(c.Row, c.Column).Value Then
c.EntireRow.Interior.Color = vbYellow
c.EntireRow.Copy mWS.Range("A" & lRow)
End If
Next c
mWS.Activate
End Sub
You are not updating your "lrow" variable in your For loop after pasting a new row, so you are likely pasting over the same location every time.
Reassign "lrow" in your If Not statement to account for newly added rows.
For Each c In dWS.Range("A2", dWS.Range("A" & Rows.Count).End(xlUp))
If Not c.Value = mWS.Cells(c.Row, c.Column).Value Then
c.EntireRow.Interior.Color = vbYellow
c.EntireRow.Copy mWS.Range("A" & lRow)
lRow = mWS.Cells(Rows.Count, "A").End(xlUp).Row
End If
Next c
Does that solve it?
EDIT: As pointed out below, .Cells.End.xlUp finds the last occupied row. You need to increment lrow+1 to point to the first empty row.
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
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.
I have two columns in spreadsheet1:
Col1 Col2
1 PDC
2 SR3
3 PDC
4 VBM
5 VBM
6 GAL
7 VBM
8 GAL
9 PDC
I have 1 column in spreadsheet2:
Col1
PDC
SR3
VBM
GAL
How can I sort Col1 and Col2 from spreadsheet1 based on the order in spreadsheet2 Col1?
Try this code:
Sub test()
Dim wb As Workbook
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim lastrow As Long
Dim rng As Range
'if workbook2 is already opened
Set wb = Workbooks("Book2") ' change Book2 to suit
'if workbook2 is not opened
'Set wb = Workbooks.Open("C:\Book2.xlsx")
'change sheet1 to suit
Set ws1 = ThisWorkbook.Worksheets("Sheet1")
'change sheet2 to suit
Set ws2 = wb.Worksheets("Sheet2")
With ws1
'change column B to column with your values "PDC", "SR3" and so on
lastrow = .Cells(.Rows.Count, "B").End(xlUp).Row
'change to address of range you wnat to sort
Set rng = .Range("A1:B" & lastrow)
With rng.Offset(, rng.Columns.Count).Resize(, 1)
.EntireColumn.Insert
.Offset(, -1).FormulaR1C1 = "=MATCH(RC[-1],'[" & wb.Name & "]" & ws2.Name & "'!C1:C1,0)"
.Offset(, -1).Value = .Offset(, -1).Value
End With
With rng.Resize(, rng.Columns.Count + 1)
.Sort Key1:=.Cells(1, .Columns.Count), Order1:=xlAscending, Header:=xlGuess
.Cells(1, .Columns.Count).EntireColumn.Delete
End With
End With
wb.Close
End Sub
Explanaiton:
There're two workbooks. Code should be places in first workbook (where you want to sort range)
The main idea is to add temporary column with formula like =MATCH(B1,[Book2]Sheet2!A:A,0) to get row numbers of values from column B (workbook1) in column A (workbook2).
Sort range based on this numbers.
Delete temporary column
Notes:
Next line assumed that column with "PDC", "SR3" (in workbook1) is last in selected range (Set rng = .Range("A1:B" & lastrow)):
.Offset(, -1).FormulaR1C1 = "=MATCH(RC[-1],'[" & wb.Name & "]" & ws2.Name & "'!C1:C1,0)"
if it's not true, change RC[-1] to, say RC[-2] if this column is last but one and so on.
C1:C1 part of formula means that in workbook2 column with "PDC", "SR3" is column A (column №1). If it's not true, change it to, say, C5:C5, which means that column is E (column №5).
I found this code which seems a bit simpler and works great.
Sub NewSortTest()
Dim keyRange As Variant
Dim sortNum As Long
keyRange = ActiveWorkbook.Worksheets("Sheet2").Cells.Range("A1:A10").Value
Application.AddCustomList ListArray:=keyRange
sortNum = Application.CustomListCount
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Add key:=Range("A1:A20"), SortOn:=xlSortOnValues, Order:=xlAscending, CustomOrder:=sortNum, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Sheet1").Sort
.SetRange Range("A1:B20")
.Header = xlGuess
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub