I have a worksheet, I need to delete rows based on cell value ..
Cells to check are in Column A ..
If cell contains "-" .. Delete Row
I can't find a way to do this .. I open a workbook, copy all contents to another workbook, then delete entire rows and columns, but there are specific rows that has to be removed based on cell value.
Need Help Here.
UPDATE
Sample of Data I have
The easiest way to do this would be to use a filter.
You can either filter for any cells in column A that don't have a "-" and copy / paste, or (my more preferred method) filter for all cells that do have a "-" and then select all and delete - Once you remove the filter, you're left with what you need.
Hope this helps.
The screenshot was very helpful - the following code will do the job (assuming data is located in column A starting A1):
Sub RemoveRows()
Dim i As Long
i = 1
Do While i <= ThisWorkbook.ActiveSheet.Range("A1").CurrentRegion.Rows.Count
If InStr(1, ThisWorkbook.ActiveSheet.Cells(i, 1).Text, "-", vbTextCompare) > 0 Then
ThisWorkbook.ActiveSheet.Cells(i, 1).EntireRow.Delete
Else
i = i + 1
End If
Loop
End Sub
Sample file is shared: https://www.dropbox.com/s/2vhq6vw7ov7ssya/RemoweDashRows.xlsm
You could copy down a formula like the following in a new column...
=IF(ISNUMBER(FIND("-",A1)),1,0)
... then sort on that column, highlight all the rows where the value is 1 and delete them.
if you want to delete rows based on some specific cell value.
let suppose we have a file containing 10000 rows, and a fields having value of NULL.
and based on that null value want to delete all those rows and records.
here are some simple tip.
First open up Find Replace dialog, and on Replace tab, make all those cell containing NULL values with Blank.
then press F5 and select the Blank option, now right click on the active sheet, and select delete, then option for Entire row.
it will delete all those rows based on cell value of containing word NULL.
If you're file isn't too big you can always sort by the column that has the - and once they're all together just highlight and delete. Then re-sort back to what you want.
You can loop through each the cells in your range and use the InStr function to check if a cell contains a string, in your case; a hyphen.
Sub DeleteRowsWithHyphen()
Dim rng As Range
For Each rng In Range("A2:A10") 'Range of values to loop through
If InStr(1, rng.Value, "-") > 0 Then 'InStr returns an integer of the position, if above 0 - It contains the string
rng.Delete
End If
Next rng
End Sub
This is the autofilter macro you could base a function off of:
Selection.AutoFilter
ActiveSheet.Range("$A$1:$A$10").AutoFilter Field:=1, Criteria1:="=*-*", Operator:=xlAnd
Selection.AutoFilter
I use this autofilter function to delete matching rows:
Public Sub FindDelete(sCol As String, vSearch As Variant)
'Simple find and Delete
Dim lLastRow As Integer
Dim rng As Range
Dim rngDelete As Range
Range(sCol & 1).Select
[2:2].Insert
Range(sCol & 2) = "temp"
With ActiveSheet
.usedrange
lLastRow = .Cells.SpecialCells(xlCellTypeLastCell).Row
Set rng = Range(sCol & 2, Cells(lLastRow, sCol))
rng.AutoFilter Field:=1, Criteria1:=vSearch, Operator:=xlAnd
Set rngDelete = rng.SpecialCells(xlCellTypeVisible)
rng.AutoFilter
rngDelete.EntireRow.Delete
.usedrange
End With
End Sub
call it like:
call FindDelete "A", "=*-*"
It's saved me a lot of work. Good luck!
Related
I am still getting into VBA and currently I am trying to do the following:
- Delete rows in an Excel sheet based on a value stored in a dictionary.
I have this code:
For Each Key In dict.Keys
i = Key
Cells.Rows(i).Delete
Next
where before that I have populated the dictionary. The idea is that the "key" stores all the row number in which a specific value is stored, for example "1".
With the for each I want to delete all the rows stored in the dictionary, but once you delete a row in Excel the number changes. For example the first stored value in the dictionary is 5, so the for each will delete row number 5. The second value in the dictionary is 10 and now when the for each tries to delete row 10, it delete row 10, which in the original file used to be row 11. Basically the for each fails to delete the correct rows.
As I said I am new to VBA and I am not even sure if this is the correct way to do this or there is something better than dictionaries for this situation. I am looking forward to hearing your ideas and suggestions.
Thank you in advance!
Update: Solved by Ambie and sktneer. Check answers.
You may try something like this...
Dim rng As Range
Dim it
For Each it In dict.keys
If rng Is Nothing Then
Set rng = Range("A" & it)
Else
Set rng = Union(rng, Range("A" & it))
End If
Next it
If Not rng Is Nothing Then
rng.EntireRow.Delete
End If
End Sub
I'm not sure I'd bother with the Dictionary. Afterall, a Range is a collection, so perhaps just consider putting all of your target rows into a range and run the 'bulk' delete on that range. It'd be far quicker than a delete by looping rows:
Dim delRng As Range, srcRng As Range, cell As Range
'Loop through the range containing your criterion test
With Sheet1
Set srcRng = .Range(.Cells(1, "A"), .Cells(.Rows.Count, "A").End(xlUp))
End With
For Each cell In srcRng.Cells
If cell.Value2 = 1 Then 'change to your criterion
'It's a hit so add the cell to our delete range
If delRng Is Nothing Then
Set delRng = cell
Else
Set delRng = Union(delRng, cell)
End If
End If
Next
'Delete the rows
If Not delRng Is Nothing Then delRng.EntireRow.Delete
I am new to Excel Macros and VBA, and am facing the following problem:
(1) I have a data-set which has ~50,000 rows and 11 columns.
(2) I need to extract rows from the sheet, based on a certain keyword - which matches the strings present in a particular column.
(3) I have the following code from another stack overflow question:
Sub testIt()
Dim r As Long, endRow as Long, pasteRowIndex As Long
endRow = 10 ' of course it's best to retrieve the last used row number via a function
pasteRowIndex = 1
For r = 1 To endRow 'Loop through sheet1 and search for your criteria
If Cells(r, Columns("B").Column).Value = "YourCriteria" Then 'Found
'Copy the current row
Rows(r).Select
Selection.Copy
'Switch to the sheet where you want to paste it & paste
Sheets("Sheet2").Select
Rows(pasteRowIndex).Select
ActiveSheet.Paste
'Next time you find a match, it will be pasted in a new row
pasteRowIndex = pasteRowIndex + 1
'Switch back to your table & continue to search for your criteria
Sheets("Sheet1").Select
End If
Next r
End Sub
(4) This works perfectly fine when the cell of the column being searched has "YourCriteria" as the only entry.
(5) However, in my data I have strings which have the "YourCriteria" embedded in them
For Example: "YourCriteria" = "ball" and the cell(s) in a particular column contain "the dog plays with the ball" , "the ball is bad" etc.
How can I extract the rows containing 'YourCriteria" ? What modification to the code is needed ?
Thanks
To expand on Doug's answer,
If InStr(Cells(r, 2).Value, "YourCriteria")>0 Then 'Found
' ^ Column A=1, B=2, ...
Edit Change 2 to whatever column number you want to look in (C=3, D=4, ...). You can also use Columns("B").Column like you had it, if you're more comfortable with that.
I have found If InStr()>0 to be more reliable than If Instr() since InStr has lots of return-value options.
A general thought, to avoid future problems - rather than switching sheets, refer expressly to which sheet you mean. Example (not all code shown):
dim shSource as Sheet
set shSource = ActiveWorkbook.Sheets("Sheet1")
dim shDest as Sheet
set shDest = ActiveWorkbook.Sheets("Sheet2")
...
If InStr(shSource.Cells(r, 2).Value, "YourCriteria")>0 Then 'Found
shSource.Rows(r).Copy
shDest.Rows(pasteRowIndex).Select
shDest.Paste
There's a built in operator for this in VBA: Like. You can just replace the current test with this:
If Cells(r, Columns("B").Column).Value Like "*YourCriteria*" Then 'Found
InStr( [start], string, substring, [compare] )
Parameters or Arguments
start
Optional. It is the starting position for the search. If this parameter is omitted, the search will begin at position 1.
string
The string to search within.
substring
The substring that you want to find.
compare Optional. It is the type of comparison to perform. It can be one of the following values:
VBA Constant Value Explanation
vbUseCompareOption -1 Uses option compare
vbBinaryCompare 0 Binary comparison
vbTextCompare 1 Textual comparison
borrowed from http://www.techonthenet.com/excel/formulas/instr.php
The fastest way is to:
Apply a Filter to the data
Set a range variable = .SpecialCells(xlCellTypeVisible)
Use range.Copy Sheets("Sheet2").Range("A1") to copy the data straight to Sheet2
Sub DoIt()
Dim SearchRange As Range
Sheets("Sheet1").UsedRange.AutoFilter Field:=2, Criteria1:="=*Ball*", _
Operator:=xlAnd
Set SearchRange = Sheets("Sheet1").UsedRange.SpecialCells(xlCellTypeVisible)
If Not SearchRange Is Nothing Then
SearchRange.Copy Sheets("Sheet2").Range("A1")
End If
End Sub
I need to be able to hide rows in a table if the first column is blank. I need the macro to work on tables in different sheets so I search for the table name first using listobjects, I have no problem getting the table name. I have seen how to accomplish this with a general range of cells, but not within a Table. Any help is appreciated.
I have a similar macro to unhide rows in the table and it works fine because it simiply loops through all rows in the ListObject variable 'MyTable' and does not have the IF statement.
HideBlankTableRows()
Application.ScreenUpdating = False
Dim ws As Worksheet
Dim myTable As ListObject
Dim row As Range
Set ws = ActiveSheet
Set myTable = ws.ListObjects(1)
For Each row In myTable.DataBodyRange
If row.Columns(1, 1).Value = "" Then ' Error is caused by this row
row.Hidden = True
End If
Next
End Sub
Each row In myTable.DataBodyRange will actually loop through each cell in the body of the table, which you probably don't want. Since you're only checking the first column in each row, it would be faster to loop through each row in the table using Each row In myTable.DataBodyRange.Rows.
Also, the Range object doesn't have a Columns property, so you'll have to you can use the Cells property and provide the row and column number of the cell you want to reference (row 1, column 1).
The updated code would be as follows:
For Each row In myTable.DataBodyRange.Rows
If row.Cells(1, 1).Value = "" Then
row.Hidden = True
End If
Next
In addition to the fix provided by JayCal, you can utilise the ListObject properties to reference the column by name:
For Each rw In myTable.ListColumns("ColumnName").DataBodyRange
If rw.Value = vbNullString Then
rw.EntireRow.Hidden = True
End If
Next
You could also use the ListObject AutoFilter method
myTable.Range.AutoFilter Field:=lo.ListColumns("ColumnName").Index, Criteria1:="<>"
[This is in Excel 2007]
In other words, the loop will cycle through all the active cells in a one-column range (rngAddressName) and, if the cell in the range AND the cell directly to the left of it contain the string "#N/A", then it will delete that row.
Unfortunately, nothing I have tried has had any actual effect. Here is my best go at it:
i = 1
For counter = 1 To rngSC2A.Rows.Count
Contents = rngSC2A.Cells(i).Value
If Contents = "#N/A" Then
If rngAddressName.Cells(i).CellOffset(0, -1).Value = "#N/A" Then
rngAddressName.Cells(i).EntireRow.Delete
Else
End If
Else
i = i + 1
End If
Next
But this doesn't seem to find any rows with the conditions satisfied (even though such rows exist in the worksheet).
I think it might have something to do with the fact that I am looking in the Cell.Value, but I am not sure.
You can autofilter your range, delete any rows that meet your criteria, then turn the autofilter off. This is a much more efficient approach than looping.
The example below works on columns A and B in Sheet1. Modify the variables to reference the range and sheet in your workbook.
Sub DeleteDoubleNA()
Dim ws As Worksheet
Dim rng As Range
Dim lastRow As Long
Set ws = ThisWorkbook.Sheets("Sheet1")
lastRow = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
Set rng = ws.Range("A1:B" & lastRow)
' filter and delete all but header row
With rng
.AutoFilter field:=1, Criteria1:="#N/A"
.AutoFilter field:=2, Criteria1:="#N/A"
.Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
' turn off the filters
ws.AutoFilterMode = False
End Sub
This is a different take on on the excellent answer posted by #Jon Crowell.
If you use an Excel table, you can use the table's ListObject to get the data range which automatically excludes the header and footer rows.
This avoids the sometimes incorrect calculation search for a last row.
You also want to clear any pre-existing filters on the data so that you don't overlook any rows.
Dim myTable As Object
Set myTable = ActiveSheet.ListObjects(1) ' Works if worksheet contains only one table
' Clear pre-existing filters on the table
myTable.AutoFilter.ShowAllData
' Filter the table
With myTable.DataBodyRange
.AutoFilter field:=1, Criteria1:="#N/A"
.AutoFilter field:=2, Criteria1:="#N/A"
End With
' Delete visible cells in the filtered table
myTable.DataBodyRange.SpecialCells(xlCellTypeVisible).EntireRow.Delete
' Clear filters on the table
myTable.AutoFilter.ShowAllData
The (1) in ListObjects(1) is the first (in my case only) table in the worksheet. DataBodyRange refers to the data range of that table excluding header and footer rows.
I am trying to create an excel macro which is probably going to end up being quite large, to make things easier I am tackling it a bit at a time. So far I have....
Sub Macro4()
'
' Test Macro
'
'Selects the product_name column by header name
Dim rngAddress As Range
Set rngAddress = Range("A1:Z1").Find("product_name")
If rngAddress Is Nothing Then
MsgBox "The product_name column was not found."
Exit Sub
End If
Range(rngAddress, rngAddress.End(xlDown)).Select
'Inserts new column to the left of the product_name column
Selection.Insert Shift:=xlToRight
'Re-selects the product_name column
Range(rngAddress, rngAddress.End(xlDown)).Select
'Copys the contents of the product_name column
Selection.Copy
Selection.Paste
End Sub
I want it to do the following....
Search the spreadsheet for the header name 'product_name'
Insert a blank column to the left of the 'product_name' column
Copy the contents of the 'product_name' column
Paste them into the newly created blank column
Change the header name in this new column to 'product_name_2'
Currently it works fine up until the pasting into this newly created column, then i get a
'Run-time error '438'; - Object doesn't support this property or method'
Can anyone suggest where i am going wrong?
Your error is:
Range(rngAddress, rngAddress.End(xlDown)).Select
This selects from the top of the column down to just above the first blank cell. The insert shifts this portion of the column right leaving the rest where it is. When you select again you are likely to get a larger range because you have mixed two columns. The copy fails because you are then trying to copy values over the top of values.
If that does not make sense, step through your macro with F8 and see what is happening at each step.
When you understand why your current macro does not work, try this:
Sub Macro5()
Dim rngAddress As Range
Dim ColToBeCopied As Integer
Set rngAddress = Range("A1:Z1").Find("'product_name")
If rngAddress Is Nothing Then
MsgBox "The product_name column was not found."
Exit Sub
End If
ColToBeCopied = rngAddress.Column
Columns(ColToBeCopied).EntireColumn.Insert
Columns(ColToBeCopied + 1).Copy Destination:=Columns(ColToBeCopied)
End Sub
Note:
I did not select anything.
I have left the code operating on the active sheet but it is better to use With Sheets("XXX") ... End With.
Answer to second question
The macro recorder is not good at showing how to address individual cells systematically.
With Sheets("xxxx")
.Cells(RowNum,ColNum).Value = "product_name 1"
End With
The above uses With which I recommend. Notice the dot in front of Cells.
The one below operates on the active sheet.
Cells(RowNum,ColNum).Value = "product_name 1"
RowNum must be a number. ColNum can be a number (say 5) or a letter (say "E").
In your case RowNum is 1 and ColNum is ColToBeCopied and ColToBeCopied + 1.
P.S.
I forgot to mention that to find the botton row of a column use:
RowLast = Range(Rows.Count, ColNum).End(xlUp).Row
That is move up from the bottom not down from the top.
P.S. 2
To specify a range using Cells:
.Range(.Cells(Top,Left),.Cells(Bottom,Right))
The dots must match: all three or none.
I'm not sure where you are trying to copy to,
but when you want to paste you need to make a selection and then
ActiveSheet.Paste
For example:
/your code/
Selection.Copy
Range("O:O").Select
ActiveSheet.Paste
I would avoid copying / pasting altogether, if you only want to transfer values.
For example, instead of:
Range("B1:B100").Copy Destination:=Range("A1")
I would use:
Range("A1:A100").Value = Range("B1:B100").Value
If we were to substitute that into your code, and include some of the comments made by Tony:
Sub Macro4()
Dim colFound As Integer
Dim rowLast As Long
Const rowSearch As Integer = 1
'Find the product_name column
colFound = Rows(rowSearch).Find("product_name").Column
If colFound = 0 Then
MsgBox "The product_name column was not found."
Exit Sub
End If
'Find the last non-empty row
rowLast = Cells(Rows.Count, colFound).End(xlUp).Row
'Inserts new column to the left of the product_name column
Columns(colFound).EntireColumn.Insert
'Transfer the contents of the product_name column to the newly inserted one
Range(Cells(rowSearch, colFound), Cells(rowLast, colFound)).Value = _
Range(Cells(rowSearch, colFound + 1), Cells(rowLast, colFound + 1)).Value
'Rename the new column
Cells(rowSearch, colFound).Value = Cells(rowSearch, colFound).Value & "_2"
End Sub