Delete row if cell has a specific value - excel

I am trying delete a row if a cell has a specific value.
Sub DeleteRows()
'
' DeleteRows Macro
Dim Rng As Range
Range("B1").Select
Range(Selection, Selection.End(xlDown)).Select
Set Rng = ActiveSheet.UsedRange
For i = Rng.Cells.Count To 1 Step -1
If Rng.Item(i).Value <> "M of E" Then
Rng.Item(i).EntireRow.Delete
End If
Next i
End Sub
I am selecting whole range in column B and trying to say if the value in the cell is not the phrase M of E, then the row should be deleted.
It deletes everything, even if the value of the cell is M of E.
If I change the <> operator in the code to an = operator, the code works, only deleting rows that have whatever text I specify.
I cannot figure out why the not equal operator isn't working.

I will identify what is wrong in your code and then show you what it should be.
First: don't use select, see This SO Question
Range("B1").Select
Second: using End(xlDown) searches down until the last used cell before the first blank cell, if there is a blank cell before the last filled cell, you will not select the whole used range.
Range(Selection, Selection.End(xlDown)).Select
Third: setting your range variable to UsedRange, selects every used cell, negating the range in Column B; e.g. If your used range was Range("A1:F20"), then Rng = Range("A1:F20")
Set Rng = ActiveSheet.UsedRange
Forth: you start your loop from the last cell in Rng to the first cell in Rng; e.g. using the above example, your loop would start at Range("F20") and the next cell in the loop would be Range("E20"), etc.
For i = Rng.Cells.Count To 1 Step -1
Fifth: Your If statement checks for the text in each cell of Rng. Thus, it will check Range("F20") first, and since your text is not in a cell until Column B. So, the first and subsequent cells will be True for <> and the row will be deleted. The loop will still check every cell left in the row and delete the row again-and-again, and then start the next row with the same outcome. etc.
If Rng.Item(i).Value <> "M of E" Then
Rng.Item(i).EntireRow.Delete
End If
Below is basic code to loop through each cell in column B, and delete the rows that don't have "M of E",
Sub DeleteRows()
' DeleteRows Macro
'you should always set the workbook and worksheet
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
'assign a last row variable using .End(xlUp).Row
Dim lRow As Long: lRow = ws.Range("B" & ws.Rows.Count).End(xlUp).Row
'loop from the last row to the second row(skip header)
For i = lRow To 2 Step -1
'use Cells to set the row and column and check for "M of E".
If ws.Cells(i, "B").Value <> "M of E" Then
'If the cell in Column B doesn't contain "M of E", delete the row.
ws.Cells(i, "B").EntireRow.Delete
End If
Next i 'loop
End Sub

Related

If cell does not contain values from a named list

I have a piece of VBA code that sorts through a worksheet and deletes all rows that in which one of the columns does not contain specific values
Sub DeleteRows()
' Defines variables
Dim Cell As Range, cRange As Range, LastRow As Long, x As Long, TestRange As Range, MyRange As Range
' Defines LastRow as the last row of data based on column C
LastRow = Sheets("Sheet1").Cells(Rows.Count, "C").End(xlUp).Row
' Sets check range as E1 to the last row of C
Set cRange = Range("C1:C" & LastRow)
' For each cell in the check range, working from the bottom upwards
For x = cRange.Cells.Count To 1 Step -1
With cRange.Cells(x)
' If the cell does not contain one of the listed values then...
If .Value <> "Location1" And .Value <> "Location2" And .Value <> "Location3" Then
' Delete that row
.EntireRow.Delete
End If
End With
' Check next cell, working upwards
Next x
End Sub
The problem is I have a very long and growing list of locations. Instead of specifying locations (e.g., "Location1", "Location2", etc.), I want the code to compare each cell in the check range against a named list ("ReferenceLocations") and delete the row if the cell contains a location name not in that list.
How can I change that section of code (if .value<>...) to achieve this?
Using Application.Match and IsError:
If IsError(Application.Match(.Value, Range("ReferenceLocations"), 0)) Then
.EntireRow.Delete
End If
This assumes that your named range is a single row or column. If that is not a safe assumption, then:
If Application.CountIfs(Range("ReferenceLocations"), .Value) = 0
.EntireRow.Delete
End If

