Find and replace inside a Cell (For particular worksheet ans column) - vba

In my Excel file I have:
A
1 10-30
2 40-45
3 30-80
There can be any range of numbers separated by - in any cell.
In any particular column (might be any cell) i want to remove all text from the start to the - hyphen.
Example: 40-45 will be replaced with 45.
I've asked this question previously and i got the following solution
Sub Update()
Application.ScreenUpdating = False
Application.EnableEvents = False
Dim ws As Worksheet, ur As Range, r As Range
For Each ws In Sheet
Set ur = ws.UsedRange
For Each r In ur
On Error Resume Next
r = Split(r, "-")(1)
Next
Next
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
But by using the Following script all the worksheets are getting updated.
I want it to updated in selected worksheet and only selected columns like J, K or L.(columns)
Please help me out with this problem.

Sorry if i am missing something here but why don't you just select the column and do Ctrl+h (replace) and in find, write '*-' (without quotes) and click replace all, without writing anything in the replace with field.
I don't understand the need of a macro. Again, apologies if i am missing something here.

This will restrict the replacement to the active sheet, columns J thru L:
Sub Update()
Application.ScreenUpdating = False
Application.EnableEvents = False
Dim ws As Worksheet, ur As Range, r As Range
Set ur = Range("J:L").Cells.SpecialCells(xlCellTypeConstants)
For Each r In ur
If InStr(1, r.Value, "-") > 0 Then
r.Value = Split(r.Value, "-")(1)
End If
Next
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub

You'll need to take out the for loop over all the worksheets and pick a range that is only a desired column, not the whole used part of the sheet as it current it. A basic way of doing it:
Sub Update()
Application.ScreenUpdating = False
Application.EnableEvents = False
Dim ws As Worksheet, ur As Range, r As Range
Set ws = ActiveSheet
Set ur = ws.Range(A:A) ' sets the range as a whole column with the chosen letter
For Each r In ur
On Error Resume Next
r = Split(r, "-")(1)
Next
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
You'll just have to replace A:A in the code with whatever column you want e.g. G:G for column G. Depends how exactly you want to picking your columns while using this code.

Related

VBA - Range("All cells but a few")

I am trying to clear the contents of all cells on a worksheet apart from a specific range. I have tried to copy the range to the clipboard then to paste it back on again in the same place, however excel being the usual tricky beast - it doesn't want to play ball.
The range I would like to keep the same is AB1:AC5.
Any Suggestions Apprichiated...
(here is my code)
Sub Button21_Click()
Application.ScreenUpdating = False
With Worksheets(2)
.Range("AB1:AC5").Copy
.Cells.ClearContents
.Paste(Destination:=Sheets("Data").Range("AB1"))
End With
Application.ScreenUpdating = True
End Sub
use an array instead:
Sub Button21_Click()
Application.ScreenUpdating = False
Dim oldValues As Variant
With Worksheets(2)
oldValues = .Range("AB1:AC5").Value
.Cells.ClearContents
.Range("AB1:AC5").Value = oldValues
End With
Application.ScreenUpdating = True
End Sub

Runtime error 13 in a for i loop, which used to work

Background:
I want to hide columns in a sheet based on whether there is an x in row 7. The x is not typed in but filled in via a formula.
I used the following code in another worksheet, were it works. The only thing I changed is the name of the sub, the worksheet and the row (7 instead of 5).
However whenever I try to manually run this sub from the vba editor as a test, it produces a runtime error 13 (mismatched type).
Sub hidCol2()
Dim i As Long
Application.ScreenUpdating = False
Set ws = ThisWorkbook.Worksheets("Zeitplan")
ws.Cells.EntireColumn.Hidden = False
For i = Cells(7, Columns.Count).End(xlToLeft).Column To 1 Step -1
If Cells(7, i) = "x" Then Cells(7, i).EntireColumn.Hidden = True
Next i
Application.ScreenUpdating = True
End Sub
My Question:
Why does the above code produce a runtime error 13, what do I need to correct?
Here it is :
Note it works without Dim ws but I think it's a good practice to dimension the variables before use.
If anyone can let me know why Dim ws here wasn't necessary that would clear some doubts in my head.
Sub hidCol2()
Dim i As Long
Dim ws As Worksheet 'As Suggested by #eirikdaude but I don't know why it worked without it as well (Tested on a workbook with a single worksheet)
Application.ScreenUpdating = False
Set ws = ThisWorkbook.Worksheets("Zeitplan")
ws.Activate
ws.Cells.EntireColumn.Hidden = False
For i = ws.Cells(7, Columns.Count).End(xlToLeft).Column To 1 Step -1
If Trim(ws.Cells(7, i).Text) = "x" Then ws.Cells(7, i).EntireColumn.Hidden = True
Next i
Application.ScreenUpdating = True
End Sub

