Sorting a Range based on sheet variable with VBA - excel

Scenario: I have a sheet and am trying to sort a part of it. Inside the sheet I have a dropdown list, which allows me to select the values (aaa, bbb, ccc). Each of those values represent a column and when selected, the code should sort the range by that column.
Problem: The process works for one of the values in the dropdown list, but not for the others (it runs, but nothing happens).
Code:
Sub ratecolumnssort()
Dim LastRow As Integer
Dim sortColumn As String, sortAgent As String
shtMonitoring.Activate
LastRow = shtMonitoring.Cells(shtMonitoring.rows.count, "B").End(xlUp).row
sortAgent = shtMonitoring.Cells(8, 9) ' this is where the dropdown with values aaa, bbb and ccc is
If sortAgent = "aaa" Then
sortColumn = "F"
ElseIf sortAgent = "bbb" Then
sortColumn = "H"
ElseIf sortAgent = "ccc" Then
sortColumn = "J"
End If
With ActiveSheet.sort
.SortFields.Add key:=Range(sortColumn & "11"), Order:=xlAscending
.SetRange Range("B11", "N" & LastRow )
.Header = xlYes
.Apply
End With
End Sub
Question: What am I doing wrong here?

You need to clear the sort settings first.
With ActiveSheet.Sort
.SortFields.Clear 'add this line
'as before

Related

VBA Select columns of visible rows from filtered table into another sheet

I wanted to add the following: After having sorted the Table I apply a Filter on it. I only require the information of 2 columns of the visible rows. and want to copy that inforation to another sheet.
This is what I have got so far but I am not able to selected the visible Rows from the filtered table.
And here is an image of the filtered Data. I need the data of Column "A" and "H" without the Header
I tried selecting the range of the ActiveSheets Listobject with the SpecialCellValue parameter but i couldnt figure out how to grab it.
Sub TabelleEinzeln()
Dim rep As Long
Dim table_name As String
Dim outcome_KW As String
For rep = Range("O4").Value To Range("P4").Value
Dim SortOrder As Integer
Dim sheet_name As String
sheet_name = Sheets("Admin").Range("H" & rep).Value
table_name = Sheets("Admin").Range("I" & rep).Value
outcome_KW = Sheets("Admin").Range("L" & rep).Value
Sheets(sheet_name).Select
Dim lo As ListObject
Set lo = ActiveSheet.ListObjects.Add(xlSrcRange, Range("$A$8:$K$2276"), , xlYes)
' Example how to sort
With lo.Sort
.SortFields.Clear
.SortFields.Add Key:=lo.DataBodyRange.Columns(1), SortOn:=xlSortOnValues, Order:=xlAscending
.Header = xlYes
.Apply
End With
'Top 5 höchsten Werte und sortiert
lo.Range.AutoFilter Field:=8, Criteria1:= _
"5", Operator:=xlTop10Items
'Here i want to selected the filtered outcome and past it to the new sheet
'.Copy Sheets("KW_Bericht_Report").Range("outcome_KW")
Next rep
Sheets("Admin").Select
MsgBox ("Tables have been created for every imported sheet")
End Sub

Value of cell that depends on another dynamic cell within a range