VBA to find a blank row, then copy two rows above it, then move to the next blank row

Suppose you have Excel data in the format
Row A
Row B
Row C
blank row
Row X
Row Y
Row Z
blank
I would like to 1) go to the row with the blank 2) copy the entire contents of the two rows above 3) paste the contents.
In the above example, the results would be
Row A
Row B
Row C
Row B
Row C
blank
Row X
Row Y
Row Z
Row Y
Row Z
blank
I am able to find the blanks. My code currently looks something like
Sub Find_Copy()
Dim rCell As Range
Dim r As Range
Dim r2 As Range
'We call the function and tell it to start the search in cell B1.
Set rCell = FindNextEmpty(Range("B8")) 'this is a separate function
'Shows a message box with the cell address. Right here is where
'you write the code that uses the empty cell.
rCell.Value = "Filled by macro 999"
MsgBox rCell.Address & " " & rCell.Value
rCell.Offset(-2, 0).EntireRow.Select 'dmt, select the row one above the blanks
Selection.Copy
Selection.Insert Shift:=xlDown
Set rCell = Nothing
End Sub
Can anyone help me get this sorted out? Thank you!
The sub which does the work is enhanceList. It takes as parameter the range you want to work on.
The basic idea of my macro is to work from the bottom up while inserting the rows.
Option Explicit
Public Sub test_enhanceList()
Dim rg As Range
Set rg = table1.Range("A1:A8") '<<< adjust to your needs
enhanceList rg
End Sub
' The sub which does the work
Private Sub enhanceList(rgToEnhance As Range)
Dim c As Range
With rgToEnhance
'we will start at the end of the range
Set c = .Cells(.Rows.Count)
End With
Dim i As Long
Do
If LenB(c.Value2) = 0 Then 'test for empty cell
For i = 1 To 2
'insert empty row and take value from 3rd row above
c.EntireRow.Insert xlShiftDown
'c.offset(-1) = new cell
'c.offset(-3) = value to copy
c.Offset(-3).EntireRow.Copy c.Offset(-1)
Next
End If
Set c = c.Offset(-1) 'set c to the cell above
Loop Until c.Row = rgToEnhance.Cells(1, 1).Row 'stop when first cell is reached
End Sub
Add this after your insert and you can get both rows B and C right. You'll have to add a loop with a range limit starting before your function call to get the next empty cell to add Y and Z and anything else that might come after. Post your function code and I can probably write a loop that will do it later.
rCell.Offset(-1, 0).EntireRow.Select 'dmt, select the row one above the blanks
Selection.Copy
rCell.Offset(-2, 0).EntireRow.Select 'dmt, select the row one above the blanks
Selection.Insert Shift:=xlDown
To choose the column you'd like to do this on by clicking on it change this line:
Set rCell = FindNextEmpty(ActiveCell.Offset(0, 0))
To this:
Set rCell = FindNextEmpty(Selection)
Then before running the macro, choose cell B1
Rather than changing my answer, I added a new one. Added a couple lines to find the range of the data, and then looped through each cell in the range, testing for empty. It eliminates the need for the extra function.
Try this:
Sub Dan_Find_Copy()
Dim wkb As Workbook
Dim rCell As Range
Dim r As Range
Dim r2 As Range
Dim colNumber As Integer 'to store the column index
Dim rowNumber As Long 'to store the last row containing data
Dim i As Long 'iterator
'Need to get the range of the data
Set wkb = ActiveWorkbook
'store the column number of the selection
colNumber = Columns(Selection.Column).Column
'find the last row containing data
rowNumber = Cells(Rows.Count, colNumber).End(xlUp).Row
Set r = wkb.ActiveSheet.Range(Sheet1.Cells(1, colNumber), Sheet1.Cells(rowNumber, colNumber))
For Each rCell In r.Cells
If rCell.Value = "" Then
If MsgBox("Continue?", vbOKCancel, "Hello!") = vbOK Then
'Shows a message box with the cell address. Right here is where
'you write the code that uses the empty cell.
rCell.Value = "Filled by macro 999"
MsgBox rCell.Address & " " & rCell.Value
rCell.Offset(-2, 0).EntireRow.Select 'dmt, select the row one above the blanks
Selection.Copy
Selection.Insert Shift:=xlDown
rCell.Offset(-1, 0).EntireRow.Select 'dmt, select the row one above the blanks
Selection.Copy
rCell.Offset(-2, 0).EntireRow.Select 'dmt, select the row one above the blanks
Selection.Insert Shift:=xlDown
rCell.Select
Else
MsgBox ("You cancelled the process.")
Exit For
End If
End If
Next rCell
Set rCell = Nothing
Set r = Nothing
Set wkb = Nothing
End Sub