Using UNION and Ranges To Speed Up Deleting Columns? [duplicate]

This question already has answers here:
Improving the performance of FOR loop
(3 answers)
Closed 7 years ago.
Trying to use Union and ranges to speed up deleting empty columns, across all sheets in my workbook, except "AA" and "Word Frequency"
Sample workbook
Example of sheet before:
Example of sheet after (note, I will need to write separate script to shift keywords up, you can't see all the keywords, but only the columns with data in them are left):
In my search for a method to speed up deleting columns in a sheet if the column is empty (except the header), I was directed by #chrisneilsen to reference the thread Improving the performance of FOR loop.
That thread shed light on the fact that deleting columns individually slows down performance significantly. Script speed can be improved by defining a "master" range to include all the ranges (columns) to be deleted (by using Union), and then simply deleting the "master" range.
As a VBA noob, I used to following references to learn about Ranges, Union, UBound and LBound to understand the code in the thread mentioned above:
Excel-Easy.com: Using UBound and LBound, Dynamic Arrays (Using ReDim)
Youtube: Using UNION method to select (and modify) multiple ranges
My old (slow) script that works, but takes about about 3 hours to run across ~30 sheets and deleting ~100 columns each sheet:
Sub Delete_No_Data_Columns()
Dim col As Long
Dim h 'to store the last columns/header
h = Range("E1").End(xlToRight).Column 'find the last column with the data/header
Application.ScreenUpdating = False
For col = h To 5 Step -1
If Application.CountA(Columns(col)) = 1 Then Columns(col).Delete
Next col
End Sub
Almost working script (for one sheet), using the same approach as #chrisneilsen code in thread mentioned above. When I run it, it doesn't do anything, however #chrisneilsen noted there were 2 syntax errors (Column. instead of Columns.) and that I was mixing an implicit ActiveSheet (by using Columns without a qualifier) with an explicit sheet Worksheets("Ball Shaker"). Errors in code are commented below.
Sub Delete_No_Data_Columns_Optimized()
Dim col As Long
Dim h 'to store the last columns/header
Dim EventState As Boolean, CalcState As XlCalculation, PageBreakState As Boolean
Dim columnsToDelete As Range
Dim ws as Worksheet '<<<<<<<<< Fixing Error (qualifying "Columns." properly)
On Error GoTo EH:
'Optimize Performance
Application.ScreenUpdating = False
EventState = Application.EnableEvents
Application.EnableEvents = False
CalcState = Application.Calculation
Application.Calculation = xlCalculationManual
PageBreakState = ActiveSheet.DisplayPageBreaks
ActiveSheet.DisplayPageBreaks = False
' <<<<<<<<<<<<< MAIN CODE >>>>>>>>>>>>>>
Set ws = ActiveSheet
h = Range("E1").End(xlToRight).Column 'find the last column with the data/header
'<<<<<<<<<<<<<< Errors corrected below in comments >>>>>>>>>>>>
For col = h To 5 Step -1
If Application.CountA(Column(col)) = 1 Then
'<<<<< should be Application.CountA(ws.Columns(col)) = 1
If columnsToDelete Is Nothing Then
Set columnsToDelete = Worksheets("Ball Shaker").Column(col)
'should be columnsToDelete = ws.Columns(col)
Else
Set columnsToDelete = Application.Union(columnsToDelete, Worksheets("Ball Shaker").Column(col))
'should be columnsToDelete = Application.Union(columnsToDelete, ws.Columns(col))
End If
End If
Next col
'<<<<<<<<<<<<<< End Errors >>>>>>>>>>>>>>>>
If Not columnsToDelete Is Nothing Then
columnsToDelete.Delete
End If
' <<<<<<<<<<<< END MAIN CODE >>>>>>>>>>>>>>
CleanUp:
'Revert optmizing lines
On Error Resume Next
ActiveSheet.DisplayPageBreaks = PageBreakState
Application.Calculation = CalcState
Application.EnableEvents = EventState
Application.ScreenUpdating = True
Exit Sub
EH:
' Handle Errors here
Resume CleanUp
End Sub
Working code that runs across all sheets in workbook, in about ~6 minutes (except "AA" and "Word Frequency" worksheets, which I don't need to format):
Option Explicit
Sub Delete_No_Data_Columns_Optimized_AllSheets()
Dim sht As Worksheet
For Each sht In Worksheets
If sht.Name <> "AA" And sht.Name <> "Word Frequency" Then
sht.Activate 'go to that Sheet!
Delete_No_Data_Columns_Optimized sht.Index 'run the code, and pass the sht.Index _
'of the current sheet to select that sheet
End If
Next sht 'next sheet please!
End Sub
Sub Delete_No_Data_Columns_Optimized(shtIndex As Integer)
Dim col As Long
Dim h 'to store the last columns/header
Dim EventState As Boolean, CalcState As XlCalculation, PageBreakState As Boolean
Dim columnsToDelete As Range
Dim ws As Worksheet
Set ws = Sheets(shtIndex) 'Set the exact sheet, not just the one that is active _
'and then you will go through all the sheets
On Error GoTo EH:
'Optimize Performance
Application.ScreenUpdating = False
EventState = Application.EnableEvents
Application.EnableEvents = False
CalcState = Application.Calculation
Application.Calculation = xlCalculationManual
PageBreakState = ActiveSheet.DisplayPageBreaks
ActiveSheet.DisplayPageBreaks = False
' <<<<<<<<<<<<< MAIN CODE >>>>>>>>>>>>>>
h = ws.Range("E1").End(xlToRight).Column 'find the last column with the data/header
For col = h To 5 Step -1
If ws.Application.CountA(Columns(col)) = 1 Then 'Columns(col).Delete
If columnsToDelete Is Nothing Then
Set columnsToDelete = ws.Columns(col)
Else
Set columnsToDelete = Application.Union(columnsToDelete, ws.Columns(col))
End If
End If
Next col
If Not columnsToDelete Is Nothing Then
columnsToDelete.Delete
End If
' <<<<<<<<<<<< END MAIN CODE >>>>>>>>>>>>>>
CleanUp:
'Revert optmizing lines
On Error Resume Next
ActiveSheet.DisplayPageBreaks = PageBreakState
Application.Calculation = CalcState
Application.EnableEvents = EventState
Application.ScreenUpdating = True
Exit Sub
EH:
' Handle Errors here
Resume CleanUp
End Sub
Note: Trying to delete columns and shift to left, so columns with data inside will all be grouped together neatly after script is run.
Is this the best way to utilize Union and ranges for deleting columns? Any help would be greatly appreciated.
The special cells method actually will not serve you so well here. Instead, find the last row of data in your sheet and delete only the cells in the column up to the that row and shift everything to the left. This will be much faster than deleting an entire column!
Sub Delete_No_Data_Columns()
Dim col As Long, lRow as Long
Dim h as Long'to store the last columns/header
lRow = Range("E" & Rows.Count).End(xlUp).Row ' assumes column E will have last used row ... adjust as needed
h = Range("E1").End(xlToRight).Column 'find the last column with the data/header
For col = h To 5 Step -1
If Application.CountA(Columns(col)) = 1 Then
Range(Cells(2,col),Cells(lRow,col)).Delete shift:=xlToLeft
End If
Next col
Application.ScreenUpdating = False ' i think you want this at the beginning of the program, no?
End Sub

How to loop through range names and hide rows if cell = 0?

I have several VBA routines in an Excel 2007. There is a template worksheet which gets copied (and accordingly altered) up to 50 times. Now, this template contains a range called "HideRows", so this range gets copied several times in all those new worksheets. I want to hide all rows that contain the value 0 in the range "HideRows". Not all rows shall be hidden, only those rows that contain the value 0. This is what I've got so far:
Option Explicit
Sub HideEmptyRows()
Dim rngName As Range
Dim cell As Range
Application.ScreenUpdating = False
For Each rngName In ActiveWorkbook.Names
If rngName.Name = "HideRows" Then
With cell
For Each cell In rngName
If .Value = 0 Then
.EntireRow.Hidden = True
End If
Next cell
End With
End If
Next rngName
What's wrong here and what do I need to do to get it to work?
You can address the named range directly without looping. There is no test that this named range exists, as per your description it is safe to assume so.
Secondly, do not use the "with" statement outside of the loop that sets the referenced variable. Try this instead:
Option Explicit
Sub HideEmptyRows()
Dim rngName As Range
Dim cell As Range
Application.ScreenUpdating = False
For Each cell In range("HideRows")
If cell.Value = 0 Then
cell.EntireRow.Hidden = True
End If
Next cell
Application.ScreenUpdating = True
edit:
If the workbook contains multiple identical sheets where each sheet may contain this named range you will have to loop. This code will not loop over all names but over all sheets, and test for existance of the named range in each sheet:
Sub HideEmptyRows()
Dim sh As Sheets
Dim rng As Range, cell As Range
For Each sh In ActiveWorkbook.Sheets
Set rng = Nothing ' this is crucial!
On Error Resume Next
Set rng = sh.Names("HideRows")
On Error GoTo 0
If Not rng Is Nothing Then
For Each cell In rng
cell.EntireRow.Hidden = (cell.Value = 0)
Next cell
End If
Next sh
End Sub
The range variable has to be reset explicitly before the assignment as this step is skipped if the range does not exist. The following If would use the value last assigned then, which would be wrong.

Excel Macro for creating new worksheets

I am trying to loop through some columns in a row and create new worksheets with the name of the value of the current column/row that I am in.
Sub test()
Range("R5").Select
Do Until IsEmpty(ActiveCell)
Sheets.Add.Name = ActiveCell.Value
ActiveCell.Offset(0, 1).Select
Loop
End Sub
This code creates the first one correctly starting at R5 but then it appears that the macro switches to that worksheet and doesn't complete the task.
The Sheets.Add automatically moves your selection to the newly created sheet (just like if you insert a new sheet by hand). In consequence the Offset is based on cell A1 of the new sheet which now has become your selection - you select an empty cell (as the sheet is empty) and the loop terminates.
Sub test()
Dim MyNames As Range, MyNewSheet As Range
Set MyNames = Range("R5").CurrentRegion ' load contigeous range into variable
For Each MyNewSheet In MyNames.Cells ' loop through cell children of range variable
Sheets.Add.Name = MyNewSheet.Value
Next MyNewSheet
MyNames.Worksheet.Select ' move selection to original sheet
End Sub
This will work better .... you assign the list of names to an object variable of type Range and work this off in a For Each loop. After you finish you put your Selection back to where you came from.
Sheets.Add will automatically make your new sheet the active sheet. Your best bet is to declare variables to your objects (this is always best practice) and reference them. See like I've done below:
Sub test()
Dim wks As Worksheet
Set wks = Sheets("sheet1")
With wks
Dim rng As Range
Set rng = .Range("R5")
Do Until IsEmpty(rng)
Sheets.Add.Name = rng.Value
Set rng = rng.Offset(0, 1)
Loop
End With
End Sub
Error handling should always be used when naming sheets from a list to handle
invalid characters in sheet names
sheet names that are too long
duplicate sheet names
Pls change Sheets("Title") to match the sheet name (or position) of your title sheet
The code below uses a variant array rather than a range for the sheet name for performance reasons, although turning off ScreenUpdating is likely to make the biggest difference to the user
Sub SheetAdd()
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim strError As String
Dim vArr()
Dim lngCnt As Long
Dim lngCalc As Long
Set ws1 = Sheets("Title")
vArr = ws1.Range(ws1.[r5], ws1.[r5].End(xltoRight))
If UBound(vArr) = Rows.Count - 5 Then
MsgBox "sheet range for titles appears to be empty"
Exit Sub
End If
With Application
.ScreenUpdating = False
.EnableEvents = False
lngCalc = .Calculation
End With
For lngCnt = 1 To UBound(vArr)
Set ws2 = Sheets.Add
On Error Resume Next
ws2.Name = vArr(lngCnt, 1)
If Err.Number <> 0 Then strError = strError & vArr(lngCnt, 1) & vbNewLine
On Error GoTo 0
Next lngCnt
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = lngCalc
End With
If Len(strError) > 0 Then MsgBox strError, vbCritical, "These potential sheet names were invalid"
End Sub
This is probably the simplest. No error-handling, just a one-time code to create sheets
Sub test()
Workbooks("Book1").Sheets("Sheet1").Range("A1").Activate
Do Until IsEmpty(ActiveCell)
Sheets.Add.Name = ActiveCell.Value
Workbooks("Book1").Sheets("Sheet1").Select
ActiveCell.Offset(0, 1).Select
Loop
End Sub