I have a bunch of .xls files that have 6 rows of junk at the beginning of the files and 1 line with the text "Not Classified" in an arbitrary row of column A. I have the below code which deletes the first 6 rows and then finds the cell with the proper text, but I don't know how to then select that row, since my understanding is the Find function is returning like A10 and I do not know how to split the reference in order to Select row 10.
I believe the address function should be able to help in this regard but I am having trouble getting it to work. In the above path is the variable that stores the location of my files and x is the cell with the offending text.
Do while files <>""
Workbooks.Open(path & files).ActiveSheet.Rows("1:6").Delete
Set x = ActiveWorkbook.ActiveSheet.Range("A:A").Find("Not Classified")
If Not x Is Nothing Then
x.Clear
'Obviously this only clears the cell with the offending text and I
'want to delete the whole row
End If
ActiveWorkbook.Close savechanges:=True
files = Dir()
Loop
I believe the address function should be able to help in this regard but I am having trouble getting it to work. In the above path is the variable that stores the location of my files and x is the cell with the offending text.
Option Explicit
Sub test()
Dim strSearchValue As String
Dim LastRow As Long
Dim rngSearch As Range, rngPosition As Range
With ThisWorkbook.Worksheets("Sheet1")
strSearchValue = "Not Classified"
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row '<- Find lastrow to create a range. No need to use thw whole column.
Set rngSearch = .Range("A1:A" & LastRow) '<- Set your range
Set rngPosition = rngSearch.Find(strSearchValue) '<- Find the position of the value
If Not rngPosition Is Nothing Then '<- To avoid error check if the position is not nothing
MsgBox rngPosition.Address '<- Message box with the address
End If
'How to delete row a row. Have in mind that when you delete you must go backwards - from bottom to top to avoid breaking indexing.
.Rows(rngPosition.Row).EntireRow.Delete
End With
End Sub
You could produce a Union of both ranges (rows 1:6 and the row containing "Not Classified") then delete them.
dim r as variant
Do while files <>""
with Workbooks.Open(path & files)
with .worksheets(1) '<~~ know what worksheet you're dealing with
r = application.match("Not Classified", .range("A:A"), 0)
if iserror(r) then
.range("A1:A6").entirerow.Delete
else
.range("A1:A6, A" & r).entirerow.Delete
end if
end with
.Close savechanges:=True
end with
files = Dir()
Loop
Related
I am working on excel VBA and I want all rows that does not equal to my cell value/reference (Cell E5) to be deleted but what is happening right, it deletes all rows in that sheet.
Sub DeleteNotEqualTo()
Dim ws As Worksheet
x = Range("E5").Value
Set ws = ThisWorkbook.Worksheets("Conso")
ws.Range("B8:Z5000").AutoFilter Field:=11, Criteria1:="<> & x"
Application.DisplayAlerts = False
ws.Range("B9:Z5000").SpecialCells(xlCellTypeVisible).Delete
Application.DisplayAlerts = True
On Error Resume Next 'Clear Filter
ws.ShowAllData
On Error GoTo 0
End Sub
I'm assuming that you're setting the reference value in cell E5 (row 5) and starting from row 8 ("B8:Z5000") you crosscheck if for each record field 11 corresponds to that specific value.
I don't recommend this approach because:
when manipulating spreadsheets, you shouldn't use the auto filter. The auto filter is rather designed for visibility when you look at a spreadsheet - not for a programmatic approach. You should be aware that, even if data is filtered out of the visible range, if you'd loop through the records, the program would still take the non visible records into consideration.
by setting a static range, you are always going to be limited by the number of rows you set. You can easily use a dynamic range instead, like that your script will always crosscheck all rows that are not empty.
Option Explicit
'always recommend to use this option, it forces you to
'declare the type of every single variable
Sub DeleteNotEqualTo()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Conso")
Dim x As String 'declaring string variable, you may have to adapt
'x = Range("E5").Value you could also with the cells object, check next line
x = ws.Cells(5, 5).Value '5,5 = row 5, column 5
'now comes the loop
'you have to declare a so called iterator variable to loop, in this case "i"
'the iterator drives the loop through the records
'you also need a variable that determines the last row to be checked
'in this case you define this by checking the so called "UsedRange"
'the "UsedRange" counts from the first row that contains to the last
'check out the indicated link for more info regarding finding the last row
Dim i As Integer 'iterator variable
Dim lrow As Integer 'variable for the last row
lrow = ws.UsedRange.Rows.Count + 4 'assuming that row 5 is the first to contain a value
'now loop through the records
'here I'm assuming field 11 of your auto filter correspond to column 11 in the sheet
'important sidenote: as you're deleting rows, you have to loop bottom up
'if you loop top to bottom, the loop may skip rows that shift up
For i = lrow To 8 Step -1 'assuming that the first row to be checked is row 8
If ws.Cells(i, 11).Value = x Then
ws.Cells(i, 11).EntireRow.Delete
End If
Next i
End Sub
Here's a link with more info regarding finding the last row in a spreadsheet. Very helpful with dynamic ranges:
https://www.automateexcel.com/vba/find-last-row-column-cell/
What worked for me in your workbook is changing this line:
ws.Range("B8:Z5000").AutoFilter Field:=11, Criteria1:="<> & x"
to:
ws.Range("B8:Z5000").AutoFilter Field:=11, Criteria1:="<>*" & x & "*"
The presence of * wildcard changes the criteria from an exact match to a partial match. (This shouldn't have been necessary, but specifying an exact match didn't filter the range correctly for reasons I'm unsure of.)
Option Explicit
Private Sub DeleteNotEqualTo()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Conso")
Dim x As Variant
x = Range("E5").Value
ws.Range("B8:Z5000").AutoFilter Field:=11, Criteria1:="<>*" & x & "*"
Application.DisplayAlerts = False
ws.Range("B9:Z5000").SpecialCells(xlCellTypeVisible).Delete
Application.DisplayAlerts = True
On Error Resume Next 'Clear Filter
ws.ShowAllData
On Error GoTo 0
End Sub
I have a workbook that includes duplicated headers and a page number that I want to remove via vba macro.
The below screenshot repeats itself throughout my workbook, I have tried to write a macro that finds the specific text and delete but causes the data to shift incorrectly and is wildly inefficient. The rows in between the headers are not always 3 so I can't have a macro that blindly deletes 5 rows every x rows. For clarity, I am trying to delete the bolded text while not losing the integrity of the unbolded data.
Is it possible to have a macro that goes over a specific range to delete the repeated headers and metadata depicted above?
Macro attempted to use from How to delete row based on cell value. Understandably changing the text for each header / metadata is not a reasonable solution and causes the data shift.
Sub DeleteRowsWithHyphen()
Dim rng As Range
For Each rng In Range("A2:A20") 'Range of values to loop through
If InStr(1, rng.Value, "Page 1 of 10") > 0 Then 'InStr returns an integer of the position, if above 0 - It contains the string
rng.Delete
End If
Next rng
End Sub
It looks like you can delete all rows that are not number in column. If so then try ..
Sub DeleteNonNumberRows()
Dim rng As Range, Cl As Range, DelRng As Range
LRow = Range("A" & Rows.Count).End(xlUp).Row
Set rng = Range("A1", "A" & LRow)
For Each Cl In rng
If IsNumeric(Cl) = False Or Cl = "" Then
If DelRng Is Nothing Then
Set DelRng = Cl
Else
Set DelRng = Union(DelRng, Cl)
End If
End If
Next
DelRng.EntireRow.Delete
End Sub
Note that rng is starting from A1. Change it as suitable.
I have an excel file like
Original File
I want to transform all the cells that filled with information into a single column. Like
To transform This
How to i do this ?
I searched in internet about that i found just only transform cells in a single row to a single cell. But i couldn't find anything like this. Can you help me about that
This is a bit of code I keep around for this kind of job. It assumes that the values in each row are contiguous, that is there are no blank cells inside the data set. It also assumes that you're on the sheet that contains the data when you trigger it, and that you want the data to be placed on a new worksheet.
Option Explicit
Sub Columnise()
Dim shtSource As Worksheet
Dim shtTarget As Worksheet
Dim rngRow As Range, rngCol As Range
Dim lCount As Long
Set shtSource = ActiveSheet 'Or specify a sheet using Sheets(<name>)
Set rngCol = Range("A1", Range("A" & Rows.Count).End(xlUp))
Set shtTarget = Sheets.Add 'Or specify a sheet using Sheets(<name>)
'Define starting row for the data
lCount = 1
'Loop through each row
For Each rngRow In rngCol
'On each row, loop through all cells until a blank is encountered
Do While rngRow.Value <> ""
'Copy the value to the target
shtTarget.Range("A" & lCount).Value = rngRow.Value
'Move one space to the right
Set rngRow = rngRow.Offset(0, 1)
'Increment counter
lCount = lCount + 1
Loop
Next rngRow
End Sub
You should end up with all the data in a single column on a new worksheet.
EDITED TO ADD: Since you mentioned your data does contain blank cells, it gets more complicated. We'll want to add a way to continue to the actual end of the data, rather than just looping until we hit a blank cell. We'll modify the Do While... condition to this:
Do While rngCell.Column <= Cells(rngCell.Row, Columns.Count).End(xlToLeft).Column
This will loop until the end of the data in the row, then move on. Give it a shot and let us know.
I can pull together a decent macro that does what I need but I forgot that the range will change everyday.
To be specific the row count will get higher.
Right now my macro goes through and hides any row that doesn't have today's date and then copies a set range to a worksheet in a different workbook.
The only problem I have is that range will change everyday, so I figure I need a way to copy only rows with data in them once the rest are hidden and then paste them to the other workbook.
Sub automate()
Dim cell As Range
For Each cell In Range("AB2:AB30000")
If cell.Value < Date And cell.Value <> Empty Then cell.EntireRow.Hidden = True
Next
Range("K28336:K28388,O28336:O28388,P28336:P28388,Q28336:Q28388,R28336:R28388,S28336:S28388,T28336:T28388,U28336:U28388,V28336:V28388,Y28336:Y28388,AA28336:AA28388,AB28336:AB28388").Select
Selection.Copy
Workbooks.Open ("\\gvwac09\Public\Parts\Test\2014 IPU.xlsx")
Sheets("Historical Data").Activate
ActiveSheet.Range("c1").End(xlDown).Offset(1, 0).Select
Selection.PasteSpecial Paste:=xlPasteFormats
ActiveSheet.Paste
This is my macro so far. I'm sorry if I didn't format this post correctly, new to this.
I do not understand exacting what you are attempting but I believe I can give you some useful pointers.
I do not explain the statements I use in the code below. Look them up in the Visual Basic Editor's Help or try searching the web for "Excel VBA xxxxx". Come back with questions if necessary but the more you can discover for yourself, the quicker your skills will develop.
Firstly you need to find the last row containing data. Examining every row down to AB30000 just wastes time. Macro Demo1 below demonstrates two techniques. There are more techniques for finding the last row, none of which are appropriate in every situation. Search StackOverflow for "[excel-vba] find last row". There are lots of relevant questions and answers although the first technique I use is far and away the most popular.
General advice: If you can break your requirement down to a sequence of single issues (such as "find last row"), you will find it easier to search StackOverflow for an answer.
Always include Application.ScreenUpdating = False at the start of your macros if you are going to amend a worksheet. Without this statement, everytime you hide a row, Excel repaints the screen.
I have created some test data which I hope is representative of your data. I have two worksheets Source and Dest. Source contains the full set of data. I copy the selected rows to Dest.
I have used Auto Filter which will be much faster than your technique if it will give you the effect you seek. Play with Auto Filter from the keyboard. If you can get the effect you seek, turn on the Macro Recorder, use Auto Filter to get the selection you seek and switch the Macro Recorder off. Adjust the Macro Recorder's statements to remove Selection and replace the corresponding statements in Demo2.
The secret of Demo2 is Set Rng = .AutoFilter.Range.SpecialCells(xlCellTypeVisible) which sets Rng to the visible rows. If you cannot get Auto Filter to work as you wish and you decide to use your current technique to set uninteresting rows invisible, keep this statement to get the remaining rows. However, I think macro Demo3 uses a better technique.
Option Explicit
Sub demo1()
Dim ColLast As Long
Dim Rng As Range
Dim RowLast As Long
Application.ScreenUpdating = False
With Worksheets("Source")
' This searches up from the bottom of column AB for a cell with a value.
' It is the VBA equivalent of placing the cursor at the bottom of column AB
' and clicking Ctrl+Up.
RowLast = .Cells(Rows.Count, "AB").End(xlUp).Row
Debug.Print "Last row with value in column AB: " & RowLast
' This searches for the last cell with a value.
Set Rng = .Cells.Find(What:="*", After:=.Range("A1"), SearchDirection:=xlPrevious)
If Rng Is Nothing Then
' Worksheet is empty
Else
RowLast = Rng.Row
ColLast = Rng.Column
Debug.Print "Last cell with value is: (" & RowLast & ", " & ColLast & _
") = " & Replace(Rng.Address, "$", "")
End If
End With
End Sub
Sub Demo2()
Dim Rng As Range
Dim SearchDate As String
SearchDate = "14-May-14"
Application.ScreenUpdating = False
With Sheets("Source")
.Cells.AutoFilter
.Cells.AutoFilter Field:=28, Criteria1:=SearchDate
Set Rng = .AutoFilter.Range.SpecialCells(xlCellTypeVisible)
End With
' Rng.Address has a maximum length of a little under 256 characters.
' Rng holds the addresses of all the visible rows but you cannot display
' all those addresses in an easy manner. However, this is only to give
' you an idea of what is in Rng; the Copy statement below uses the full
' set of addresses.
Debug.Print "Visible rows: " & Rng.Address
Rng.Copy Worksheets("Dest").Range("A1")
End Sub
Sub Demo3()
Dim RngToBeCopied As Range
Dim RowCrnt As Long
Dim RowLast As Long
Dim SearchDate As Long
' Excel holds dates as integers and times as fractions.
SearchDate = CLng(DateValue("20 May 2014"))
With Worksheets("Source")
RowLast = .Cells(Rows.Count, "AB").End(xlUp).Row
' Include header row in range to be copied
Set RngToBeCopied = .Rows(1)
For RowCrnt = 2 To RowLast
If .Cells(RowCrnt, "AB").Value = SearchDate Then
Set RngToBeCopied = Union(RngToBeCopied, .Rows(RowCrnt))
End If
Next
End With
Debug.Print RngToBeCopied.Address
RngToBeCopied.Copy Worksheets("Dest").Range("A1")
End Sub
I receive an excel file monthly and have to export parts of it to a new file. I have a list of identifier numbers and I am trying to match the list of numbers in the selected list to the full file and then export the rows of relevant data to a new sheet.
Sub Run_All_Macros()
Application.ScreenUpdating = False
Sheets.Add.Name = "Output"
Call Convert_to_Numbers
Call Highlight_Selected_Contractors
End Sub
'Original Spreadsheet is formatted incorrectly
'Convert PSD Codes to Numbers
Sub Convert_to_Numbers()
Dim xCell As Range
Range("A2:A2500").Select
For Each xCell In Selection
xCell.Value = CDec(xCell.Value)
Next xCell
End Sub
'Highlight Selected Contractors
Sub Highlight_Selected_Contractors()
Dim Full, Selection, Code, SelectedCode As Range
Worksheets("Sheet1").Select
'Set all cells in Column A Sheet 1 to Full
Set Full = Worksheets("Sheet1").Range("A1", Range("A1").End(xlDown))
'Set all cells in Column A Sheet 2 to Selection
Worksheets("Sheet2").Select
Set Selection = Worksheets("Sheet2").Range("A1", Range("A1").End(xlDown))
'If the numbers match highlight the cell
For Each Code In Full
For Each SelectedCode In Selection
If Code.Value = SelectedCode.Value Then
*** Code.Select
Selection.Copy
Sheets.Select ("Output")
ActiveSheet.Paste
End If
Next SelectedCode
Next Code
End Sub
After executing this code column A in 'Output' is filled with zeros from A2:A2500. From messing around with breakpoints I've identified the problem to be where I've placed * but I'm not sure what's wrong with what's written there.
Thanks
There few errors in the code above and I also have few suggestions and finally the code.
ERRORS
1) Sheets.Add.Name = "Output" This line will give you an error if there is already a sheet called "Ouput". Delete the sheet first and then create it. You must be wondering that in case the sheet is not there, then how can I delete it? For such scenarios you can use On Error Resume Next which should be avoided in most cases.
2) When working with ranges, always specify which sheet you are referring to else Excel will always assume that you are referring to the "ActiveSheet". As you realized that Sub Convert_to_Numbers() was taking Output Sheet into consideration whereas you want the operation to happen in "Output" Sheet.
3) Dim Full, Selection, Code, SelectedCode As Range As mentioned in my comments earlier avoid using Excel Reserved words as variables. Also unlike VB.Net, if you declare variables as you did in VBA then only the last variable will be declared as Range. The other 3 will be declared as variant. VB defaults the variable to being type Variant. A Variant type variable can hold any kind of data from strings, to integers, to long integers, to dates, to currency etc. By default “Variants” are the “slowest” type of variables. Variants should also be avoided as they are responsible for causing possible “Type Mismatch Errors”. It’s not that we should never use Variants. They should only be used if you are unsure what they might hold on code execution.
4) Avoid the use of words like .ActiveCell, Selection, Select, Activate etc. They are a major cause of errors. Also they slow your code down.
SUGGESTIONS
1) Instead to using Sheets("WhatEver") every time, store it in a variable and then use that variable. Will cut down your code.
2) Indent your code :) it's much easier to read
3) Group tasks together. For example if you have to do with something with a particular sheet then keep it together. It is easier to read and amend if required.
4) Instead of hard coding your values, get actual ranges. Range("A2:A2500") is a classic example. Will you always have data till 2500? What if it is less or more?
5) End(xlDown) will never give you the last row if there is a blank cell in between. To get the last row in a column, say A in "Sheet1", use this
Sheets("Sheet1").Range("A" & Rows.Count).End(xlUp).Row`
6) Instead of looping, you can use the WorksheetFunction CountIf(). Loops should be avoided as much as possible as they slow down your code.
7) Use appropriate Error handling.
8) Comment your code. It's much easier to know what a particular code or section is doing.
CODE
Option Explicit
Sub Run_All_Macros()
Dim ws1I As Worksheet, ws2I As Worksheet, wsO As Worksheet
Dim ws1LRow As Long, ws2LRow As Long, wsOLr As Long
Dim xCell As Range, rFull As Range, rSelection As Range
Dim rCode As Range, rSelectedCode As Range
On Error GoTo Whoa '<~~ Error Handling
Application.ScreenUpdating = False
'~~> Creating the Output Sheet
Application.DisplayAlerts = False
On Error Resume Next
Sheets("Output").Delete
On Error GoTo 0
Sheets.Add.Name = "Output"
Application.DisplayAlerts = True
'~~> Working with 1st Input Sheet
Set ws1I = Sheets("Sheet1")
With ws1I
'~~> Get Last Row of Col A
ws1LRow = .Range("A" & Rows.Count).End(xlUp).Row
'~~> Set the range we want to work with
Set rFull = .Range("A1:A" & ws1LRow)
'~~> The following is not required unless you want to just format the sheet
'~~> This will have no impact on the comparision. If you want you can
'~~> uncomment it
'For Each xCell In .Range("A2:A" & ws1LRow)
'xCell.Value = CDec(xCell.Value)
'Next xCell
End With
'~~> Working with 2nd Input Sheet
Set ws2I = Sheets("Sheet2") '<~~ Input Sheet 2
ws2LRow = ws2I.Range("A" & Rows.Count).End(xlUp).Row
Set rSelection = ws2I.Range("A1:A" & ws2LRow)
'~~> Working with Output Sheet
Set wsO = Sheets("Output")
wsO.Range("A1") = "Common values"
wsOLr = wsO.Range("A" & Rows.Count).End(xlUp).Row + 1
'~~> Comparison : If the numbers match copy them to Output Sheet
For Each rCode In rFull
If Application.WorksheetFunction.CountIf(rSelection, rCode.Value) > 0 Then
rCode.Copy wsO.Range("A" & wsOLr)
wsOLr = wsOLr + 1
End If
Next rCode
MsgBox "Done"
LetsContinue:
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume LetsContinue
End Sub
Let me know if you still get any errors :)
HTH