Replacing the values in a column depending on a match condition using VBA

Test_FileI am trying to replace the values in a column with a specific condition:
IF(C37808=$Q$1,D37808,0); So I have copied the filtered column D(D31924:D37908) and pasted it another column F. My goal is to replace the corresponding values of Column F based on the IF-ELSE condition mentioned above.
The first challenge was locating the range of Column C & D because they're are dynamic meaning the first value of Column C or D could start from row number 1 or row number 100 based on a filtered criteria. I have figured out a way to locate that dynamic range so no issue there. Now when I try to replace the values in the corresponding cells of Column F, I see that it pastes only one value. Sharing the code segment below:
Sub Replacement()
Dim rng1 As Range, rng2 As Range, rng3 As Range
Dim v
With ActiveSheet.AutoFilter.Range
.SpecialCells(xlCellTypeVisible).Areas(2)(1, 3).Select
End With
Set rng1 = Range(Selection, Selection.End(xlDown)) 'Finding the range of column C from the filtered region
rng1.Copy
v = WorksheetFunction.Mode(rng1) 'finding the mode/highest occuring number in rng1
Range("Q1").Value = v 'storing if for comparison purpose later
'Navigating to column F
With ActiveSheet.AutoFilter.Range
.SpecialCells(xlCellTypeVisible).Areas(2)(1, 3).Select
End With
ActiveCell.Offset(0, 3).PasteSpecial 'pasting the copied column C into column F
With ActiveSheet.AutoFilter.Range
.SpecialCells(xlCellTypeVisible).Areas(2)(1, 6).Select
End With
Set rng2 = Range(Selection, Selection.End(xlDown)) 'storing the column F's range for replacing
With ActiveSheet.AutoFilter.Range
.SpecialCells(xlCellTypeVisible).Areas(2)(1, 4).Select
End With
Set rng3 = Range(Selection, Selection.End(xlDown)) 'storing the column D's range for using in Column F
'Looping through each element of column F now and matching against my condition
For Each z1 In rng2
If z1.Value = v Then
z1.Value = rng3.Value 'expectation is to implement the formula :IF(C37808=$Q$1,D37808,0)
Else: z1.Value = 0
End If
Next z1
End Sub
After executing the code, I get the only 1st value of column D in all the cells of column F. I would really appreciate if someone can help me fix this issue. Attached is the screenshot of the result.
result
I commented and re-worked your code. Now it looks like this.
Sub Replacement()
Dim RngC As Range ' Range is in column C
Dim RngF As Range ' use descriptive names
Dim RngD As Range
Dim Cell As Range
Dim v As Variant
' don't Select anything
' With ActiveSheet.AutoFilter.Range
' .SpecialCells(xlCellTypeVisible).Areas(2)(1, 3).Select
' End With
' the next line will produce an error if no filter is set
On Error Resume Next
Set RngC = ActiveSheet.AutoFilter.Range.SpecialCells(xlCellTypeVisible).Areas(2)(1, 3)
If Err Then
MsgBox "Please specify filter criteria", vbCritical, "Can't Proceed"
Exit Sub
End If
On Error GoTo 0
Set RngC = Range(RngC, RngC.End(xlDown)) 'Finding the range of column C from the filtered region
v = WorksheetFunction.Mode(RngC) 'finding the mode/highest occuring number in RngC
' no need to write v to the sheet
' Range("Q1").Value = v 'storing if for comparison purpose later
'Navigating to column F ' don't "navigate". Just address
' With ActiveSheet.AutoFilter.Range
' this is the first cell of RngC
' .SpecialCells(xlCellTypeVisible).Areas(2)(1, 3).Select
' End With
' ActiveCell.Offset(0, 3).PasteSpecial 'pasting the copied column C into column F
RngC.Copy Destination:=RngC.Cells(1).Offset(0, 3)
' don't Select anything!
' With ActiveSheet.AutoFilter.Range
' .SpecialCells(xlCellTypeVisible).Areas(2)(1, 6).Select
' End With
Set RngF = ActiveSheet.AutoFilter.Range.SpecialCells(xlCellTypeVisible).Areas(2)(1, 6)
' as an alternative you might specify:-
' Set RngF = RngC.Offset(0, 3)
Set RngF = Range(RngF, RngF.End(xlDown)) 'storing the column F's range for replacing
' With ActiveSheet.AutoFilter.Range
' .SpecialCells(xlCellTypeVisible).Areas(2)(1, 4).Select
' End With
' Set RngD = Range(Selection, Selection.End(xlDown)) 'storing the column D's range for using in Column F
Set RngD = RngC.Offset(0, 1)
' Looping through each element of column F now and matching against my condition
For Each Cell In RngF
With Cell
If .Value = v Then
' your line always inserts RngD.Value.
' Since RngD is an array but the cell can only take one value
' VBA inserts the first value of the array.
' z1.Value = RngD.Value 'expectation is to implement the formula :IF(C37808=$Q$1,D37808,0)
.Value = RngD.Cells(.Row).Value
Else
.Value = 0
End If
End With
Next Cell
End Sub
It pastes different values now but seems to select them from the wrong rows. The reason is the method by which you set ranges. I don't believe it works as you expect and I can't test it because I don't have data. Two facts:-
A range defined as starting from any cell (such as ActiveSheet.AutoFilter.Range.SpecialCells(xlCellTypeVisible).Areas(2)(1, 3)) and ending at the last used cell, will include both visible and invisible cells.
The index number of the cells in your ranges aren't aligned with the row numbers of the worksheet.
The task actually is very simple. Just loop through all cells in column F and if an examination meets certain criteria copy the value from column D in the same row. The row number is never in question if handled in this way. So, if my solution doesn't copy from the correct row I suggest you redesign the loop.

