Last row offset within a filter - vba

So the purpose of my macro is to add a version number to a part number. First a user will input all the data then my macro will create a new entry with that information at the bottom of the table. Then it will filter the table based on part number. Still within the filter I need it to look at the previous entry and add 1 for my current entry. For example if the previous was 01 the next would need to be 02 and so on. I am running into trouble getting my macro to run the add 1 part below is my code. I am thinking that it may not be possible within a filter or I have to write my code differently. Any insight would be awesome thanks!
'Update version number
Sheets("New Version ").Select
part = Range("B4").Value
Sheets("PN_List").Select
ActiveSheet.Range("$A$1:$K$3000").AutoFilter Field:=1, Criteria1:=part
ActiveWorkbook.Worksheets("PN_List").AutoFilter.Sort.SortFields.Clear
ActiveWorkbook.Worksheets("PN_List").AutoFilter.Sort.SortFields.Add Key:= _
Range("B1:B3000"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption _
:=xlSortNormal
With ActiveWorkbook.Worksheets("PN_List").AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Worksheets("PN_List").Activate
With Range("B" & Rows.Count).End(xlUp).Offset(1)
.Value = "0" & .Offset(-1).Value + 1
End With

Let's re-write your code a bit.
Dim wsNewVersion = Thiwworkbook.Sheets("New Version")
Dim wsPNList = Thiwworkbook.Sheets("PN_List")
part = wsNewVersion.Range("B4").Value
wsPNList.Range("A1:K3000").AutoFilter Field:= 1, Criteria1:=part
wsPNList.AutoFilter.Sort.SortFields.Clear
wsPNList.AutoFilter.Sort.SortFields.Add Key:= _
wsPNList.Range("B1:B3000"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption _
:=xlSortNormal
With wsPNList.AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
'~~> Below code is what you need i guess
Dim myrng As Range, lrow As Long
Dim myadd
'~~> Get the Range Address of the visible cells only
lrow = wsPNList.Range("A" & Rows.Count).End(xlUp).Row
Set myrng = wsPNList.Range("B1", Range("B" & lrow)).Offset(1, 0).Resize(lrow - 1).SpecialCells(xlCellTypeVisible)
'~~> pass addresses in array
myadd = Split(myrng.Address, ",")
'~~> Now you know the address, you can assign the value
With wsPNList
.Range(myadd(UBound(myadd))).Value = Range(myadd(UBound(myadd) - 1)).Value + 1
End With
Hope this is what you need.
I believe it is :) but who knows.

My guess is your cell is formatted as "General"
Right-click the cell, go to Format Cells. Change the Category to "text"
or, if you want it done within VBA, add this:
.Value = "0" & .Offset(-1).Value + 1
.Style = "Text"

Per request:
'Update version number
Sheets("New Version ").Select
part = Range("B4").Value
Sheets("PN_List").Select
ActiveSheet.Range("$A$1:$K$3000").AutoFilter Field:=1, Criteria1:=part
ActiveWorkbook.Worksheets("PN_List").AutoFilter.Sort.SortFields.Clear
ActiveWorkbook.Worksheets("PN_List").AutoFilter.Sort.SortFields.Add Key:= _
Range("B1:B3000"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption _
:=xlSortNormal
With ActiveWorkbook.Worksheets("PN_List").AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Worksheets("PN_List").Activate
With Range("B" & Rows.Count).End(xlUp).Offset(1)
' following line was added to set text format in the target cell
.NumberFormat = "#"
.Value = "0" & .Offset(-1).Value + 1
End With

Related

Excel VBA automating Merge and Centering, Sorting

I am trying to automate a report generation process in excel.
So, let me give you guys a background:-
I have 3 columns in my datasheet:
Column A is manufacturing location
Column B is Vehicle line
Column C is Progress Update
I trying to generate a report which merges and centers MFG Locations in Column A and the same time Merge and Centers Vehicle lines in that plant with Column B
I am attaching a sample of the output I need. As of now , I am manually doing this process, I hope someone can guide me with automating this process
This code will help you what you have asked.
Sub MergeSameCells()
Dim Rng As Range
Dim xRows, lastRow As Integer
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
Range("A2:C" & lastRow).Select
With ActiveWorkbook.ActiveSheet.Sort
With .SortFields
.Clear
.Add Key:=Range("A2:A" & lastRow), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.Add Key:=Range("B2:B" & lastRow), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.Add Key:=Range("C2:C" & lastRow), SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:=xlSortNormal
End With
.SetRange Range("A2:C" & lastRow)
.Header = xlGuess
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Set WorkRng = Range("A2:B" & lastRow)
xRows = WorkRng.Rows.Count
If WorkRng Is Nothing Then Exit Sub
Application.ScreenUpdating = False
Application.DisplayAlerts = False
For Each Rng In WorkRng.Columns
For i = 1 To xRows - 1
For j = i + 1 To xRows
If Rng.Cells(i, 1).Value <> Rng.Cells(j, 1).Value Then
Exit For
End If
Next
WorkRng.Parent.Range(Rng.Cells(i, 1), Rng.Cells(j - 1, 1)).Merge
i = j - 1
Next
Next
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
Try and let me know if it works.

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

Delete rows that DON'T meet VBA criteria

I have many worksheets with sequential linear xy data that vary in length. The objective is to delete all rows where x data is not divisible by 50. Below is the generated macro that uses a helper column to search for integers to be deleted.
Sub Divis50()
Sheets("VERT SCALES").Select
Range("C2").Select
ActiveCell.FormulaR1C1 = _
"=IF((OR((RIGHT(RC[-2],2)=""50""),(RIGHT(RC[-2],2)=""00""))),""YES"",""NO"")"
Range("C2").Select
Selection.AutoFill Destination:=Range("C2:C6062")
'sort filtered results
Range("C2").Select
ActiveWorkbook.Worksheets("VERT SCALES").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("VERT SCALES").Sort.SortFields.Add Key:=Range("C2") _
, SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("VERT SCALES").Sort
.SetRange Range("A2:C6062")
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
' scroll to first no and delete rows
Rows("123:123").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.ClearContents
'sort "A" back to consecutive numbers
Range("A2").Select
ActiveWorkbook.Worksheets("VERT SCALES").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("VERT SCALES").Sort.SortFields.Add Key:=Range("A2") _
, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("VERT SCALES").Sort
.SetRange Range("A2:C122")
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
'delete filtered column
Columns("C:C").Select
Selection.Delete Shift:=xlToLeft
End Sub
This will delete rows that don't equal a whole number when divided by 50
Sub Button1_Click()
Dim FrstRng As Range, Lrw As Long
Dim UnionRng As Range
Dim c As Range
Lrw = Cells(Rows.Count, "A").End(xlUp).Row
Set FrstRng = Range("A2:A" & Lrw)
For Each c In FrstRng.Cells
If Int(c / 50) / (c / 50) <> 1 Then
If Not UnionRng Is Nothing Then
Set UnionRng = Union(UnionRng, c) 'adds to the range
Else
Set UnionRng = c
End If
End If
Next c
UnionRng.EntireRow.Delete
End Sub
I would recommend a helper column that flags your data appropriately. Either through formula or VB.
Then use an Autofilter to select the the flags and then delete.
try here for sample code that will delete filtered data.
http://www.mrexcel.com/forum/excel-questions/460513-visual-basic-applications-code-delete-only-rows-filtered.html

VBA to sort table and ignore total row

I have the range Table3 as shown below:
The rows are not fixed and could increase or decrease, I have thus created it as a table Table3 to accommodate this behavior and also so I could use it in a VBA as a ListObjects.
The VBA below is meant to sort the table, however because the Totals is part of the range, the sort doesn't work as intended.
Sub sort()
ActiveWorkbook.Worksheets("Project 2013").ListObjects("Table3").sort.SortFields _
.Clear
ActiveWorkbook.Worksheets("Project 2013").ListObjects("Table3").sort.SortFields _
.Add Key:=Range("Table3[Description3]"), SortOn:=xlSortOnValues, Order:= _
xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Project 2013").ListObjects("Table3").sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
Can someone please help modify the code to ignore the Totals row (i.e to include only the range below the header and above the Totals row) before applying the sort
EDIT
At the moment, this is my attempt at redefining a new range without the last row
Sub sort()
Dim resizedTable As ListObject
Set resizedTable = Sheets("Sheet1").ListObjects("Table1")
With resizedTable
.Resize .Range.Resize(.Range.Rows.Count - 1, .Range.Columns.Count)
End With
resizedTable.sort.SortFields.Clear
resizedTable.sort.SortFields _
.Add Key:=Range("resizedTable[Description]"), SortOn:=xlSortOnValues, Order:= _
xlAscending, DataOption:=xlSortNormal
   
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End Sub
Any help will be appreciated.
Set a new range for your table, just one row shorter » totalRowCount - 1.
Here, x is your input range
Set x = Range(x.Cells(1, 1), x.Cells(x.Rows.Count - 1, x.Columns.Count))
or use the resize method
Sub CutOffLastLine()
With ActiveWorkbook.Worksheets("Project 2013").ListObjects("Table3")
.Resize .Range.Resize(.Range.Rows.Count - 1, .Range.Columns.Count)
End With
End Sub