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

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...

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

How to speed up vba code that delete rows when column Q has blank cells

I have a sheet of almost 100000 rows & column A to Q
I have a code that delete entire rows if column Q has blank cells.
I have tried this code on 4000 rows it is running in 3 minutes but when I take 100000 rows it just processing for hours.
I will be very great full if some help/guide me in speeding up this code.
The code is :
Sub DeleteBlank()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim lo As ListObject
set lo = sheets("BOM 6061").ListObjects(1)
Sheets("BOM 6061").Activate
lo.AutoFilter.ShowAllData
lo.range.AutoFilter Field:=17, Criteria1:=""
Application.DisplayAlerts = False
Application.Calculation = xlCalculationAutomatic
lo.DataBodyRange.SpecialCells(xlCellsTypeVisible).Delete
Application.DisplayAlerts = True
lo.AutoFilter.ShowAllData
End Sub
Remove Criteria Rows in an Excel Table Efficiently
In a nutshell, if you don't sort the criteria column, deleting the rows may take 'forever'.
The following will do just that, keeping the initial order of the remaining rows.
Option Explicit
Sub DeleteBlankRows()
Const wsName As String = "BOM 6061"
Const tblIndex As Variant = 1
Const CriteriaColumnNumber As Long = 17
Const Criteria As String = ""
' Reference the table.
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim ws As Worksheet: Set ws = wb.Worksheets(wsName)
Dim tbl As ListObject: Set tbl = ws.ListObjects(tblIndex)
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
' Remove any filters.
If tbl.ShowAutoFilter Then
If tbl.AutoFilter.FilterMode Then tbl.AutoFilter.ShowAllData
Else
tbl.ShowAutoFilter = True
End If
' Add a helper column and write an ascending integer sequence to it.
Dim lc As ListColumn: Set lc = tbl.ListColumns.Add
lc.DataBodyRange.Value = _
ws.Evaluate("ROW(1:" & lc.DataBodyRange.Rows.Count & ")")
' Sort the criteria column ascending.
With tbl.Sort
.SortFields.Clear
.SortFields.Add2 tbl.ListColumns(CriteriaColumnNumber).Range, _
Order:=xlAscending
.Header = xlYes
.Apply
End With
' AutoFilter.
tbl.Range.AutoFilter Field:=CriteriaColumnNumber, Criteria1:=Criteria
' Reference the filtered (visible) range.
Dim svrg As Range
On Error Resume Next
Set svrg = tbl.DataBodyRange.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
' Remove the filter.
tbl.AutoFilter.ShowAllData
' Delete the referenced filtered (visible) range.
If Not svrg Is Nothing Then svrg.Delete
' Sort the helper column ascending.
With tbl.Sort
.SortFields.Clear
.SortFields.Add2 lc.Range, Order:=xlAscending
.Header = xlYes
.Apply
.SortFields.Clear
End With
' Delete the helper column.
lc.Delete
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
' Inform.
MsgBox "Blanks deleted.", vbInformation
End Sub
I would not use an Autofilter on large data sets as they can take quite a bit of time trying to enumerate the available options before actually filtering the data. The AutoFilter.ShowAllData takes just as much time. For my super simple test dataset, which consisted of 26 columns of 1000000 rows, it took 30+ seconds for each to process.
From what I can tell you are filtering the list to show only the blank items and then deleting the blank rows. Since the filtering is what is causing the delay we could just loop through each row looking at a specific column and if it is blank you can just delete it. Below is an example of how to do this.
**Edit: After testing I found this to be much slower than what you would want. Check out the next example below as it is super fast.
Option Explicit
Sub DeleteBlank()
Application.ScreenUpdating = False
Dim calcType As Integer
Dim rowCount, columnNumToCheck, currow, dataStartRow As Long
Dim WkSht As String
Dim lo As ListObject
WkSht = "BOM 6061" 'The name of the worksheet where the table is located.
columnNumToCheck = 17 'The column number to check for blank cells.
calcType = Application.Calculation
Application.Calculation = xlCalculationManual
Set lo = Sheets(WkSht).ListObjects(1)
rowCount = lo.ListRows.Count
dataStartRow = (lo.DataBodyRange.Row - 1)
For currow = rowCount To 1 Step -1
If Sheets(WkSht).Cells((currow + dataStartRow), columnNumToCheck).Value = "" Then
Call DeleteRows(WkSht, (currow + dataStartRow))
End If
Next currow
Application.Calculation = calcType
Application.ScreenUpdating = True
End Sub
Private Sub DeleteRows(sheetNameIn As String, startRow As Long, Optional optionalEndRow As Long)
If IsNull(optionalEndRow) Or optionalEndRow = 0 Then
optionalEndRow = startRow
End If
Worksheets(sheetNameIn).Range(startRow & ":" & optionalEndRow).Delete Shift:=xlUp
End Sub
If you are able to sort your data where the blank cells are all together you could use the below to perform a single delete function remove them all at once. This deleted 70000 rows in a few seconds.
Sub DeleteBlankWithSort()
'Application.ScreenUpdating = False
Dim columnNumToCheck, tableLastRow, lrow As Long
Dim calcType As Integer
Dim WkSht As String
Dim lo As ListObject
WkSht = "BOM 6061" 'The name of the worksheet where the table is located.
columnNumToCheck = 17 'The column number to check for blank cells.
calcType = Application.Calculation
Application.Calculation = xlCalculationManual
Set lo = Sheets(WkSht).ListObjects(1)
tableLastRow = FindLastRow(WkSht, (columnNumToCheck))
With lo.Sort
.SortFields.Clear
.SortFields.Add _
Key:=Range("Table1[[#All],[q]]"), _
SortOn:=xlSortOnValues, _
Order:=xlDescending, _
DataOption:=xlSortNormal
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
lrow = FindLastRow(WkSht, (columnNumToCheck), (tableLastRow))
Call DeleteRows(WkSht, (tableLastRow), (lrow + 1))
Application.Calculation = calcType
Application.ScreenUpdating = True
End Sub
Private Sub DeleteRows(sheetNameIn As String, startRow As Long, Optional optionalEndRow As Long)
If IsNull(optionalEndRow) Or optionalEndRow = 0 Then
optionalEndRow = startRow
End If
Worksheets(sheetNameIn).Range(startRow & ":" & optionalEndRow).Delete Shift:=xlUp
End Sub
Private Function FindLastRow(sheetNameIn As String, columnNum As Long, Optional optionalStartRow As Long) As Long
'finds the last row of the column passed in the sheetname passed in
If IsNull(optionalStartRow) Or optionalStartRow = 0 Then
optionalStartRow = 1048576
End If
FindLastRow = Worksheets(sheetNameIn).Range(Cells(optionalStartRow, columnNum).Address).End(xlUp).Row
End Function
I had an simple example of this from a while ago. Advanced filtering is the fastest way to filter in place or to filter and copy in excel/vba. In advanced filtering you usually have your filters listed out in columns/rows and can have as many as you need, use >"" for filtering out blanks on a column, should take no time at all. In my example it might be different as this was used alongside sheetchange to autofilter if anything was added to the filters.
Sub Advanced_Filtering_ModV2()
Dim rc As Long, crc As Long, trc As Long
Dim wb As Workbook, ws As Worksheet
Set wb = ActiveWorkbook: Set ws = wb.Worksheets("sheet1")
ws.Range("AA1").Value = ws.Range("Q1").Value: ws.Range("AA2").Value = ">"""""
On Error Resume Next
ws.ShowAllData: rc = ws.Range("A" & Rows.Count).End(xlUp).Row
ws.Range("A1:V" & rc).AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:=ws.Range("AA1:AA2")
On Error GoTo 0
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.

Excel Auto Sort

Okay so I'm a newbie to VBA (actionscript no problem - so not new to coding, just VBA) and have been working trying learn what I can and get some code to work for me. I have a report in Excel. Row 6 is the header, 7 blank and 8 is additional information; the actual data begins on row 9 (B9:D9) downwards. Colum C contains the data I'd like to sort by. As new data gets entered in subsequent rows I'd like for the report to automatically re-sort after I've completed the data entry in D#. (Hope this makes sense)
Is this possible in VBA? Or am I asking too much?
Many thanks.
Here's what I've been working with:
`Private Sub Worksheet_Change(ByVal Target As Range)
Dim CCell As Range
Dim ActiveCellInTable As Boolean
Dim r As Single
Dim Value As Variant
Rows("9:38", "B:E").Select
Set CCell = Target
On Error Resume Next
ActiveCellInTable = (CCell.ListObject.Name = "Table2")
On Error GoTo 0
If ActiveCellInTable = True Then
r = Target.Row - Target.ListObject.Range.Row + 9
For c = 1 To ActiveSheet.ListObjects(CCell.ListObject.Name).Range.Columns.Count
If ActiveSheet.ListObjects(CCell.ListObject.Name).Range.Cells(r, c).Value = "" Then Exit Sub
Next c
With ActiveSheet.ListObjects(CCell.ListObject.Name).Sort
.SortFields.Clear
.SortFields.Add _
Key:=Range("Table2[[#All],[Item number]]"), SortOn:=xlSortOnValues, Order _
:=xlAscending, DataOption:=xlSortNormal
.Apply
End With
End If
End Sub`
Yes it's possible and here's my version of what you're trying to do. 'Worksheet_Change' has to go in Sheet1 code and will get executed each time any cell in the worksheet changes. Just to be safe you should disable events otherwise the code could get interrupted prematurely by a different spreadsheet event:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim lastRow As Integer
Dim entireRange As Range, sortRange As Range
Application.EnableEvents = False
lastRow = ActiveSheet.Cells(ActiveSheet.Rows.Count, "B").End(xlUp).row
If Not Intersect(Target, Me.Range("D9:D" & lastRow)) Is Nothing Then
Set entireRange = Range("B9:D" & lastRow)
Set sortRange = Range("C9:C" & lastRow)
entireRange.Sort Key1:=sortRange, Order1:=xlAscending, Header:=xlNo
End If
Application.EnableEvents = True
End Sub

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