Remove text after a space across multiple columns in Excel using VBA - vba

I have a list of items' monthly prices and the price columns have both the amount and currencies. I am trying to remove the currencies across multiple columns.
Sample data sets:
I wrote below macro to remove text after space of cells in A1:E6:
Sub RemoveCurrency()
Dim cell As Range
Dim withCurrency As String
Dim PriceOnly As String
For Each cell In Worksheets("Sheet1").Range("A1:E6")
withCurrency = cell.Value
PriceOnly = Left(withCurrency, InStr(withCurrency, " ") - 1)
cell.Value = PriceOnly
Next cell
End Sub
After I ran it, it gives me a VB run-time error '5':
Invalid procedure call or argument
Can someone help to debug my VBA code?

Am I right, reading "to remove text after space" as "including this space"? If so:
Dim sStr$
' ...
For Each cell In Worksheets("Sheet1").Range("A1:E6")
' ...
sStr = cell.Value
If LenB(sStr) Then cell.Value = Split(sStr, " ")(0)
'or
cell.Value = Split(sStr & " ", " ")(0)
' ...
Next
' I'm not sure which is faster... but LenB seems to me more realiable

This code is much longer/less efficient than yours, but it won't give you an error when it runs out of cells to edit, at least (it's a module I had already written in the past for nearly the exact same purpose). Alternatively, yours should work (but still error at the end) if you update the range to start at the cell where your currency amount starts. You can read more about VBA ranges in the MSDN Documentation.
It requires that you add a VBA reference to "Microsoft VBScript Regular Expressions 5.5" if you don't already have it checked. You can do that in Tools -> References.
Public Sub TruncateIDs()
Dim strPattern As String: strPattern = "[A-Z]{1,3}"
Dim strReplace As String: strReplace = ""
Dim regEx As New RegExp
Dim strInput As String
Dim MyRange As Range
Set MyRange = ActiveSheet.Range("B3:E6")
ActiveSheet.Range("B3").Select
Do While strPattern <> ""
strInput = ActiveCell.Value
With regEx
.Global = True
.MultiLine = True
.IgnoreCase = False
.Pattern = strPattern
End With
If regEx.Test(strInput) Then
ActiveCell = regEx.Replace(strInput, strReplace)
ActiveCell.Offset(1, 0).Select
ElseIf ActiveCell = "" Then
ActiveCell.Offset(-4, 1).Select
If ActiveCell <> "" Then
strInput = ActiveCell.Value
ActiveCell = regEx.Replace(strInput, strReplace)
ActiveCell.Offset(1, 0).Select
Else
Exit Do
End If
Else
Exit Do
End If
Loop
End Sub

Related

VBA Code to Autofill