Range A1:A5 is filled with dynamic data, though the data is limited to 5 values, the sequence could differ. It is also possible that only 4 or less is presented. The values are also unique.
Column B's value will depend on Column A.
Example:
A B
1 item2 USD18
2 item1 USD15
3 item3 USD4
4 item5 USD23
5 item4 USD11
How do I accomplish this on VBA?
Quite tricky. Please try this code after adjusting the items marked "change to suit".
Sub SetSequence()
' 156
Const DataClm As Long = 2 ' change to suit (2 = column B)
Const ItemClm As Long = 1 ' change to suit (1 = column A)
Dim Wb As Workbook
Dim Ws As Worksheet
Dim DataRng As Range ' sorted given data (column B in your example)
Dim Results As Variant ' results: sorted 1 to 5
Dim TmpClm As Long ' a column temporarily used by this macro
Dim Tmp As String ' working string
Dim R As Long ' oop counter: rows
Dim i As Long ' index of Results
Results = Array("Item1", "Item2", "Item3", _
"Item4", "Item5") ' modify list items as required (sorted!)
Set Wb = ThisWorkbook ' modify as needed
Set Ws = Wb.Worksheets("Sheet1") ' change to suit
With Ws
With .UsedRange
TmpClm = .Column + .Columns.Count
End With
' create a copy of your data (without header) in an unused column
.Range(.Cells(2, DataClm), .Cells(.Rows.Count, DataClm).End(xlUp)) _
.Copy .Cells(1, TmpClm)
Set DataRng = .Range(.Cells(1, TmpClm), .Cells(.Rows.Count, TmpClm).End(xlUp))
With .Sort.SortFields
.Clear
.Add2 Key:=Ws.Cells(1, TmpClm), _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortNormal
End With
With .Sort
.SetRange DataRng
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
' blanks are removed, if any
Set DataRng = .Range(.Cells(1, TmpClm), .Cells(.Rows.Count, TmpClm).End(xlUp))
' start in row 2 of DataClm and look at next 5 cells
For R = 2 To 6
' skip over blanks
Tmp = .Cells(R, DataClm).Value
If Len(Trim(Tmp)) Then
i = WorksheetFunction.Match(Tmp, DataRng, 0)
.Cells(R, ItemClm).Value = Results(i - 1)
End If
Next R
.Columns(TmpClm).ClearContents
End With
End Sub
The code creates a sorted copy of the items you have in column B and draws the output in column A from the similarly sorted list of results. Blanks are ignored. But if there is one blank in the input list (column B) there will be only 4 items in the sorted input list and therefore none of the items can be assigned "Item 5" in column A.
I've replaced my answer with the one below from your edit which completely changed things.
See if this is what you're looking for:
Dim ValueArr As Variant
ValueArr = Array("USD15", "USD18", "USD4", "USD11", "USD23")
For i = 1 To 5
If Range("A" & i) <> "" Then
Range("B" & i) = ValueArr(Right(Range("A" & i), 1) - 1)
End If
Next i
This code is based off of using the number on the end of item to know which value to put in place. If the row is blank it will skip over it.

Apply greater than logic to already autofiltered rows

