Excel VBA Delete empty rows in table - vba

I have a table tabelaClientes in Sheet "Clientes" and I want to delete the rows where the field "Nome" is empty.
How do I do that?
This is what I'm trying:
Sub Cliente()
Dim ws As Worksheet
Dim row As Range
Set ws = Sheets("Clientes")
For Each row In ws.[tabelaClientes[Nome]].Rows
If row.Value = "" Then
row.Delete
End If
Next
Exit Sub
But this is deleting only some of the rows where Nome is empty, not all, why?

You can use a very simple call to SpecialCells() to do that instead of using a loop.
Range("tabelaClientes[Nome]").SpecialCells(xlCellTypeBlanks).EntireRow.Delete
Edit: To expand on my answer because I was in a hurry. SpecialCells mimic the menu that you will find in Excel after having pressed F5 and selected "Special cells... Blanks". This has the advantage of selecting all blanks at the same time and then delete the rows. Iteration can be very slow if your table is getting large thus this way will save a lot of time.
It does seem that you cannot delete multiple non-contiguous rows in a table. You can do either one of two things:
1- Convert back the table to a range and change the reference to a standard excel reference
2- Loop through the results of SpecialCells().
Option #2 will yield in slower code because of the loop but it will still be better than looping through all cells and check if they are blank but I can understand that you may need to keep it as a table.

Related

How to assign macro to a table column in Excel?

Currently I'm creating a macro that reads data from a table column, Asset No. The data is obtained through drop-down menu via data validation from another table in another sheet, DieMaster.
It will then perform Index and Match to find the matching data from DieMaster and insert it into Description column.
In addition, Upon obtaining data, the table column will then copy and paste as value to get the data only. That way the formula won't slow down filter searches.
This is what I have come up with.
Sub convert()
Dim osh As Worksheet
Set osh = ActiveSheet
osh.Range("ProjectEntry[Description]").Formula = "=IF(ISNA(INDEX(DieMaster,MATCH(B4,DieMaster[Asset No],FALSE),2)),"""",INDEX(DieMaster,MATCH(B4,DieMaster[Asset No],FALSE),2))"
osh.Range("ProjectEntry[Description]").Copy
osh.Range("ProjectEntry[Description]").PasteSpecial xlPasteValues
End Sub
I have tested the macro and it works perfectly. However the issue I have now is trying to assign the macro to Asset No column. My plan is to have it so that when a value is selected in a cell within the Asset No column via aforementioned drop-down menu, it will automatically show me the data for description.
Since I cannot assign macro to column using the traditional method, is there an alternative?
You can use the Worksheet_Change Event to accomplish this:
Private Sub Worksheet_Change(Target as Range)
If Not Intersect(Target, Me.ListObjects("ProjectEntry").ListColumns("AssetNo").DataBodyRange) Is Nothing Then
With Me.Range("ProjectEntry[Description]")
.Formula = "=IF(ISNA(INDEX(DieMaster,MATCH(B4,DieMaster[Asset No],FALSE),2)),"""",INDEX(DieMaster,MATCH(B4,DieMaster[Asset No],FALSE),2))"
.Value = .Value
End With
End If
End Sub
My syntax may be slightly off (since I am not on my normal computer), but it will get you close enough to tweak on your own.

Macro to adjust an entire row in an Excel table at once

I need a way to programatically adjust an entire row in an excel table based on the ActiveCell, irrespective of what table it's in or where the table is. I was using
ActiveSheet.ListObjects(1).ListRows(ActiveCell.Row - 1).Range.Select
Selection.Style = "Good"
but upon shifting the table down five rows, it now applies the action down a further five rows from the ActiveCell.
I've tried finding a way to replace the - 1 with some sort of - .HeaderRowRange.Row but nothing happens when I activate the macro.
You can take advantage of the ActiveCell.ListObject property. This makes it more flexible, e.g., if there's two tables in a worksheet:
Sub FormatActiveTableRow()
Dim lo As Excel.ListObject
Set lo = ActiveCell.ListObject
If Not lo Is Nothing Then
Intersect(ActiveCell.EntireRow, lo.Range).Style = "Good"
End If
End Sub
If I understood the problem correctly, try using the following code and report back:
ActiveSheet.ListObjects(1).ListRows(ActiveCell.Row - ActiveSheet.ListObjects(1).HeaderRowRange.Row).Range.Select

Copy/Paste For Loop

I am working on a for loop that takes an entry on the first page of a worksheet, and copy and pastes it to the remaining sheets in the workbook, with the array size being Worksheets.Count. Specifically, I am having trouble finding a way to call on each table in each specific worksheet. I was trying to select the table based on the counter in the for loop such that:
For i=1 to Worksheets.count
Range.("Table(Counter").Select
Next i
but it doesn't work. Is there a better way to make sure that my code selects the proper table in each successive worksheet? I am still pretty new to VBA, so any and all help is appreciated!
If you are using the counter, I am assuming your tables are named Table1, Table2, and so on for each worksheet.
The problem here is that you need to make sure that the Worksheet Index is in the same sequence as your table.
You can try this alternative which is not dependent on the sequence of the worksheet and name of the Tables.
Dim sh As Worksheet
For Each sh In ThisWorkbook.Worksheets
sh.ListObjects(1).Range.Select
' to check if you got the correct object, add below line
Debug.Print sh.ListObjects(1).Range.Address(, , , True)
Next
Above is considering you only have one(1) Table in each Sheet.