Have a column H with alphanumeric characters. Some cells in this column have the content (RAM) followed by 5 digits starting from 00000 to 99999.
If cell H219 has the content (RAM) 23596 then i have to fill cell A219 with a comment "completed".
This has to be done for all cells with the content "(RAM) followed by 5 digits"
Sub Macro16_B()
' ' Macro16_B Macro ' '
intRowCount = Worksheets("Reconciliation").UsedRange.Rows.Count
For i = 11 To intRowCount
If InStr(Range("H" & i).Value, "(RAM 00000-99999") Then
Range("A" & i).Value = "Completed"
End If
Next i
End Sub
A non-VBA answer could be (if the cell doesn't have extra text other than (RAM) & 5 numbers):
=IFERROR(IF(LEN(VALUE(TRIM(SUBSTITUTE(H1,"(RAM)",""))))=5,"completed",""),"")
My VBA answer would be:
Sub Test()
Dim rLastCell As Range
Dim rCell As Range
With Worksheets("Reconciliation")
Set rLastCell = .Columns(8).Find("*", , , , xlByColumns, xlPrevious)
If Not rLastCell Is Nothing Then
For Each rCell In .Range(.Cells(1, 8), rLastCell)
If rCell Like "*(RAM) #####*" Then
rCell.Offset(, -7) = "complete"
End If
Next rCell
End If
End With
End Sub
Cheers #Excelosaurus for heads up on the * would've forgotten it as well. :)
One way is to use the Like operator. The precise format of your string is not clear so you may have to amend (and assuming case insensitive). # represents a single number; the * represents zero or more characters.
Sub Macro16_B()
Dim intRowCount As Long, i As Long
' ' Macro16_B Macro ' '
intRowCount = Worksheets("Reconciliation").UsedRange.Rows.Count
For i = 11 To intRowCount
If Range("H" & i).Value Like "(RAM) #####*" Then
Range("A" & i).Value = "Completed"
End If
Next i
End Sub
Well, there are already 2 good answers, but allow me to paste my code here for good measure, the goal being to submerge #user2574 with code that can be re-used in his/her next endeavors:
Sub Macro16_B()
'In the search spec below, * stands for anything, and # for a digit.
'Remove the * characters if you expect the content to be limited to "(RAM #####)" only.
Const SEARCH_SPEC As String = "*(RAM #####)*"
Dim bScreenUpdating As Boolean
Dim bEnableEvents As Boolean
'Keep track of some settings.
bScreenUpdating = Application.ScreenUpdating
bEnableEvents = Application.EnableEvents
On Error GoTo errHandler
'Prevent Excel from updating the screen in real-time,
'and disable events to prevent unwanted side effects.
Application.ScreenUpdating = False
Application.EnableEvents = False
'Down with business...
Dim scanRange As Excel.Range
Dim cell As Excel.Range
Dim content As String
Dim ramOffset As Long
With ThisWorkbook.Worksheets("Reconciliation").Columns("H")
Set scanRange = .Worksheet.Range(.Cells(11), .Cells(.Cells.Count).End(xlUp))
End With
For Each cell In scanRange
content = CStr(cell.Value2)
If content Like SEARCH_SPEC Then
cell.EntireRow.Columns("A").Value = "Completed"
End If
Next
Recover:
On Error Resume Next
'Restore the settings as they were upon entering this sub.
Application.ScreenUpdating = bScreenUpdating
Application.EnableEvents = bEnableEvents
Exit Sub
errHandler:
MsgBox Err.Description, vbExclamation + vbOKOnly, "Error"
Resume Recover
End Sub

Merge Cells with Duplicate Data VBA

I'm trying to get a Macro working to merge cells with duplicate data. It will work on small numbers of cells, but I get the following error if I try to run it on a larger group of cells. I'm not sure if there's a more efficient way for excel to run through this.
Run-Time error '1004':
Method 'Range' of object '_Global' failed
Here's the code:
Sub MergeDuplicates()
Dim varData As Variant, varContent As Variant
Dim strMyRange As String
Application.ScreenUpdating = False
Application.DisplayAlerts = False
strMyRange = ActiveCell.Address
varContent = ActiveCell.Value
For Each varData In Selection.Cells
If varData.Value <> varContent Then
strMyRange = strMyRange & ":" & Cells(varData.Row - 1, varData.Column).Address & ", " & Cells(varData.Row, varData.Column).Address
varContent = Cells(varData.Row, varData.Column).Value
End If
Next
strMyRange = strMyRange & Mid(Selection.Address, InStr(1, Selection.Address, ":"), Len(Selection.Address))
Range(strMyRange).Merge
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
I have recreated the issue using the code you posted and it is working for me. I did what you suggested and put the merge into the For loop. Then I split strMyRange using the comma as the delimiter. I set up a test to look for the ":" character in TestArray(0). If it is in that target string, then I know it is ready for the merge. After that I reset strMyRange to the TestArray(1) which is the beginning of the next range.
Note: I was able to step through it with the debugger with 100 cells and it worked. Then I tried running it without any code breakpoints, but it merged all the selected cells. I put a 1 second wait statement right before the final merge and that seems to work.
Here is the code:
Sub MergeDuplicates()
Dim varData As Variant, varContent As Variant
Dim strMyRange As String
Dim TestArray() As String
Dim target As String
Dim pos As Integer
Application.ScreenUpdating = False
Application.DisplayAlerts = False
strMyRange = ActiveCell.Address
varContent = ActiveCell.Value
For Each varData In Selection.Cells
If varData.Value <> varContent Then
strMyRange = strMyRange & ":" & Cells(varData.Row - 1, varData.Column).Address & ", " & Cells(varData.Row, varData.Column).Address
TestArray = Split(strMyRange, ",")
target = TestArray(0)
pos = InStr(target, ":")
If (pos > 0) Then
Range(target).Merge
strMyRange = TestArray(1)
End If
varContent = Cells(varData.Row, varData.Column).Value
End If
Next
strMyRange = strMyRange & Mid(Selection.Address, InStr(1, Selection.Address, ":"), Len(Selection.Address))
Application.Wait (Now + #12:00:01 AM#) 'This helps the application run OK if there are no breakpoints.
Range(strMyRange).Merge
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub

Excel Automatically Numbering Sheets that Have the Same Name

hope everyone is doing good! I have a quick question concerning excel and sheet names. Currently I have a macro assigned to a forum control button on my template invoice page that when clicked will make a duplicate sheet named after cell E7 (the project name) + the word "Invoice" at the end of it. But since there will be more than one invoice per project, I would like the macro to include some code where if it finds a duplicate name, it will automatically start numbering them from two. So for example, I use the macro to create "Project A Invoice". If I use it again to create another one, I want it to be named "Project A Invoice(2)" automatically instead of giving me an error message. Here's what I have so far:
Sub invoice_export_test()
Dim sName As String
Dim wks As Worksheet
Worksheets("Invoice").Copy after:=Sheets(Worksheets.Count)
Set wks = ActiveSheet
Do While sName <> wks.Name
sName = Range("E7") + " Invoice"
wks.Name = sName
On Error GoTo 0
Loop
Range("C7:D7").Select
With Selection.Validation
.Delete
.Add Type:=xlValidateInputOnly, AlertStyle:=xlValidAlertStop, Operator _
:=xlBetween
.IgnoreBlank = True
.InCellDropdown = True
.IMEMode = xlIMEModeNoControl
.ShowInput = True
.ShowError = True
End With
ActiveSheet.Shapes.Range(Array("Button 1")).Select
Selection.Delete
Set wks = Nothing
End Sub
Everything after the loop is irrelevant to this question (its there to delete the forum control button and data validation), but I included it just in case. The code is probably messy since I am not too experienced with VBA and used a few tutorials to work this out, so please forgive me for that haha.
Thank you in advanced for your time/help!
Create a couple functions to help yourself out:
Function GetUniqueName(strProject As String) As String
' If this is the first time it's being used, just return it without a number...
If Not SheetNameExists(strProject & " Invoice") Then
GetUniqueName = strProject & " Invoice"
Exit Function
End If
' Otherwise, suffix the sheet name with a number, starting at 2...
Dim i As Long, strName As String
i = 1
Do
i = i + 1
strName = strProject & " Invoice (" & i & ")"
Loop While SheetNameExists(strName)
GetUniqueName = strName
End Function
Function SheetNameExists(strName As String) As Boolean
Dim sh As Worksheet
For Each sh In Worksheets
If StrComp(sh.Name, strName, vbTextCompare) = 0 Then
SheetNameExists = True
Exit Function
End If
Next
End Function
Then you can change your code from:
Dim sName As String
Dim wks As Worksheet
Worksheets("Invoice").Copy after:=Sheets(Worksheets.Count)
Set wks = ActiveSheet
Do While sName <> wks.Name
sName = Range("E7") + " Invoice"
wks.Name = sName
On Error GoTo 0
Loop
To:
Worksheets("Invoice").Copy after:=Sheets(Worksheets.Count)
Worksheets(Worksheets.Count).Name = GetUniqueName(Range("E7"))

VBA Worksheet Data extraction to search for multiple values

I am tasked with pulling two specific rows of data from monthly sheets in a workbook.
Current code, using MyVal and a search box, is only compatible with one search. How can I change the code & searchbox function to be compatible with multiple searches?
Current code looks like this:
Sub Set_Hyper()
' Object variables
Dim wks As Excel.Worksheet
Dim rCell As Excel.Range
Dim fFirst As String
' {i} will act as our counter
Dim i As Long
' Use an input box to type in the search criteria
Dim MyVal As String
MyVal = InputBox("What are you searching for", "Search-Box", "")
' if we don't have anything entered, then exit the procedure
If MyVal = "" Then Exit Sub
Application.ScreenUpdating = False
Application.DisplayAlerts = False
' Add a heading to the sheet with the specified search value
With Cells(1, 1)
.Value = "Found " & MyVal & " in the Link below:"
.EntireColumn.AutoFit
.HorizontalAlignment = xlCenter
End With
i = 2
' Begin looping:
' We are checking all the Worksheets in the Workbook
For Each wks In ActiveWorkbook.Worksheets
If wks.Name <> "Data" Then
' We are checking all cells, we don't need the SpecialCells method
' the Find method is fast enough
With wks.Range("A:A")
' Using the find method is faster:
' Here we are checking column "A" that only have {myVal} explicitly
Set rCell = .Find(MyVal, , , xlWhole, xlByColumns, xlNext, False)
' If something is found, then we keep going
If Not rCell Is Nothing Then
' Store the first address
fFirst = rCell.Address
Do
' Link to each cell with an occurence of {MyVal}
rCell.Hyperlinks.Add Cells(i, 1), "", "'" & wks.Name & "'!" & rCell.Address
wks.Range("B" & rCell.Row & ":R" & rCell.Row).Copy Destination:=Cells(i, 2)
Set rCell = .FindNext(rCell)
i = i + 1 'Increment our counter
Loop While Not rCell Is Nothing And rCell.Address <> fFirst
End If
End With
End If
Next wks
' Explicitly clear memory
Set rCell = Nothing
' If no matches were found, let the user know
If i = 2 Then
MsgBox "The value {" & MyVal & "} was not found on any sheet", 64, "No Matches"
Cells(1, 1).Value = ""
End If
' Reset application settings
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
I'm thinking what you could do is create a UserForm with the following controls:
A text box
A Listbox
A button to add text to the listbox
Another button to run the VBA
The Textbox can hold the search string(s). You can make an event when you click the button to do the following:
1) Add the text from textbox to the listbox. Lookup the AddItem method to do this.
2) Clear the text box contents, so a new value can be added.
Once that's added you can add another for loop around your code to go through each item added to the listbox. That way you can do multiple searches based on what was added.
Hopefully this helps :)