Understanding & Additional code for excel row copy based on value

Please see below code I have found on the internet, which is currently working to a certain degree for me.
Could someone possibly commentate on what each line of this code means so I can understand what its doing?
Im trying to understand it with little programming knowledge and add additional code to look for additional values to paste into additional sheets.
I'm also trying to work out how to make them paste to certain rows one after the other and not maintain the row they were originally in on sheet 1.
Code:
Sub Test()
Dim rw As Long, Cell As Range
For Each Cell In Sheets(1).Range("H:H")
rw = Cell.Row
If Cell.Value = "Dept 1" Then
Cell.EntireRow.Copy
Sheets("Sheet2").Range("A" & rw).PasteSpecial xlPasteValues
End If
Next
End Sub
--
Many thanks
I've added comments as requested. To paste them onto the same row, look at removing the rw variable and replacing it with something that increments by one each time
Sub Test()
'declare variables
Dim rw As Long, Cell As Range
'loop through each cell the whole of column H in the first worksheet in the active workbook
For Each Cell In Sheets(1).Range("H:H")
'set rw variable equal to the row number of the Cell variable, which changes with each iteration of the For loop above
rw = Cell.Row
'check if the value of Cell variable equals Dept 1
If Cell.Value = "Dept 1" Then
'copy the entire row if above is true
Cell.EntireRow.Copy
'paste to the same row of Sheet 2
Sheets("Sheet2").Range("A" & rw).PasteSpecial xlPasteValues
End If
Next
End Sub
Here is your Code Commented hope you understand:
Sub Test()
' Variables Defined as follows:
Dim rw As Long, Cell As Range
' Loop Searching each Cell of (Range H1 to end of last H on sheet1
For Each Cell In Sheets(1).Range("H:H")
' now determine current row number:
rw = Cell.Row
' Test cell value if it contain >> Dept 1 as value:
If Cell.Value = "Dept 1" Then
' Select that row and copy it:
Cell.EntireRow.Copy
' now paste the values of that row into A column and rw row on sheet2:
Sheets("Sheet2").Range("A" & rw).PasteSpecial xlPasteValues
' You should add following to:
' Disable marching ants around copied range:
Application.CutCopyMode = False
End If
Next
End Sub

