I am looking for assistance in deleting two columns within my range of data that have blank headers. These blank headers will appear in the first row of my used range. What would be the best way to go about this? Should I use .Find to search for blank cells in the first row and then get the column address of the two blank cells in order to delete them?
Currently, I am just deleting the columns that I know they'll appear in, but this has the potential to change. Current code:
rngUsed.Columns("F").Delete
rngUsed.Columns("H").Delete
Because the data can change, what would be the better way of handling this?
Thanks!
You can use SpecialCells to find the blanks in the first row and remove the corresponding columns:
Dim rng As Range
Set rng = Range("B3").CurrentRegion 'for example...
On Error Resume Next 'ignore error if no blanks
rng.Rows(1).SpecialCells(xlCellTypeBlanks).EntireColumn.Delete
On Error GoTo 0 'stop ignoring errors
Delete Columns With Blank Headers
The current setup is in Test Mode i.e. it will select the columns to be deleted. If the result is satisfactory, switch to Const TestMode As Boolean = False when the columns will be deleted.
Adjust the values in the constant sections.
The Code
Option Explicit
Sub TESTdeleteBlankHeadered()
Const wsName As String = "Sheet1"
Const ColumnsCount As Long = 2 ' -1 - all columns containing blank headers.
Dim wb As Workbook: Set wb = ThisWorkbook
Dim rg As Range: Set rg = wb.Worksheets(wsName).UsedRange
deleteBlankHeadered rg, ColumnsCount ' first found columns
'deleteBlankHeadered rg, ColumnsCount, True ' last found columns
'deleteBlankHeadered rg ' all found columns
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: In a worksheet, deletes a specified number of its columns,
' defined by blank cells in the first (header) row of
' a given range.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub deleteBlankHeadered( _
rg As Range, _
Optional ByVal ColumnsCount As Long = -1, _
Optional ByVal LastOccurringColumns As Boolean = False)
' When 'True', tests with select.
' When 'False', deletes.
Const TestMode As Boolean = True
' Validate inputs.
If rg Is Nothing Then Exit Sub
If ColumnsCount < -1 Or ColumnsCount = 0 Then Exit Sub
' Define Source Row Range.
Dim srg As Range: Set srg = rg.Areas(1).Rows(1)
' Write values from Source Row Range to Data Array.
Dim cCount As Long: cCount = srg.Columns.Count
Dim Data As Variant
If cCount > 1 Then
Data = srg.Value
Else
ReDim Data(1 To 1, 1 To 1): Data(1, 1) = srg.Value
End If
' Define 'elements' of the 'For...Next' Loop.
Dim cFirst As Long, cLast As Long, cStep As Long
If LastOccurringColumns Then
cFirst = cCount: cLast = 1: cStep = -1
Else
cFirst = 1: cLast = cCount: cStep = 1
End If
' Declare additional variables.
Dim drg As Range ' Delete Range
Dim oCount As Long ' Occurrences Count
Dim j As Long ' Data Array (Source Row Range) Columns Counter
' Loop through columns of Data Array and use found blank values
' to combine blank cells with Delete Range.
For j = cFirst To cLast Step cStep
If Not IsError(Data(1, j)) Then
If Len(Data(1, j)) = 0 Then
oCount = oCount + 1
Select Case oCount
Case 1
Set drg = srg.Cells(j)
If ColumnsCount = 1 Then
Exit For
End If
Case ColumnsCount
Set drg = Union(drg, srg.Cells(j))
Exit For
Case Else
Set drg = Union(drg, srg.Cells(j))
End Select
End If
End If
Next
' Declare additional variables.
Dim ActionTaken As Boolean
' Delete Column Ranges (containing blank headers).
If Not drg Is Nothing Then
Application.ScreenUpdating = False
If TestMode Then
drg.Worksheet.Activate
drg.EntireColumn.Select
Else
drg.EntireColumn.Delete
End If
Application.ScreenUpdating = True
ActionTaken = True
End If
' Inform user.
If ActionTaken Then
MsgBox "Columns deleted: " & oCount, vbInformation, "Success"
Else
MsgBox "No columns deleted.", vbExclamation, "No Action Taken"
End If
End Sub
Related
Is there in VBA faster way to change row (or cell) color based on values without referring to cell
Referring to cell each time inside loop is very slow, that's why i am looking for faster method doing it in VBA.
Table:
Amount1
Amount2
100
50
20
200
...
...
If Amount1 is greater than Amount2, entire row(or cell) is red, vice versa entire row(or cell) is green.
Thank You!
It would have been helpful if you had clarified why you can't use CF as suggested, but if you really can't when looping it's best to refer to directly to cells as little as possible, especially changing values or formats. Try something like this:
Sub SampleValues()
Dim bGreater As Boolean
Dim rng As Range, rRow As Range
Set rng = ActiveSheet.Range("A1:B1000")
rng.Formula = "=RANDBETWEEN(1,1000)"
rng.Value = rng.Value
End Sub
Sub RedOrGreen()
Dim clr As Long, i as long
Dim rng As Range, rRow As Range
Dim arr As Variant
Const clrMore = vbGreen, clrLessEqual = vbRed
Dim t As Single
t = Timer
Set rng = Range("A1:B1000")
arr = rng.Value
For Each rRow In rng.Rows
i = i + 1
If arr(i, 2) > arr(i, 1) Then
clr = clrMore
Else
clr = clrLessEqual
End If
If rRow.Interior.Color <> clr Then
rRow.Interior.Color = clr
End If
Next
Debug.Print Timer - t
End Sub
Highlight Rows
Sub HighlightRows()
Dim t As Double: t = Timer
' Define constants (adjust).
Const PROC_TITLE As String = "Highlight Rows"
Const SMALL_COL As Long = 1
Const GREAT_COL As Long = 2
Dim RowColors(): RowColors = VBA.Array(vbGreen, vbRed)
' Reference the table range.
' Turn off screen updating.
Application.ScreenUpdating = False
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
If ws.AutoFilterMode Then ws.AutoFilterMode = False
Dim trg As Range: Set trg = ws.Range("A1").CurrentRegion
' Validate rows and columns.
' Validate rows.
Dim rCount As Long: rCount = trg.Rows.Count
If rCount < 2 Then
MsgBox "No data or just headers in the range '" _
& trg.Address(0, 0) & "'.", vbCritical, PROC_TITLE
Exit Sub ' screen updating will automatically get turned on
End If
' Validate columns.
Dim cCount As Long: cCount = trg.Columns.Count
Dim MaxCol As Long: MaxCol = Application.Max(SMALL_COL, GREAT_COL)
If cCount < GREAT_COL Then
MsgBox "Column " & MaxCol & " is greater than the number " _
& "of columns (" & cCount & ") in the range ('" _
& trg.Address(0, 0) & "').", vbCritical, PROC_TITLE
Exit Sub ' screen updating will automatically get turned on
End If
' Reference and populate the helper columns.
' Insert two helper columns adjacent to the right of the table range.
trg.Offset(, cCount).Resize(, 2).Insert xlShiftToRight
' Remove this line if there is no data to the right.
' Reference the expanded table range (including the helper columns)...
Dim erg As Range: Set erg = trg.Resize(, cCount + 2) ' has headers
' ... and reference its data to be used with 'SpecialCells'.
Dim edrg As Range: Set edrg = erg.Resize(rCount - 1).Offset(1) ' no headers
' Reference the helper columns.
Dim CompareCol As Long: CompareCol = cCount + 1 ' for the auto filter
Dim crg As Range: Set crg = erg.Columns(CompareCol)
Dim irg As Range: Set irg = erg.Columns(cCount + 2)
' Write an ascending integer sequence to the Integer column.
irg.Value = ws.Evaluate("ROW(1:" & rCount & ")")
' Write the values from the criteria columns to arrays.
Dim SmallData(): SmallData = erg.Columns(SMALL_COL).Value
Dim GreatData(): GreatData = erg.Columns(GREAT_COL).Value
' Define the Compare array.
Dim CompareData(): ReDim CompareData(1 To rCount, 1 To 1)
Dim SmallVal, GreatVal, r As Long
' Write the Compare results to the Compare array
' (1 for the 1st color and 2 for the 2nd), ...
For r = 2 To rCount ' skip headers
SmallVal = SmallData(r, 1)
GreatVal = GreatData(r, 1)
If IsNumeric(SmallVal) And IsNumeric(GreatVal) Then
Select Case SmallVal
Case Is < GreatVal: CompareData(r, 1) = 1
Case Is > GreatVal: CompareData(r, 1) = 2
End Select
End If
Next r
Erase SmallData
Erase GreatData
' ... write the results from the array to the Compare column...
crg.Value = CompareData
Erase CompareData
' ... and sort the range by it.
erg.Sort crg, xlAscending, , , , , , xlYes
' Highlight the rows.
edrg.Interior.Color = xlNone ' clear previous colors
Dim vedrg As Range
For r = 1 To 2
erg.AutoFilter CompareCol, CStr(r)
On Error Resume Next ' prevent error when no filtered rows
Set vedrg = edrg.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
ws.AutoFilterMode = False ' turn off the auto filter!!!
If Not vedrg Is Nothing Then
Debug.Print vedrg.Address ' only two areas are being highlighted
vedrg.Interior.Color = RowColors(r - 1) ' 'RowColors' is zero-based
Set vedrg = Nothing ' reset for the next iteration
End If
Next r
' Clean up.
' Sort the range by the Integer column restoring initial order.
erg.Sort irg, xlAscending, , , , , , xlYes
' Delete the helper columns.
crg.Resize(, 2).Delete xlShiftToLeft
' If you have removed the Insert-line, replace this line with:
'crg.Resize(, 2).Clear
' Turn on screen updating to immediately see the changes
' (if the worksheet is active) before OK-ing the message box.
Application.ScreenUpdating = True
Debug.Print Format(Timer - t, "00.000000")
' Inform.
MsgBox "Rows highlighted.", vbInformation, PROC_TITLE
End Sub
I need a VBA code, that will allow me to select and copy custom number of visible rows only.
For example: I filtered a column data, and the count of all the visible cells is 1000. However, I want to copy only the first 800 visible cells only out of the 1000 visible cells.
One idea is to get all visible cells using SpecialCells(xlCellTypeVisible) and then loop through and collect them one by one using Application.Union to limit them to your desired amount.
Option Explicit
Public Sub Example()
Dim Top800Cells As Range
Set Top800Cells = GetTopVisibleRows(OfRange:=Range("A:A"), TopAmount:=800)
Top800Cells.Select
End Sub
Public Function GetTopVisibleRows(ByVal OfRange As Range, ByVal TopAmount As Long) As Range
Dim VisibleCells As Range
Set VisibleCells = OfRange.SpecialCells(xlCellTypeVisible)
If VisibleCells Is Nothing Then
Exit Function
End If
Dim TopCells As Range
Dim Count As Long
Dim Row As Range
For Each Row In VisibleCells.Rows
If TopCells Is Nothing Then
Set TopCells = Row
Else
Set TopCells = Application.Union(TopCells, Row)
End If
Count = Count + 1
If Count = TopAmount Then Exit For
Next Row
Set GetTopVisibleRows = TopCells
End Function
If you want to use it as a UDF (user defined function) in a formula SpecialCells(xlCellTypeVisible) is known to fail there (see SpecialCells(xlCellTypeVisible) not working in UDF). And you need to check visibility yourselft:
Public Function GetTopVisibleRows(ByVal OfRange As Range, ByVal TopAmount As Long) As Range
Dim TopCells As Range
Dim Count As Long
Dim Row As Range
For Each Row In OfRange.Rows
If Not Row.EntireRow.Hidden Then
If TopCells Is Nothing Then
Set TopCells = Row
Else
Set TopCells = Application.Union(TopCells, Row)
End If
Count = Count + 1
If Count = TopAmount Then Exit For
End If
Next Row
Set GetTopVisibleRows = TopCells
End Function
Copy First n Rows of SpecialCells(xlCellTypeVisible)
This is usually done to more columns as illustrated in the code.
To apply it just to column A, replace Set rg = ws.Range("A1").CurrentRegion with
Set rg = ws.Range("A1").CurrentRegion.Columns(1)
assuming that the header is in the first worksheet row.
In a nutshell, it loops through the rows (rrg) of each area (arg) of the range (MultiRange, dvrg) counting each row (r) and when it hits the 'mark' (DataRowsCount), it uses this row (Set SetMultiRangeRow = rrg, lrrg) and the first row (frrg) as arguments in the range property to set the required range and reapply the same type of SpecialCells to finally reference the required amount of rows.
Sub ReferenceFirstMultiRangeRows()
' Define constants
Const CriteriaColumn As Long = 1
Const CriteriaString As String = "Yes"
Const DataRowsCount As Long = 800
' Reference the worksheet ('ws').
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
If ws.AutoFilterMode Then ws.AutoFilterMode = False
' Reference the ranges.
Dim rg As Range ' the range (has headers)
Set rg = ws.Range("A1").CurrentRegion ' you may need to use another way!
Dim drg As Range ' the data range (no headers)
Set drg = rg.Resize(rg.Rows.Count - 1).Offset(1)
' Apply the auto filter to the range.
rg.AutoFilter CriteriaColumn, CriteriaString
' Attempt to reference the visible data range ('vdrg').
Dim vdrg As Range
On Error Resume Next
Set vdrg = drg.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
' Reference the required amount of visible rows ('vdrg').
' Reference the partial range ('vdrg') from the first row
' to the DataRowsCount-th row of the visible range
' and reapply special cells to this range.
If Not vdrg Is Nothing Then ' filtered rows found
Dim lrrg As Range: Set lrrg = SetMultiRangeRow(vdrg, DataRowsCount)
If Not lrrg Is Nothing Then ' there are more rows than 'DataRowsCount'
Dim frrg As Range: Set frrg = vdrg.Rows(1)
Set vdrg = ws.Range(frrg, lrrg).SpecialCells(xlCellTypeVisible)
'Else ' the visible data range is already set; do nothing
End If
'Else ' no filtered rows found; do nothing
End If
ws.AutoFilterMode = False ' remove the auto filter
If vdrg Is Nothing Then
MsgBox "No filtered rows.", vbExclamation
Exit Sub
End If
' Continue using vdrg e.g.:
Debug.Print vdrg.Address ' only the first <=257 characters of the address
'vdrg.Select
'vdrg.Copy Sheet2.Range("A2")
End Sub
Function SetMultiRangeRow( _
ByVal MultiRange As Range, _
ByVal MaxRowNumber As Long) _
As Range
Dim rCount As Long
rCount = MultiRange.Cells.CountLarge / MultiRange.Columns.Count
If rCount < MaxRowNumber Then Exit Function
Dim arg As Range
Dim rrg As Range
Dim r As Long
Dim lrrg As Range
For Each arg In MultiRange.Areas
For Each rrg In arg.Rows
r = r + 1
If r = MaxRowNumber Then
Set SetMultiRangeRow = rrg
Exit For
End If
Next rrg
Next arg
End Function
Hello I am wondering if anyone has any suggestions for a replacement for a Do Until loop in VBA??
My Code (see below), basically looks at cell F4, if Cell F4 is 0 then the row is selected and deleted. the cells then shift up, it loops again until the F4 is either greater than zero or it is empty.
The code actually works perfectly well but it takes an age to finish (around 3 mins at a guess). I do make sure that screen updating is turned off etc, I just haven't included that in this example.
I am not to fussed that it takes so long in the first instance but eventually it will doing this search multiple times in one hit, potentially up to 10K cells at a time so I want it to be a bit more snappy...
So my question is is there anything I can do other than Do until loops?
Do Until Raw1.Range("F4") = "" Or Raw1.Range("F4") > 0
If Raw1.Range("F4").Value = 0 Then
Raw1.Range("A4:H4").Select
Selection.Delete Shift:=xlUp
End If
Loop
Delete Data Using AutoFilter
Starting from row 4 (the header row is 3), this will delete all consecutive A:H row ranges, whose cell values in column F are equal to 0 (preserving blank cells).
Option Explicit
Sub DeleteZeros()
' 'Raw1' is the code name of a worksheet in the workbook containing this code.
Const FirstCellAddress As String = "F3"
Const ColumnsAddress As String = "A:H"
If Raw1.FilterMode Then Raw1.ShowAllData
Dim crg As Range ' Column Range (Has Headers - 'F')
With Raw1.Range(FirstCellAddress)
Dim lRow As Long
lRow = Raw1.Cells(Raw1.Rows.Count, .Column).End(xlUp).Row
Dim rCount As Long: rCount = lRow - .Row + 1
If rCount < 2 Then Exit Sub ' to few rows
Set crg = .Resize(rCount)
End With
Dim drg As Range ' Data Range (No Headers - 'A:H')
With crg
Set drg = .Resize(rCount - 1).Offset(1) _
.EntireRow.Columns(ColumnsAddress)
End With
Dim FirstDataRow As Long: FirstDataRow = drg.Row
' Filter Column Range
crg.AutoFilter 1, "0"
Dim vdrg As Range ' Visible Data Range (No Headers - 'A:H')
On Error Resume Next
Set vdrg = drg.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
Raw1.AutoFilterMode = False
' Delete
If vdrg Is Nothing Then Exit Sub
If vdrg.Cells(1).Row <> FirstDataRow Then Exit Sub
vdrg.Areas(1).Delete xlShiftUp
End Sub
It is always a better solution to delete from bottom up then from top down.
Sub deleteRows()
Const checkColumn As Long = 6 'Column F
Dim rg As Range
'!!!!!you will have to adjust this to your needs!!!!
Set rg = ActiveSheet.Cells(checkColumn, 4).CurrentRegion
Dim cntRows As Long
cntRows = rg.Rows.Count
Dim i As Long
For i = cntRows To 1 Step -1
If rg.Cells(i, checkColumn) = 0 Then
'rg.Rows(i).EntireRow.Delete xlShiftUp 'removes entire row
rg.Rows(i).Delete xlShiftUp 'removes only columns A-H
End If
Next
End Sub
It is faster to delete all the cells in 1 operation. In my example code, I have a runner find the last valid cell. I use that cell to determine the size of range that needs to be deleted.
Sub RemoveEmptyRowsBasedOnColumnValues()
Dim CalculationMode As XlCalculation
CalculationMode = Application.Calculation
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Dim Cell As Range
With Raw1
For Each Cell In .Range("F4", .Cells(.Rows.count, "F").End(xlUp))
If Cell.Value > 0 Then
If Cell.Row > 3 Then
.Range("A4:H4").Resize(Cell.Row - 4).Delete Shift:=xlUp
End If
Exit For
End If
Next
End With
Application.Calculation = CalculationMode
End Sub
Function Raw1() As Worksheet
Set Raw1 = ThisWorkbook.Worksheets("Raw1")
End Function
I am trying to find people who are missing their street address and moving their row to a separate tab in my sheet.
Sub NEW_NoAddress()
Const Title As String = "Move Data Rows"
Const scCol As Long = 6
Const dCol As Long = 1
Const Criteria As String = "ISEmpty()"
' Remove any previous filters.
If Sheet1.AutoFilterMode Then
Sheet1.AutoFilterMode = False
End If
' Filter.
Dim srg As Range ' Source Range (Headers and Data)
Set srg = Sheet1.Range("A1").CurrentRegion
srg.AutoFilter scCol, Criteria
' Count the number of matches.
Dim sdrg As Range ' Source Data Range (Without Headers)
Set sdrg = srg.Resize(srg.Rows.Count - 1).Offset(1)
Dim sdccrg As Range ' Source Data Criteria Column Range
Set sdccrg = sdrg.Columns(scCol)
Dim drCount As Long ' Destination Rows Count (Matches Count)
drCount = Application.Subtotal(103, sdccrg)
' Move if there are matches.
If drCount > 0 Then ' matches found
Dim sdfrrg As Range ' Source Data Filtered Rows Range
Set sdfrrg = sdrg.SpecialCells(xlCellTypeVisible)
Dim dCell As Range ' Destination Cell
Set dCell = Sheet10.Cells(Sheet10.Rows.Count, dCol).End(xlUp).Offset(1, 0)
With sdfrrg
.Copy dCell
' Either delete the entire worksheet rows...
'.EntireColumn.Delete
' ... or remove filter to prevent...
Sheet1.AutoFilterMode = False
' ... deleting the entire worksheet rows leaving possible data
' to the right (after the empty column) intact.
.Delete xlShiftUp
End With
Else ' no matches
Sheet1.AutoFilterMode = False
End If
End Sub
I tried "<>", "<> **", " "" ", I think I tried one that had vbStringISNull, (), and other things I came across in Google. I considered going the other direction and keeping the <> to move those who have an address, but I'd rather move the incorrect entries to my exceptions tab.
Move Matching Rows
I'm glad you like my code. Unfortunately, it has a big mistake:
drCount = Application.Subtotal(103, sdccrg)
which is similar to Excel's ACOUNT which results in 0 when selecting blanks.
I've seen this in a couple of codes and adopted it as valid. Was I in for a surprise.
When you plan on using such a code so intensely, you want to move the changing variables to the arguments section to easily use it many times (see the long procedure below).
You can use the new procedure...
... for your first question like this:
Sub MoveMatchRows()
MoveMatchingRows Sheet1, 4, "FD.Matching Gifts FY22", Sheet2, 1, False
End Sub
... for yesterday's question like this:
Sub NEW_Move_Stock_InKind_DAF()
MoveMatchingRows Sheet1, 44, "<>*/*", Sheet8, 1, False
End Sub
... and for today's question like this:
Sub NewNoAddress()
MoveMatchingRows Sheet1, 6, "=", Sheet10, 1, False
End Sub
I have declared SourceCriteria as variant and added xlFilterValues to be able to use multiple criteria, e.g. Array("1", "2").
The Procedure
Sub MoveMatchingRows( _
ByVal SourceWorksheet As Worksheet, _
ByVal SourceColumn As Long, _
ByVal SourceCriteria As Variant, _
ByVal DestinationWorksheet As Worksheet, _
Optional ByVal DestinationColumn As Long = 1, _
Optional ByVal DoClearPreviousDestinationData As Boolean = False)
Const ProcTitle As String = "Move Matching Rows"
' Remove any previous filters.
If SourceWorksheet.AutoFilterMode Then
SourceWorksheet.AutoFilterMode = False
End If
' Filter.
Dim srg As Range ' Source Range (Headers and Data)
Set srg = SourceWorksheet.Range("A1").CurrentRegion
srg.AutoFilter SourceColumn, SourceCriteria, xlFilterValues
' Create a reference to the Source Data Range (no headers).
Dim sdrg As Range
Set sdrg = srg.Resize(srg.Rows.Count - 1).Offset(1)
' Clear Destination worksheet.
If DoClearPreviousDestinationData Then ' new data, copies headers
DestinationWorksheet.Cells.Clear
End If
' Attempt to create a reference to the Source Data Filtered Rows Range.
Dim sdfrrg As Range
On Error Resume Next
Set sdfrrg = sdrg.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If Not sdfrrg Is Nothing Then
' Create a reference to the Destination Cell (also, add headers).
Dim dCell As Range ' Destination Cell
Set dCell = DestinationWorksheet.Cells(1, DestinationColumn)
If IsEmpty(dCell) Then
srg.Rows(1).Copy dCell
Set dCell = dCell.Offset(1)
Else
Set dCell = DestinationWorksheet.Cells( _
DestinationWorksheet.Rows.Count, DestinationColumn) _
.End(xlUp).Offset(1, 0)
End If
With sdfrrg
.Copy dCell
' Either delete the entire worksheet rows...
'.EntireColumn.Delete
' ... or remove filter to prevent...
SourceWorksheet.AutoFilterMode = False
' ... deleting the entire worksheet rows leaving possible data
' to the right (after the empty column) intact.
.Delete xlShiftUp
End With
Else ' no matches
SourceWorksheet.AutoFilterMode = False
End If
End Sub
I regularly download an excel file that has 1000+ columns, many of these are unwanted and manually deleting them is quite tedious. I found a VBA that will delete the unwanted columns but this method is not suited for a large list.
So, I have a workbook where Sheet1 is the data and columns run from A to BQM. I took all the header names and transposed them into column A in Sheet2 (A2:A1517). I think I'm looking for a way to have the vba look through the table in Sheet2 and delete any matching header titles on Sheet1. Any suggestions? I'm new at this so go slow.
Sub DeleteColumnByHeader()
Set P = Range("A2:BQM2")
For Each cell In P
If cell.Value = "MAP Price" Then cell.EntireColumn.Delete
If cell.Value = "Retail Price" Then cell.EntireColumn.Delete
If cell.Value = "Cost" Then cell.EntireColumn.Delete
If cell.Value = "Additional Specifications" Then cell.EntireColumn.Delete
Next
End Sub
EDIT2: actually works now...
EDIT: added re-positioning of matched columns
Using Match():
Sub DeleteAndSortColumnsByHeader()
Dim wsData As Worksheet, wsHeaders As Worksheet, mHdr, n As Long
Dim wb As Workbook, arr, rngTable As Range, addr
Dim nMoved As Long, nDeleted As Long, nMissing As Long
Set wb = ThisWorkbook 'for example
Set wsData = wb.Sheets("Products")
Set wsHeaders = wb.Sheets("Headers")
'get array of required headers
arr = wsHeaders.Range("A1:A" & _
wsHeaders.Cells(Rows.Count, "A").End(xlUp).Row).Value
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
'shift the data over so we can move columns into the required order
Set rngTable = wsData.Range("a1").CurrentRegion 'original data
addr = rngTable.Address 'remember the position
rngTable.EntireColumn.Insert
Set rngTable = wsData.Range(addr) 'restore to position before insert
'loop over the headers array
For n = 1 To UBound(arr, 1)
mHdr = Application.Match(arr(n, 1), wsData.Rows(1), 0) 'current position of this header
If IsError(mHdr) Then
'required header does not exist - do nothing, or add a column with that header?
wsData.Cells(1, n).Value = arr(n, 1)
nMissing = nMissing + 1
Else
wsData.Columns(mHdr).Cut wsData.Cells(1, n) 'found: move
nMoved = nMoved + 1
End If
Next n
'delete everything not found and moved
With rngTable.Offset(0, rngTable.Columns.Count)
nDeleted = Application.CountA(.Rows(1)) 'count remaining headers
Debug.Print "Clearing: " & .Address
.EntireColumn.Delete
End With
Application.Calculation = xlCalculationAutomatic
Debug.Print "moved", nMoved
Debug.Print "missing", nMissing
Debug.Print "deleted", nDeleted
End Sub
In Sheet2 please clear the cells that display names of columns to delete.
And run the below code.
Sub DeleteColumnByHeader()
For Col = 1517 To 2 Step -1
If Range("Sheet2!A" & Col).Value == "" Then
Columns(Col).EntireColumn.Delete
End If
Next
End Sub
Delete Columns by Headers
The DeleteColumnsByHeaders procedure will do the job.
Adjust the values in the constants section.
The remaining two procedures are here for easy testing.
Testing
To test the procedure, add a new workbook and make sure it contains the worksheets Sheet1 and Sheet2.
Add a module and copy the complete code to it.
Run the PopulateSourceRowRange and the PopulateDestinationColumnRange procedures. Look at the worksheets to see the example setup.
Now run the DeleteColumnsByHeaders procedure. Look at the Destination Worksheet (Sheet1) and see what has happened: all the unwanted columns have been deleted leaving only the 'hundreds'.
Option Explicit
Sub DeleteColumnsByHeaders()
Const sName As String = "Sheet2"
Const sFirst As String = "A2"
Const dName As String = "Sheet1"
Const dhRow As String = "A2:BQM2"
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Create a reference to the Source Column Range (unwanted headers).
Dim srg As Range
Dim srCount As Long
With wb.Worksheets(sName).Range(sFirst)
Dim slCell As Range
Set slCell = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If slCell Is Nothing Then Exit Sub
srCount = slCell.Row - .Row + 1
Set srg = .Resize(srCount)
End With
' Write the values from the Source Range to the Source Data Array.
Dim sData As Variant
If srCount = 1 Then
ReDim sData(1 To 1, 1 To 1): sData(1, 1) = srg.Value
Else
sData = srg.Value
End If
' Create a reference to the Destination Row Range.
Dim drg As Range: Set drg = wb.Worksheets(dName).Range(dhRow)
' Combine all cells containing unwanted headers into the Union Range.
Dim urg As Range
Dim dCell As Range
For Each dCell In drg.Cells
If IsNumeric(Application.Match(dCell, sData, 0)) Then
If urg Is Nothing Then
Set urg = dCell
Else
Set urg = Union(urg, dCell)
End If
End If
Next dCell
Application.ScreenUpdating = False
' Delete the entire columns of the Union Range.
If Not urg Is Nothing Then
urg.EntireColumn.Delete
End If
Application.ScreenUpdating = True
End Sub
' Source Worksheet ('Sheet1'):
' Writes the numbers from 1 to 1807 into the cells of the row range
' and to five rows below.
Sub PopulateSourceRowRange()
With ThisWorkbook.Worksheets("Sheet1").Range("A2:BQM2").Resize(6)
.Formula = "=COLUMN()"
.Value = .Value
End With
End Sub
' Destination Worksheet ('Sheet2'):
' Writes the numbers from 1 to 1807 except the hundreds (100, 200,... 1800)
' to the range 'A2:A1790'. The hundreds are the columns you want to keep.
Sub PopulateDestinationColumnRange()
Dim n As Long, r As Long
r = 1
With ThisWorkbook.Worksheets("Sheet2")
For n = 1 To 1807
If n Mod 100 > 0 Then
r = r + 1
.Cells(r, "A").Value = n
End If
Next n
End With
End Sub