I am new to VBA in Excel. I have a spreadsheet that always has 172 columns. A:FP. It may have 2 to many rows. For every cell in the spreadsheet, I want to remove all leading and trailing spaces along with any leading or trailing "/" or "\". A string can contain a back or forward slash, just not at the beginning or end of the string. My code removes leading and trailing blank spaces. It removes trailing back or forward slashes. However, it does not remove leading slashes. I cannot understand why. Is there a better way of accomplishing my goal? Thank you for your help.
For Each Rng In ActiveSheet.UsedRange
CellVal = Trim(Rng.Value)
LengT = Trim(Len(CellVal))
If CellVal <> "" Then
If Not Rng.HasFormula Then
While Trim(Left(CellVal, 1) = Chr(47)) Or Trim(Left(CellVal, 1) = Chr(92)) Or Trim(Right(CellVal, 1) = Chr(47)) Or Trim(Right(CellVal, 1) = Chr(92))
If Trim(Left(CellVal, 1) = Chr(47)) Or Trim(Left(CellVal, 1) = Chr(92)) Then
CellVal = Trim(Mid(CellVal, 2, LengT))
ElseIf Trim(Right(CellVal, 1) = Chr(47)) Or Trim(Right(CellVal, 1) = Chr(92)) Then
LengT = Len(CellVal)
CellVal = Trim(Left(CellVal, LengT - 1))
Rng.Value = CellVal
LengT = LengT - 1
End If
Wend
End If
End If
Next Rng
First, I'd say try to avoid UsedRange but find a way to get your last used row and column. Then, instead of making a ton of calls to the Worksheet object, I'd run over your values in memory through the use of an array.
Last, you can do many replacements etc. But maybe using a regular expression isn't out of place here. Just for inspirations, I'll put down the following:
Sub Test()
Dim ws As Worksheet: Set ws = ThisWorkbook.Worksheets("Sheet1")
Dim lr As Long, lc As Long, x As Long, y As Long
Dim arr As Variant
Dim RegEx As Object: Set RegEx = CreateObject("vbscript.regexp")
'Set up regular expression first
RegEx.Global = True
RegEx.Pattern = "^[ \/\\]*(.*?)[ \/\\]*$"
With ws
lr = .Cells(.Rows.Count, 1).End(xlUp).Row
lc = .Cells(1, .Columns.Count).End(xlToLeft).Column
arr = .Range(.Cells(1, 1), .Cells(lr, lc)).Value
For x = LBound(arr, 1) To UBound(arr, 1)
For y = LBound(arr, 2) To UBound(arr, 2)
If RegEx.Test(arr(x, y)) Then arr(x, y) = RegEx.Execute(arr(x, y))(0).submatches(0)
Next
Next
.Range(.Cells(1, 1), .Cells(lr, lc)).Value = arr
End With
End Sub
So in the end it's reading back the cleaned data to the worksheet.
Regex Demo
Related
I have few values in column I and column H, i have a code which highlights specific words in H column if those words are exactly present in I column.
Drawback is it highlights the works only if they are exactly ditto and are present together, Can any changes be made in the code and make highlight each word even if they are not together
attaching a image of what i want vs what i have, also attaching the existing code.
Dim c1 As Range, c2 As Range, md As Variant, i As Long, w1 As String, os As Long
Set c1 = Range("I2")
Set c2 = Range("H2")
md = Range(c1, Cells(Rows.Count, c1.Column).End(xlUp)).Value
For i = 1 To UBound(md)
If md(i, 1) <> "" Then
w1 = c2.Cells(i, 1).Value
os = InStr(1, w1, md(i, 1), vbTextCompare)
While os > 0
c2.Cells(i, 1).Characters(Start:=os, Length:=Len(md(i, 1))).Font.Color = vbBlue
os = InStr(os + 1, w1, md(i, 1), vbTextCompare)
Wend
End If
Next i
It would be a great help if someone solves my problem.
For pattern matching use a Regular Expression.
Option Explicit
Sub markup()
Dim regex As Object, m As Object, ar
Dim pattern As String, s As String
Dim Lastrow As Long, i As Long, k As Long, n As Long, p As Long
' Create regular expression.
Set regex = CreateObject("VBScript.RegExp")
With regex
.IgnoreCase = True
.Global = True
End With
'update sheet
With ActiveSheet
Lastrow = .Cells(.Rows.Count, "I").End(xlUp).Row
For i = 2 To Lastrow
pattern = Replace(.Cells(i, "I"), ",", "|")
If Len(pattern) > 0 Then
regex.pattern = pattern
s = .Cells(i, "H")
If regex.test(s) Then
' markup matches
Set m = regex.Execute(s)
For k = 0 To m.Count - 1
p = m(k).firstindex + 1
n = Len(m(k))
With .Cells(i, "H").Characters(Start:=p, Length:=n)
.Font.Color = vbBlue
.Font.Bold = True
End With
Next
End If
End If
Next
End With
End Sub
I am trying to split some cells into multiple rows. I currently am trying to break the cells up into column G (see pic below and) have all the same data in the lines below, with just the cells being split into multiple rows. Is this possible to be done?
Starting here:
and finishing with this:
If macro function is ok, here is sample code
Sub Split()
Dim meargedline As String
Dim rowNumber As Integer
rowNumber = 2
Dim splitData
For i = 2 To 4
meargedline = Cells(i, 3)
splitData = VBA.Split(meargedline, Chr(10))
For j = LBound(splitData, 1) To UBound(splitData, 1)
Cells(j + rowNumber, 4) = Cells(i, 1)
Cells(j + rowNumber, 5) = Cells(i, 2)
Cells(j + rowNumber, 6) = splitData(j)
Next j
rowNumber = rowNumber + UBound(splitData, 1) + 1
Next i
This will do a basic loop through and generate an output. Note the constants and make sure you specify for your workbook. You can see an example of it here.
Sub runSplitter()
Const topRightCellAddress = "E20"
Const startCellToSetValues = "A1" 'where new rows will be placed
Const sheetOneName = "Start" 'make sure these match"
Const sheetTwoName = "Output"
Const codeOfLineSplitter = 10 'asci code line splitter
Dim firstSheet As Worksheet, secondSheet As Worksheet
Set firstSheet = Sheets(sheetOneName)
Set secondSheet = Sheets(sheetTwoName)
Dim aCell As Range
Set aCell = firstSheet.Range(topRightCellAddress)
Dim aRR() As String
Dim r As Long
Do While Not IsEmpty(aCell)
aRR = Split(aCell.Value2, Chr(codeOfLineSplitter), -1)
Dim i As Long
With secondSheet.Range(startCellToSetValues)
For i = LBound(aRR) To UBound(aRR)
.Offset(r, 0).Value2 = aCell.Offset(0, -2).Value2
.Offset(r, 1).Value2 = aCell.Offset(0, -1).Value2
.Offset(r, 2).Value2 = aRR(i)
r = r + 1
Next i
End With
Set aCell = aCell.Offset(1, 0)
Loop
End Sub
you can use power query to achieve the target you want. Click here for reference https://www.youtube.com/watch?v=wJ6y2anloW4.
Instead of receiving the first 12 character from the right, what's needed is the same cell without the first 12 characters.
Dim arrData As Variant, LastRow As Long, i As Long, ws As Worksheet
Set ws = ThisWorkbook.Sheets("Long List 15032019") 'change the name of the sheet to the one you are doing the code
With ws
LastRow = .Cells(.Rows.Count, 3).End(xlUp).Row
arrData = .Range("A2", .Cells(LastRow, "C")).Value
For i = 1 To UBound(arrData)
If arrData(i, 3) Like "Bus*" Then
arrData(i, 1) = "BU CRM"
Else
arrData(i, 1) = "CSI ACE"
End If
If arrData(i, 3) Like "CSI*" Or arrData(i, 3) = vbNullString Then
arrData(i, 2) = vbNullString
Else: arrData(i, 2) = Right(arrData(i, 3), 12)
End If
Next i
.Range("A2", .Cells(LastRow, "C")).Value = arrData
End With
i.e. If C3 = "Example (ID:15654534)" then B3 = "(ID:15654534)" or If C3 = "Example (ID:152)" then B3 = "(ID:152)"
I did attempt using Left or -12. But the outcome was not what I needed.
I hope that this clarifies my question.
Thank you
Sounds like you want a regex. In this case you would have
arrData(i,2) = GetId(arrData(i, 3) , "ID:\d+")
Code:
Option Explicit
Public Sub test()
Dim items(), item As Variant
items = Array("Example (ID:15654534)", "Example (ID:152)")
For Each item In items
Debug.Print GetId(item, "ID:\d+")
Next
End Sub
Public Function GetId(ByVal inputString As String, ByVal sPattern As String) As String
Dim matches As Object, iMatch As Object, arrMatches(), i As Long
i = 1
With CreateObject("vbscript.regexp")
.Global = True
.MultiLine = True
.IgnoreCase = True
.Pattern = sPattern
If .test(inputString) Then
Set matches = .Execute(inputString)
ReDim arrMatches(1 To matches.Count)
For Each iMatch In matches
arrMatches(i) = iMatch.Value
i = i + 1
Next iMatch
Else
GetId = "No match"
Exit Function
End If
End With
GetId = arrMatches(1)
End Function
This can be done without VBA, but only if input is the same structure. Your data now is:
text(idnumber) and you want to obtain only part (idnumber) including both parenthesis.
This formula will work ONLY as long as data structure is the same. If input changes something, it could not work properly (For example, if first parenthesis is missing, it won't work properly).
The formula I've used is this one:
=MID(C1;SEARCH("(";C1;1);SEARCH(")";C1)-SEARCH("(";C1;1)+1)
My data example:
Hope you can adap it to your needs.
I need to do the following:
I have a table where the 13th column contains strings such as
acbd,ef,xyz
qwe,rtyu,tqyuiop
And what I want to create new rows in order to separate those values:
acbd
ef
xyz
qwe
rtyu
tqyuiop
Meaning I would have now 6 rows instead of 2, and all the other information on cells would remain the same (i.e. all the other values of the row would repeat themselves through all the new rows).
What I have tried is the following:
Sub test()
Dim coma As Integer
Dim finalString As String
Set sh = ActiveSheet
For Each rw In sh.Rows
* If find a coma, then copy the row, insert a new row, and paste in this new row*
If InStr(1, sh.Cells(rw.Row, 13).Value, ",") Then
Rows(rw.Row).Copy
Rows(rw.Row).insert shift:=xlShiftDown
Rows(rw.Row).PasteSpecial xlPasteValues
* Now it will look for the position of the comma and assign
to finalString what's before the comma, and assign to mod String
what's after the comma *
coma = InStr(1, sh.Cells(rw.Row, 13).Value, ",")
finalString = Left(sh.Cells(rw.Row, 13).Value, coma - 1)
modString = Right(sh.Cells(rw.Row, 13).Value, Len(sh.Cells(rw.Row, 13).Value) - coma)
* Replace the values: *
sh.Cells(rw.Row, 13).Value = modString
sh.Cells(rw.Row - 1, 13).Value = finalString
End If
Next rw
MsgBox ("End")
End Sub
This code works perfectly well except that for tables with 400 rows it takes 15 +-5 seconds to be completed.
I would like some suggestions on how to improve the performance of this. Thank you!
With data in column L, give this a try:
Sub LongList()
Dim wf As WorksheetFunction, arr, s As String
Set wf = Application.WorksheetFunction
s = wf.TextJoin(",", True, Range("L:L"))
arr = Split(s, ",")
Range("M1").Resize(UBound(arr) + 1, 1).Value = wf.Transpose(arr)
End Sub
Note:
No looping over cells.No looping within cells. This process can be accomplished with just worksheet formulas, VBA is not needed.
Try this.
Sub test()
Dim vDB, vR(), vS, s
Dim i As Long, j As Integer, n As Long
vDB = Range("a1").CurrentRegion
For i = 1 To UBound(vDB, 1)
vS = Split(vDB(i, 13), ",")
For Each s In vS
n = n + 1
ReDim Preserve vR(1 To 13, 1 To n)
For j = 1 To 12
vR(j, n) = vDB(i, j)
Next j
vR(13, n) = s
Next s
Next i
Range("a1").Resize(n, 13) = WorksheetFunction.Transpose(vR)
End Sub
Before.
After.
If you have more columns, do like this.
Sub test()
Dim vDB, vR(), vS, s
Dim i As Long, j As Integer, n As Long
Dim c As Integer
vDB = Range("a1").CurrentRegion
c = UBound(vDB, 2)
For i = 1 To UBound(vDB, 1)
vS = Split(vDB(i, 13), ",")
For Each s In vS
n = n + 1
ReDim Preserve vR(1 To c, 1 To n)
For j = 1 To c
vR(j, n) = vDB(i, j)
Next j
vR(13, n) = s
Next s
Next i
Range("a1").Resize(n, c) = WorksheetFunction.Transpose(vR)
End Sub
If you want an immediate boost in performance without having to adjust any kind of code just add Application events at the beginning...
With Application
.DisplayAlerts = False
.ScreenUpdating = False
End With
and be sure to turn them back on at the end of the code...
With Application
.DisplayAlerts = True
.ScreenUpdating = True
End With
These two simple statements usually speed up code considerably.
This should look for comma-delimited values in column M and overwrite the values in column M with the split values (basically what your code was doing).
Option Explicit
Sub splitValues()
Dim sourceSheet As Worksheet
Set sourceSheet = ActiveSheet
With sourceSheet
Dim lastRow As Long
lastRow = .Cells(.Rows.Count, "M").End(xlUp).Row
Dim inputValues() As Variant
inputValues = .Range("M1:M" & lastRow).Value2
Dim splitString() As String
Dim rowIndex As Long
Dim outputArray As Variant
Dim outputRowIndex As Long
outputRowIndex = 1
For rowIndex = LBound(inputValues, 1) To UBound(inputValues, 1)
splitString = VBA.Strings.Split(inputValues(rowIndex, 1), ",", -1, vbBinaryCompare)
outputArray = Application.Transpose(splitString)
.Cells(outputRowIndex, "M").Resize(UBound(outputArray, 1), UBound(outputArray, 2)).Value2 = outputArray
outputRowIndex = outputRowIndex + UBound(outputArray, 1)
Next rowIndex
End With
End Sub
How do i split the first picture(whole address) into second picture(address, city, state, zipcode). I have more than thousand data, so kindly suggest me the easy method?
All the whole address is formatted as 729 quail creek drive, frisco tx 75034
I need to split Address as 729 quail creek drive, City as frisco, State as tx and zipcode as 75034.
Thanks
If you know your data will always be of this format:
<address>, <city> <2-letter state> <5-digit zip code>
Then this is the easiest way I can think of:
Formula for <address>:
= LEFT(A1,FIND(",",A1)-1)
Formula for <city>:
= MID(A1,FIND(",",A1)+2,LEN(A1)-FIND(",",A1)-10)
Formula for <2-letter state>:
= MID(A1,LEN(A1)-7,2)
Formula for <5-digit zip code>:
= RIGHT(A1,5)
See below example.
This could be done with RegExp but instead of figuring out the RegExp pattern I've used the Split function and a couple of arrays. I've assumed that the Address is before the comma.
Sub SplitAddress()
Dim Addresses As Variant, results As Variant, tmp As Variant
Dim i As Long, j As Long
' Update for you range
With Sheet1
'Trick to get 1D array from range
Addresses = Application.Transpose(.Range(.Cells(2, "A"), .Cells(.Cells(.Rows.Count, "A").End(xlUp).Row, "A")))
End With
ReDim results(1 To UBound(Addresses), 1 To 4)
For i = 1 To UBound(results, 1)
tmp = Split(Addresses(i), ",")
results(i, 1) = Trim(tmp(0))
tmp = Split(Trim(tmp(1)), " ")
For j = LBound(tmp) To UBound(tmp)
results(i, j + 2) = Trim(tmp(j))
Next j
Next i
' Update for your destination
With Sheet1.Cells(2, "B")
Range(.Offset(0, 0), .Offset(UBound(results, 1) - 1, UBound(results, 2) - 1)).Value2 = results
End With
End Sub
Updated with RegExp
This method uses RegExp to split your strings
Sub splitAddressRegEx()
Dim ReGex As Object
Dim Addresses As Range
Dim j As Long
Dim c, m
' Update for your range
With Sheet1
Set Addresses = .Range(.Cells(2, "A"), .Cells(.Cells(.Rows.Count, "A").End(xlUp).Row, "A"))
End With
Set ReGex = CreateObject("VBScript.RegExp")
With ReGex
.Global = True
.IgnoreCase = True
.Pattern = "(.+?(?=,))|(\w+)"
End With
For Each c In Addresses
j = 1
If ReGex.Test(c.Value2) Then
For Each m In ReGex.Execute(c.Value2)
' Update for your output
c.Offset(0, j).Value2 = m
j = j + 1
Next m
End If
Next c
End Sub