EXCEL VBA - Delete if a column exists - vba

I have a requirement wherein, I need to delete that Particular column if that exists.
I am trying to locate the Particular column through column header.
This is my code,
If sColumnName = (WorksheetFunction.Match("RSD", Sheets("RS_Report").Rows(1), 0)) And sColumnName = True Then
DDC= WorksheetFunction.Match("RSD", Sheets("RS_Report").Rows(1), 0)
DFC= GetColumnLetter(DDC)
Range(DFC& 1 & ":" & DFC& lastrow).Select
Selection.Delete Shift:=xlUp
the GetColumnLetter and lastrow are my user defined functions and they return correct values. I am not sure how to check if a column exists or not. Kindly help me with this. Share your thoughts.

you can simply go like this
Dim col As Variant
With Sheets("RS_Report") '<--| reference relevant worksheet
col = WorksheetFunction.Match("RSD", .Rows(1), 0) '<--| try searching its row 1 for wanted header text
If Not IsError(col) Then .Columns(col).Delete '<--| if found then delete its entire column
End With

There are three ways to do this.
1) A for loop that looks to the extents of the header row for a specific string.
Pro: its easy
Con: the string has to be exact
something like
Dim string as yourString
Dim lColumn As Long
lColumn = ws.UsedRange.Columns.Count
yourString = whatever
for x = 1 to lcolumn
if range(cells(1, 1), Cells(1, x)).Value = yourString
Columns(x).EntireColumn.Delete
End If
next
2) use the Range.Find method which you can learn about here https://msdn.microsoft.com/en-us/library/office/ff839746.aspx
Here is a short rough example you can use as reference:
Sub Header_Format()
Dim rLastCell As Range
Set rLastCell = UpdateSheet.Cells.Find(What:="*", After:=UpdateSheet.Cells(1, 1), LookIn:=xlFormulas, LookAt:= _
xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False)
With UpdateSheet.Range(Cells(4, 1), Cells(4, rLastCell.Column))
.Copy MasterSheet.Cells(1, 1)
.Copy RemovalSheet.Cells(1, 1)
End With
End Sub
3) Finally there is using the match method, which someone spoke on already.
https://msdn.microsoft.com/en-us/library/office/ff835873.aspx

Related

Range from first empty row to last filled row [duplicate]