I have applied the below filter to an excel sheet which autofilters the column O based on start date and end date.
sht1.Range("$A$1:$X$3432").AutoFilter Field:=15, Criteria1:= _
">=" & CDbl(StartDate), Operator:=xlAnd, Criteria2:="<=" & CDbl(EndDate)
here I have calculated the visible filtered rows based on start date and end date.
With sht1
Total_DCR = WorksheetFunction.Subtotal(102, ActiveSheet.Range("O1:O5000").Columns(1))
Debug.Print Total_DCR
End With
Dim i, delay_count As Integer
Now I want to compare the dates present in filtered column O and X for greater than logic.If O2 >X2 then increment a counter by 1.
For i = 2 To Total_DCR
If sht1.Range("O" & i).Value > sht1.Range("X" & i).Value Then
delay_count = delay_count + 1
Debug.Print delay_count
End If
Next
After executing the above comparison code the count of greater than dates from O column is showing wrong data. I feel it is considering the hidden rows as well. In O column there are 79 dates which are between start date and end date. when I opt for greater than logic between filtered O and X for greater than logic using IF(O2>X2,"YES","NO") logic the greater than rows counts to 53.
I want to implement the same using vba code. But I am getting greater than rows count to 76. I dont know what is wrong here. kindly help
You can get the visible cells without header:
Sub ZZZ()
Dim rng As Range, rngVisible As Range, rngRow As Range, delay_count As Double
Set rng = Range("$A$1:$X$3432")
'// Get visible cells excluding header
With rng
Set rngVisible = .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible)
End With
'// Must use "Cells" when dealing with "Rows" property
For Each rngRow In rngVisible.Rows
delay_count = delay_count + IIf(rngRow.Cells(1, "O") > rngRow.Cells(1, "X"), 1, 0)
Next
End Sub
Private Sub CommandButton1_Click()
Dim F11, F22, Month, Year As Variant
Dim f_name1, f_name2
Dim wb1, wb2, wb3, wb4 As Workbook
Dim sht1, sht2, sht3, sht4 As Worksheet
Dim Total_DCR As Long
Dim StartDate, EndDate As Date
'Select Product Backlog File
F11 = Application.GetOpenFilename("check (*.xlsm*), *.xlsm*")
If (F11 <> vbNullString) Then
If (F11 <> "False") Then
f_name1 = F11
End If
End If
If (f_name1 = "") Then
MsgBox "The check file must be specified."
Exit Sub
End If
Set wb1 = Excel.Workbooks.Open(f_name1)
Set sht1 = wb1.Sheets("Product Backlog")
Set wb3 = ThisWorkbook
Set sht3 = wb3.Sheets("check")
With sht3
StartDate = sht3.Range("J3").Value
'Debug.Print StartDate
If IsDate(StartDate) = True Then
MsgBox ("The following string is a valid date expression")
Else
'if its not a date expression show a message box
MsgBox ("The following string is not a valid date expression")
End If
EndDate = sht3.Range("J4").Value
'Debug.Print EndDate
End With
wb1.Activate
sht1.Activate
sht1.Columns("O:O").Select
wb1.Worksheets("Product Backlog").Sort.SortFields.Clear
wb1.Worksheets("Product Backlog").Sort.SortFields.Add2 Key:=Range( _
"O1"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
With wb1.Worksheets("Product Backlog").Sort
.SetRange Range("O1:O3437")
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
sht1.Range("$A$1:$X$3432").AutoFilter Field:=15, Criteria1:= _
">=" & CDbl(StartDate), Operator:=xlAnd, Criteria2:="<=" & CDbl(EndDate)
With sht1
Total_DCR = WorksheetFunction.Subtotal(102, ActiveSheet.Range("O1:O5000").Columns(1))
Debug.Print Total_DCR
End With
Dim rng As Range, rngVisible As Range, rngRow As Range, delay_count As Double
Set rng = Range("$A$1:$X$5000")
'// Get visible cells excluding header
With rng
Set rngVisible = .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible)
End With
'// Must use "Cells" when dealing with "Rows" property
For Each rngRow In rngVisible.Rows
delay_count = delay_count + IIf(rngRow.Cells(1, "O") > rngRow.Cells(1, "X"), 1, 0)
Next
MsgBox ("total delay DCR is" & delay_count)
End Sub
#JohnyL this is my whole code. The check work book contains random date from year 2017 to 2020 as per every month in Col O. same way Col X contains date as well. The code must filter them for February 2020 dates and then compare between O and X dates.
start date:01-02-2020
end date:20-02-2020
All the A-X columns have filter dropdown added. when autofilter is applied it will filter the february 2020 dates only. A-X row is there in first row with headers
#EylM Would you mind answering this

How to delete rows in an Excel ListObject based on criteria using VBA?

I have a table in Excel called tblFruits with 10 columns and I want to delete any rows where the Fruit column contains Apple. How can I do this?
The following sub works:
Private Sub deleteTableRowsBasedOnCriteria(tbl As ListObject, columnName As String, criteria As String)
Dim x As Long, lastrow As Long, lr As ListRow
lastrow = tbl.ListRows.Count
For x = lastrow To 1 Step -1
Set lr = tbl.ListRows(x)
If Intersect(lr.Range, tbl.ListColumns(columnName).Range).Value = criteria Then
'lr.Range.Select
lr.Delete
End If
Next x
End Sub
The sub can be executed like this:
Dim tbl As ListObject
Set tbl = ThisWorkbook.Worksheets("Sheet1").ListObjects("tblFruits")
Call deleteTableRowsBasedOnCriteria(tbl, "Fruit", "Apple")
Well, it seems the .listrows property is limited to either ONE list row or ALL list rows.
Easiest way I found to get around this was by:
Setting up a column with a formula that would point out to me all rows I would like to eliminate (you may not need the formula, in this case)
Sorting the listobject on that specific column (preferably making it so that my value to be deleted would be at the end of the sorting)
Retrieving the address of the range of listrows I will delete
Finally, deleting the range retrieved, moving cells up.
In this specific piece of code:
Sub Delete_LO_Rows
Const ctRemove as string = "Remove" 'value to be removed
Dim myLO as listobject, r as long
Dim N as integer 'number of the listcolumn with the formula
Set myLo = Sheet1.ListObjects("Table1") 'listobject goes here
With myLO
With .Sort
With .SortFields
.Clear
.Add Key:=.HeaderRowRange(myLO.ListColumns(N)), SortOn:= _
xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
End With
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
On Error GoTo NoRemoveFound
r = Application.WorksheetFunction.Match(ctRemove, .ListColumns(.ListColumns.Count).DataBodyRange, 0)
Range(.parent.name & "!" & .DataBodyRange(r, 1).Address & ":" & .DataBodyRange(.ListRows.Count, .ListColumns.Count).Address).Delete xlShiftUp
'Added the .parent.name to make sure the address is on the correct sure, but it will fail if there are any spaces or characters on the sheet name that will make it need a pair of '.
'The error is just to skip these two lines in case the match returns an error. There's likely a better/cleaner way to do that.
NoRemoveFound:
End With
End sub
Hope it helps...