Check if unique identifier is present in "new data", if not, add entries to "historical data" sheet?

I've got data with a unique ID number in one column. This is pasted into a "Raw Data" sheet by the user.
Then, I've got macros that manipulate the "Raw Data", including archiving some of it on a "archival" worksheet which includes the ID numbers.
I want to run a check to see if there are any "new" ID numbers in the "Raw Data", and if so add a new row with some of the data including the ID number to the "archival" sheet.
I've googled and checked here. It looks like I want to use a Collection? Never encountered this so far, not sure where to start.
Sorry that this isn't the most well structured question, and that it doesn't include any code. Not sure how to get started.
It should be noted that this reconciliation only needs to go one way -- I don't need to delete "old" unique IDs from the "archival" sheet.
Thanks!
If you specifically want to use collections, over Range.Find, you can do so like this.
Although handling errors this way in VBA is best to avoid most times, for iterating over collections, it can result in faster and often less verbose code.
I have used named ranges for a Rawdata and Archive worksheet tabs, so you will need to adapt this to your own situation. What this does is use collections to store unique items already in Archive, and compare these against items in Rawdata - when a new (unique) item is found in Rawdata that is not already in Archive, it is added to the sheet (and collection).
Sub IDcollection()
Dim IDcoll As New Collection
Dim cells, Rng, Rng_a As Range
Set Rng = Worksheets("Rawdata").Range("IDRANGE")
Set Rng_a = Worksheets("Archive").Range("IDRANGE_A")
'IDRANGE looks like string IDs 'abc', 'ab1',etc
'get unique IDs already in ARCH sheet via named range
On Error Resume Next 'includes only unique items
For Each cells In Rng_a.cells
IDcoll.Add cells.Text, cells.Text
'use the IDs as the KEY in the VBA Collection
Next cells
'check for unique items not in Archive, but in Rawdata (i.e. new items)
On Error Resume Next
For Each cells In Rng
IDcoll.Item cells.Text
If Err.Number <> 0 Then
IDcoll.Add cells.Text, cells.Text
LastRow = Rng_a.Rows(Rows.Count).End(xlUp).Row + 1
Rng_a(LastRow).Value = cells.Text
Err.Clear
End If
Next
End Sub
Just stick this in a module in your VBA for your worksheet, set up some named ranges as above, populated with dummy data.

Excel Macro Autofilter issue with variable

I have a table of data with the top row being filters, I have a loop that changes which filter needs to be used inside the loop is the variable filterColumn that is being assigned a new value every time the loop runs through.
when i try to use filterColumn to determine which filter will be 'switched on' i get an error
Autofilter method of Range Class Failed
ActiveSheet.Range("$U$83:$CV$1217").AutoFilter Field:=filterColumn, Criteria1:="<>"
What is the correct syntax in order to use a variable to determine which field the filter is in?
Problem Solved I found the solution. I was referencing the filters columns position in terms of the whole worksheet when in fact I should have been referencing what number it was in the group of filters. For example the filter I wanted to change was in 'CF' which is the 84th column but my the filter I wanted to change is the 64th in the group.
Dim filterColumn As Integer
filterColumn = 2
ActiveSheet.Range("$U$83:$CV$1217").AutoFilter Field:=filterColumn, _
Criteria1:="<>"
EDIT: I tried #HeadofCatering's solution and initially it failed. However I filled in values in the referenced columns and it worked (my solution also failed under reverse conditions - make the column headers blank and it fails).
However this doesn't quite mesh with what I've (and probably you've) seen - you can definitely add filters to columns with blank headers. However one thing was consistent in the failures I saw - the filterColumn referenced a column that was outside of Application.UsedRange. You may want to try verifying that the column you are referencing is actually within Application.UsedRange (easy way: run Application.UsedRange.Select in the Immediate Window and see if your column is selected). Since you are referencing a decent amount of columns, it is possible that there are no values past a certain point (including column headers), and when you specify the column to filter, you are actually specifying something outside of your UsedRange.
An interesting (this is new to me as well) thing to test is taking a blank sheet, filling in values in cells A1 and B1, selecting columns A:G and manually adding AutoFilters - this will only add filters to columns A and B (a related situation can be found if you try to add filters to a completely blank sheet).
Sorry for the babble - chances are this isn't even your problem :)
Old solution (doesn't work when conditions described above are used)
I may be overkilling it, but try setting the sheet values as well (note I used a sample range here):
Sub SOTest()
Dim ws As Worksheet
Dim filterColumn As Integer
' Set the sheet object and declare your variable
Set ws = ActiveSheet
filterColumn = 2
' Now try the filter
ws.Range("$A$1:$E$10").AutoFilter Field:=filterColumn, Criteria1:="<>"
End Sub