How to clear a row with an empty cell in one of its columns, without deleting entire row?

A seemingly simple issue:
I have data in Columns A:E. Column E has some blank cells in some of the rows.
I would like to remove ALL THE ROW that include a blank cell in E. However, Here's the catch, there is other data in subsequent columns. if I delete the entire row, this data will be also deleted, which I don't want.
To be more specific, I need to:
(1) Check column E for blank cells
(2) When a blank cell if found, clear the row that has this cell, but only Columns A:E
(3) Shift the data in Columns A:E up
I tried:
Range("E2:E100").SpecialCells(xlCellTypeBlanks).Delete Shift:=xlUp
But this one only shifts data in column E, not the entire row.
of course, i can use:
Range("E2:E100").SpecialCells(xlCellTypeBlanks).EntireRow.Delete
But like I said, this will delete data in subsequent columns, which I don't want.
Any tips?
Thanks,
Al
You can't shift the entire and leave some of the row behind, that is a contradiction. It sounds like you want to do this:
Range("A" & row & ":E" & row).Delete Shift:=xlUp
Where row is the row number you want to delete
To remove A:E in "chunks" but preserve you other columns intact a full solution is this
Sub PartKill2()
Dim rng1 As Range
Dim rng2 As Range
ActiveSheet.UsedRange
On Error Resume Next
Set rng1 = Columns("E").SpecialCells(xlBlanks)
On Error GoTo 0
If rng1 Is Nothing Then Exit Sub
For Each rng2 In rng1.Areas
rng2.Cells(1).Offset(0, -4).Resize(rng2.Rows.Count, 5).Delete xlUp
Next
End Sub
If you wanted to delete the entire row where E was blank, and columns F:End were blank (but otherwise leave the row as is) then this more complex version can be used
Sub PartKill1()
Dim rng1 As Range
Dim lngCell As Long
Dim lngArea As Long
ActiveSheet.UsedRange
On Error Resume Next
Set rng1 = Columns("E").SpecialCells(xlBlanks)
On Error GoTo 0
If rng1 Is Nothing Then Exit Sub
For lngArea = rng1.Areas.Count To 1 Step -1
For lngCell = rng1.Areas(lngArea).Cells.Count To 1 Step -1
If Application.CountA(Range(rng1.Areas(lngArea).Cells(lngCell).Offset(0, 1), Cells(rng1.Areas(lngArea).Cells(lngCell).Row, Columns.Count))) = 0 Then
rng1.Areas(lngArea).Cells(lngCell).EntireRow.Delete
End If
Next
Next
End Sub

Resources