Trying to refer to columns in Excel using variables. What function should I use?

Trying to sort data in Excel alphabetically by row, but NOT in all of the columns for each row.
For example: Sort J7 through U7 alphabetically, then J8 through U8, all the way down through J2000 through U2000, while keeping each item within its row (like all cells in row 7 should still be in row 7 by the end of it.)
It seems like I'll need to use a macro/VBA for this, as Excel only lets you sort one row at a time alphabetically. If I select more than one row to sort, it still only sorts the first row.
I have done a bit of research as far as what kind of macros I could use for this, but nothing seems to do the job. The one thing I found that was similar to this sorts by number, but can't sort alphabetically.
Here's what I've got at a moment. I recorded a macro of myself selecting and sorting the row so that I could hopefully get all the code for that right at least:
Sub Macro4()
'
' Macro4 Macro
'
' Keyboard Shortcut: Option+Cmd+j
'
Dim lngIndex As Long
Dim strArray(9 To 11000) As String
Dim intCounter As Integer
Dim x As Integer
intCounter = 1
x = 9
For lngIndex = LBound(strArray) To UBound(strArray)
intCounter = intCounter + 1
strArray(lngIndex) = intCounter
x = x + 1
Range("Jx:UNx").Select
ActiveWorkbook.Worksheets("export_729559 (3).xlsx").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("export_729559 (3).xlsx").Sort.SortFields.Add Key:= _
Range("Jx:UNx"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
With ActiveWorkbook.Worksheets("export_729559 (3).xlsx").Sort
.SetRange Range("Jx:UNx")
.Header = xlNo
.MatchCase = False
.Orientation = xlLeftToRight
.SortMethod = xlPinYin
.Apply
End With
Next
End Sub
I'm getting: Run-time error ‘9’: Subscript out of range And it's highlighting the line: ActiveWorkbook.Worksheets("export_729559 (3).xlsx").Sort.SortFields.Clear
I think the error I'm getting is because it's thinking I mean Column JX, rather than Column Jx, where x is a variable I was trying to use to cycle through each row. It seems like there are a few different ways to use variables instead of directly naming the columns, which I'll need to do to reference a new row through each iteration. Could someone recommend a certain function?
One of the ones I've come across is =OFFSET(), but I'm not sure if that would be a good fit for what I'm trying to do, and I'm having a hard time knowing how to fill it in with the right things.
Something like this:
Sub Macro4()
Dim lngIndex As Long
Dim strArray(9 To 11000) As String
Dim intCounter As Integer
Dim sht As Worksheet, rng As Range
Set sht = ActiveWorkbook.Worksheets("export_729559 (3).xlsx")
Set rng = sht.Range("J10:UN10")
intCounter = 1
For lngIndex = LBound(strArray) To UBound(strArray)
intCounter = intCounter + 1
strArray(lngIndex) = intCounter
With sht.Sort
.SortFields.Clear
.SortFields.Add Key:=rng, SortOn:=xlSortOnValues, _
Order:=xlAscending, DataOption:=xlSortNormal
.SetRange rng
.Header = xlNo
.MatchCase = False
.Orientation = xlLeftToRight
.SortMethod = xlPinYin
.Apply
End With
Set rng = rng.Offset(1, 0)
Next
End Sub
If you want to use x as a variable:
Range("J" & x & ":UN" & x)
If you put that in what you are using, should work.

Resources