Need to copy certain data over workbooks

new to VBA here. I've been stuck on this problem for a while now:
Essentially, I need to create a macro that copies over specific data from one sheet to another, that is up to the user to specify. The catch is that while all the data is in one column (B), not all rows of the column have relevant entries; some are blank and some have other data that I don't want.
Only entries that begin with 4 numbers are wanted. I can't seem to get how the iterated copy-pasting works; what I've come up with is as follows:
'defining input
Dim dater As Date
dater = Range("B2")
If dater = False Then
MsgBox "Date not specified"
Exit Sub
End If
Dim sheetin As String
sheetin = Range("B5")
If sheetin = "" Then
MsgBox "Input Sheet not specified"
Exit Sub
End If
Dim wbin As String
wbin = Range("B4")
If wbin = "" Then
MsgBox "Input workbook not specified"
Exit Sub
End If
Dim sheetout As String
sheetout = Range("B9")
If sheetout = "" Then
MsgBox "Output Sheet not specified"
Exit Sub
End If
Dim wbout As String
wbout = Range("B8")
If wbout = "" Then
MsgBox "Output Workbook not specified"
Exit Sub
End If
Windows(wbout).Activate
Dim sh As Worksheet, existx As Boolean
For Each sh In Worksheets
If sh.Name Like sheetout Then existx = True: Exit For
Next
If existx = True Then
If Sheets(sheetout).Visible = False Then Sheets(sheetout).Visible = True
Else
Sheets.Add.Name = CStr(sheetout)
End If
'copy pasting values
Windows(wbin).Activate
Sheets(sheetin).Select
'specify maximum row
iMaxRow = 500
For iRow = 1 To iMaxRow
With Worksheets(sheetin).Cells(iRow, 2)
'Check that cell is not empty.
If .Value = "####*" Then
.Copy Destination:=Workbooks(wbout).Worksheets(sheetout).Range("A" & i)
'Else do nothing.
End If
End With
Next iRow
End Sub
Subsequently i'll have to match data to these entries that have been copied over but I figure once i get the hang of how to do iterated stuff it shouldn't be too much of a problem. But right now i'm really stuck... Please help!
It looks like it should work, except for that part :
With Worksheets(sheetin).Cells(iRow, 2)
If .Value = "####*" Then
.Copy Destination:=Workbooks(wbout).Worksheets(sheetout).Range("A" & i)
End If
End With
The third line contains an unknown variable : i.
You need to define it to contain the number of the line to which you're copying. For example, if you want to copy to the first available line, try this :
Set wsOut = Workbooks(wbout).Worksheets(sheetout)
With Worksheets(sheetin).Cells(iRow, 2)
If .Value = "####*" Then
i = wsOut.Cells(wsOut.Rows.Count, 1).End(xlUp).Row + 1
.Copy Destination:=wsOut.Range("A" & i)
End If
End With