I want to select the formatted range of an Excel sheet.
To define the last and first row I use the following functions:
lastColumn = ActiveSheet.UsedRange.Column - 1 + ActiveSheet.UsedRange.Columns.Count
lastRow = ActiveSheet.UsedRange.Rows(ActiveSheet.UsedRange.Rows.Count).Row
In the next step I want to select this area:
Formula should look like this:
Range(cells(1, 1), cells(lastRow, lastColumn).Select
However, this is not working. Maybe somebody has an idea what is wrong with it. Thanks a lot!
I recorded a macro with 'Relative References' and this is what I got :
Range("F10").Select
ActiveCell.Offset(0, 3).Range("A1:D11").Select
Heres what I thought : If the range selection is in quotes, VBA really wants a STRING and interprets the cells out of it so tried the following:
Dim MyRange as String
MyRange = "A1:D11"
Range(MyRange).Select
And it worked :) ie.. just create a string using your variables, make sure to dimension it as a STRING variables and Excel will read right off of it ;)
Following tested and found working :
Sub Macro04()
Dim Copyrange As String
Startrow = 1
Lastrow = 11
Copyrange = "A" & Startrow & ":D" & Lastrow
Range(Copyrange).Select
End Sub
I ran into something similar - I wanted to create a range based on some variables. Using the Worksheet.Cells did not work directly since I think the cell's values were passed to Range.
This did work though:
Range(Cells(1, 1).Address(), Cells(lastRow, lastColumn).Address()).Select
That took care of converting the cell's numerical location to what Range expects, which is the A1 format.
If you just want to select the used range, use
ActiveSheet.UsedRange.Select
If you want to select from A1 to the end of the used range, you can use the SpecialCells method like this
With ActiveSheet
.Range(.Cells(1, 1), .Cells.SpecialCells(xlCellTypeLastCell)).Select
End With
Sometimes Excel gets confused on what is the last cell. It's never a smaller range than the actual used range, but it can be bigger if some cells were deleted. To avoid that, you can use Find and the asterisk wildcard to find the real last cell.
Dim rLastCell As Range
With Sheet1
Set rLastCell = .Cells.Find("*", .Cells(1, 1), xlValues, xlPart, , xlPrevious)
.Range(.Cells(1, 1), rLastCell).Select
End With
Finally, make sure you're only selecting if you really need to. Most of what you need to do in Excel VBA you can do directly to the Range rather than selecting it first. Instead of
.Range(.Cells(1, 1), rLastCell).Select
Selection.Font.Bold = True
You can
.Range(.Cells(1,1), rLastCells).Font.Bold = True
You're missing a close parenthesis, I.E. you aren't closing Range().
Try this Range(cells(1, 1), cells(lastRow, lastColumn)).Select
But you should really look at the other answer from Dick Kusleika for possible alternatives that may serve you better. Specifically, ActiveSheet.UsedRange.Select which has the same end result as your code.
you are turning them into an address but Cells(#,#) uses integer inputs not address inputs so just use lastRow = ActiveSheet.UsedRange.Rows.count and lastColumn = ActiveSheet.UsedRange.Columns.Count
I tried using:
Range(cells(1, 1), cells(lastRow, lastColumn)).Select
where lastRow and lastColumn are integers, but received run-time error 1004. I'm using an older VB (6.5).
What did work was to use the following:
Range(Chr(64 + firstColumn) & firstRow & ":" & Chr(64 + lastColumn) & firstColumn).Select.

VBA: Transferring data from one worksheet to another with a "moving range"

I'm trying to create a dashboard of my inventory system. The dashboard will display Sales for the day (category 1), Purchases that need to be made (Category 2), Purchase Orders that are to be expected (Cateogry 3), and Work in Process (Category 4). For this question, I'm only going to focus on category 2, Purchases that need to be made.
I'm trying to transfer all the data from Worksheets("Purchasing") to the Dashboard under category 2. I'm trying to use named ranges to do this, because the range of each category will fluctuate as items are added/deleted. You can find a sample of the workbook that I'm working on here - it's on excelforum.com.
The code below is what I have so far. It works to a degree, but the Range("PurchaseStart"), which is Cell $A$8, starts at A:1. I don't know how to only select the named range that I'm looking for. I added "End #" statements at the end of each row to signify a cutoff and hope to trick excel into only selecting the range of the particular category.
Option Explicit
Sub purchPull()
Dim Dashboard As Worksheet
Dim Purchasing As Worksheet
Dim PM As Range, D As Range, Rng As Range
Dim purchName As Range
Set Purchasing = Worksheets("Purchasing")
Set Dashboard = Worksheets("Dashboard")
' Go through each Item in Purchasing and check to see if it's anywhere within the named range "PurchaseStart"
' In this case it should be "A8:A9" - as there is nothing in the dasboard yet
For Each PM In Purchasing.Range(Purchasing.Cells(1, 1), Purchasing.Cells(Purchasing.Rows.Count, 1).End(xlUp))
With Dashboard.Range("PurchaseStart", Dashboard.Cells(Dashboard.Rows.Count, 1))
Set Rng = .Find(What:=PM.Offset(0, 1), _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not Rng Is Nothing Then
' Do nothing, as we don't want duplicates
Else
' From the start of the named range, transfer data over - THIS IS THE PROBLEM AREA
With Dashboard.Range("PurchaseStart", Dashboard.Cells(.Rows.Count, 1)).End(xlUp)
.Offset(1, 1) = PM.Offset(0, 0) ' Order Number
.Offset(1, 2) = PM.Offset(0, 1) ' SKU
.Offset(1, 3) = PM.Offset(0, 3) ' Qty
.Offset(1, 4) = PM.Offset(0, 4) ' Date
End With
End If
End With
Next
End Sub
You could do something along the lines of:
(This assumes that the beginning of each data section has some header in place, ie "Need to be made" and then below that header is where the data for that section goes):
Sub findDataStartRow()
Dim f as Range, dataStartRange as Range
Set f = Columns(1).Find(what:="Need to be made", lookat:xlWhole)
If Not f is Nothing Then
dataStartRange = Cells(f.row + 1, 1) 'Do stuff with this range... maybe insert rows below it to start data
Else: Msgbox("Not found")
Exit Sub
End if
End Sub
Do similar for each section. This way, no matter where the header goes (and therefore the beginning of where the data should be placed), you'll always have a named range of the location right below the header.
Alternatively, if you want to add the data to the end of the section, just find the header for the section below where you want the data to be and set the dataStartRange = Cells(f.row - 1, 1) after modifying the .Find correctly.
I figured it out. I think it's a pretty good way of handling the problem, but if anyone can think of a better way, I'd love to hear it. Thanks for the help everyone.
Option Explicit
Sub purchPull()
Dim Dashboard As Worksheet
Dim Purchasing As Worksheet
Dim PM As Range, D As Range, Rng As Range
Dim purchName As Range
Dim lastRow As Long
Dim firstRow As Long
Set Purchasing = Worksheets("Purchasing")
Set Dashboard = Worksheets("Dashboard")
' first row of named range "PurchaseStart"
firstRow = Dashboard.Range("PurchaseStart").Row + Dashboard.Range("PurchaseStart").Rows.Count
' Go through each Item in Purchasing and check to see if it's anywhere within the named range "PurchaseStart"
With Purchasing
For Each PM In Purchasing.Range(Purchasing.Cells(2, 1), Purchasing.Cells(Purchasing.Rows.Count, 1).End(xlUp))
With Dashboard.Range("PurchaseStart", Dashboard.Cells(Dashboard.Rows.Count, 1))
Set Rng = .Find(What:=PM.Offset(0, 0), _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not Rng Is Nothing Then
' Do nothing, as we don't want duplicates
Else
' Identify the last row within the named range "PurchaseStart"
lastRow = Dashboard.Range("PurchaseStart").Cells(1, 1).End(xlDown).Row
' Transfer the data over
With Dashboard.Cells(lastRow, 1).End(xlUp)
.Offset(1, 0).EntireRow.Insert
.Offset(1, 0) = PM.Offset(0, 0) ' Order Number
.Offset(1, 1) = PM.Offset(0, 1) ' SKU
.Offset(1, 2) = PM.Offset(0, 2) ' Qty
.Offset(1, 3) = PM.Offset(0, 3) ' Date
End With
End If
End With
Next
End With
End Sub

VBA: copying the first empty cell in the same row

I am a new user of VBA and am trying to do the following (I got stuck towards the end):
I need to locate the first empty cell across every row from column C to P (3 to 16), take this value, and paste it in the column B of the same row.
What I try to do was:
Find non-empty cells in column C, copy those values into column B.
Then search for empty cells in column B, and try to copy the first non-empty cell in that row.
The first part worked out fine, but I am not too sure how to copy the first non-empty cell in the same row. I think if this can be done, I might not need the first step. Would appreciate any advice/help on this. There is the code:
Private Sub Test()
For j = 3 To 16
For i = 2 To 186313
If Not IsEmpty(Cells(i, j)) Then
Cells(i, j - 1) = Cells(i, j)
End If
sourceCol = 2
'column b has a value of 2
RowCount = Cells(Rows.Count, sourceCol).End(xlUp).Row
'for every row, find the first blank cell, copy the first not empty value in that row
For currentRow = 1 To RowCount
currentRowValue = Cells(currentRow, sourceCol).Value
If Not IsEmpty(Cells(i, 3)) Or Not IsEmpty(Cells(i, 4)) Or Not IsEmpty(Cells(i, 5)) Or Not IsEmpty(Cells(i, 6)) Then
Paste
~ got stuck here
Next i
Next j
End Sub
Your loop is really inefficient as it is iterating over millions of cells, most of which don't need looked at. (16-3)*(186313-2)=2,422,043.
I also don't recommend using xlUp or xlDown or xlCellTypeLastCell as these don't always return the results you expect as the meta-data for these cells are created when the file is saved, so any changes you make after the file is saved but before it is re-saved can give you the wrong cells. This can make debugging a nightmare. Instead, I recommend using the Find() method to find the last cell. This is fast and reliable.
Here is how I would probably do it. I'm looping over the minimum amount of cells I can here, which will speed things up.
You may also want to disable the screenupdating property of the application to speed things up and make the whole thing appear more seemless.
Lastly, if you're new to VBA it's good to get in the habit of disabling the enableevents property as well so if you currently have, or add in the future, any event listeners you will not trigger the procedures associated with them to run unnecessarily or even undesirably.
Option Explicit
Private Sub Test()
Dim LastUsed As Range
Dim PasteHere As Range
Dim i As Integer
Application.ScreenUpdating=False
Application.EnableEvents=False
With Range("B:B")
Set PasteHere = .Find("*", .Cells(1, 1), xlFormulas, xlPart, xlByRows, xlPrevious, False, False, False)
If PasteHere Is Nothing Then Set PasteHere = .Cells(1, 1) Else: Set PasteHere = PasteHere.Offset(1)
End With
For i = 3 To 16
Set LastUsed = Cells(1, i).EntireColumn.Find("*", Cells(1, i), xlFormulas, xlPart, xlByRows, xlPrevious, False, False, False)
If Not LastUsed Is Nothing Then
LastUsed.Copy Destination:=PasteHere
Set PasteHere = PasteHere.Offset(1)
End If
Set LastUsed = Nothing
Next
Application.ScreenUpdating=True
Application.EnableEvents=True
End Sub
Sub non_empty()
Dim lstrow As Long
Dim i As Long
Dim sht As Worksheet
Set sht = Worksheets("Sheet1")
lstrow = sht.Cells(sht.Rows.Count, "B").End(xlUp).Row
For i = 1 To lstrow
If IsEmpty(Range("B" & i)) Then
Range("B" & i).Value = Range("B" & i).End(xlToRight).Value
End If
Next i
End Sub

Excel Macro multiple range concatenation

I am trying to concatenate cells through excel VBA. This involves multiple ranges. Below is my table
Degree1
Course1,Course2,Course3
Course4,course5,course6
Degree2
Course1,Course2
Course3,Course4
Course5
Course6,Course7
Degree3
Course1,Course2,Course3
Course4,course5,course6
Course7
I want to concatenate all the courses listed below a degree into a single cell next to the degree. Each degree has multiple courses & the # of rows differ for each degree.
I am using excel find function to identify the cell contains the degree & select the courses below it. I am also using the concat function from http://www.contextures.com/rickrothsteinexcelvbatext.html so that I can concatenate the selected ranges.
I tried to write the below code but this is not working, I am getting value error in the end. I guess the range is not stored in the variable
Sub concatrange()
Dim D1Crng As Range 'to set courses under degree1 as range
Dim D2Crng As Range
Dim D3Crng As Range
Dim D1cell As Range 'to identify the cell of D1 and set it as range
Dim D2cell As Range
Dim D3cell As Range
Range("A1:B100").Select
Selection.Find(What:="Degree1", _
LookIn:=xlValues, LookAt:=xlPart, _
SearchOrder:=xlByColumns, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Select
ActiveCell.Select
Set D1cell = Selection
Range(D1cell).Activate
ActiveCell.Offset(1, 0).End(xlDown).Select
Set D1Crng = Selection
Range(D1cell).Activate
ActiveCell.Offset(0, 1).Select
Selection.Formula = "=concat("","",D1Crng)"
End sub
I am repeating the above process for concatenating for other degrees.
VBA's .Join command should work well here.
Sub many_degrees()
Dim rw As Long
With ActiveSheet
For rw = 1 To .Cells(Rows.Count, 1).End(xlUp).Row
If LCase(Left(.Cells(rw, 1).Value, 6)) = "degree" Then
If Application.CountA(.Cells(rw, 1).Resize(3, 1)) > 2 Then
.Cells(rw, 2) = Join(Application.Transpose(.Range(.Cells(rw, 1).Offset(1, 0), .Cells(rw, 1).End(xlDown)).Value), Chr(44))
Else
.Cells(rw, 2) = .Cells(rw, 1).Offset(1, 0).Value
End If
End If
Next rw
End With
End Sub
I have accounted for the case where only one (or none) line of degrees exists below the DegreesX title. The code does depend upon each 'title' starting with Degree as the first 6 characters (not case sensitive). I've used .Offset(x, y) where a simple +1 to the row or column probably would have sufficed, but that may help in understanding the purpose of the various code lines.
      

Find matching cell value in named range

I am trying to find the matching cell value in a named range in my excel workbook (.vba). I know the value that I am looking for, and also know the name of the range, and when I run the code the first time it runs with no problem, but on the second run with a new range name, I get an error.
I have tried a couple different ways to search the named range, and both ways result with the same error. The error is: "Method 'Range' of object '_Global' failed".
My initial code that I tried is:
'march through the list of racks
For i = iFirstRackRow To iLastRackRow
iCurrRackSize = Sheets("PLC I-O").Cells(i, 6).value
iHardwareIndexEnd = iHardwareIndex + iCurrRackSize - 1
rngCardsName = Trim(Sheets("PLC I-O").Cells(i, 2).value & "Cards")
'march through the rack hardware
For j = iHardwareIndex To iHardwareIndexEnd
modCardSize = 0
'march through each card in the rack
For Each zCell In Range(rngCardsName)
If zCell = Sheets("PLC I-O").Cells(j, 2) Then
modCardSize = Sheets("Links").Cells(zCell.Row, zCell.Column + 1).value
Exit For
End If
Next zCell
If modCardSize <> 0 Then
'io module matched
NumRows = NumRows + modCardSize
Else
'processor or adapter module found
NumRows = NumRows + 1
End If
Next
iHardwareIndex = iHardwareIndex + iCurrRackSize
Next
Or I have also tried:
Dim rngFoundCell As Range
With Range(rngCardsName)
Set rngFoundCell = .Find(What:=Sheets("PLC I-O").Cells(j, 2).value, _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not rngFoundCell Is Nothing Then
'cell match was found
rngrow = rngFoundCell.Row
rngcol = rngFoundCell.Column
modCardSize = Sheets("Links").Cells(rngrow, rngcol + 1).value
Else
'cell match was not found
End If
End With
I am not sure what I am doing wrong here. Please help.
I think that the problem is the After:= parameter of your .Find . Try to change the parameter with the value After:=.Cells(1).
Thanks everyone! I found this issue. It was the Trim instruction. Since there were multiple spaces in the Sheets("PLC I-O").Cells(i,2).value, I needed to use a custom function to make sure all spaces were removed.