I have an excel spreadsheet that contains entire addresses packed in a single cell without delimiters. The addresses look like this:
2701 NW 64TH TER MARGATE FL 33063-1703
901 NE 8 ST HALLANDALE BEACH FL 33009-2626
1840 DEWEY ST UNIT 305 HOLLYWOOD FL 33020
3049 NE 4 AVE WILTON MANORS FL 33334-2047
650 NE 56 CT OAKLAND PARK FL 33334-3528
So the first five cells in column A would contain the above addresses.
As you can see, some of the cities consist of two words but the state is always FL or NY. All I need to do is separate the address, city, state, and zip in their own columns. I'm hoping there's a way to do this in VBD (Visual Basic for Developers) in excel. So I can put it into a macro.
I have an idea of how it can be done, but my VBD is limited:
stateArray = Split("FL, NY")
cityArray = Split("Fort Lauderdale","Sunrise","Oakland Park")
For example, another programming language you might do something like this:
var arrStates, arrCities
arrCities = ["Fort Lauderdale", "Sunrise", "Oakland Park"]
arrStates = ["FL", "NY"]
var findAddress = function(curCity, curState){
for(var i=0; i < arrCities.length; i < arrStates.length; i--){
(arrCities[i] == curCity) ? arrCities[i] = CurCity : arrCities[i] = null;
(arrStates[i] == curState) ? arrStates[i] = curState : arrStates[i] = null;
}
if(arrCities[i] >= 0){
var city = arrCities[i];
}
if(arrStates[i] >= 0){
var state = arrStates[i];
}
createTable(city, state);
}
var createTable = function(city, state){
var tbl = document.createElement("Table");
var newRow = document.createElement("tr");
tbl.appendChild(newRow);
cols = [city, state];
for(var i=0; i < cols.length; i++){
var newCol = document.createElement("td");
newCol.innerText = cols[i];
newRow.appendChild(newCol);
}
}
Thanks for any response.
It seems that if you have to type out all the cities, you might as well just split all the cells manually. It may be easier to identify all the street types and use that as a delimiter. Note the spaces around the strings in the array.
Sub SplitAddresses()
Dim vaStates As Variant
Dim vaStreets As Variant
Dim i As Long
Dim rCell As Range
Dim sAddress As String
Dim sCity As String, sState As String
Dim sZip As String
Dim lStreetPos As Long, lStatePos As Long
vaStates = Array(" FL ", " NY ")
vaStreets = Array(" TER ", " ST ", " AVE ", " CT ")
For Each rCell In Sheet1.Range("A1:A5").Cells
sAddress = "": sCity = "": sZip = "": sState = ""
For i = LBound(vaStreets) To UBound(vaStreets)
lStreetPos = InStr(1, rCell.Value, vaStreets(i))
If lStreetPos > 0 Then
sAddress = Trim(Left$(rCell.Value, lStreetPos + Len(vaStreets(i)) - 1))
Exit For
End If
Next i
For i = LBound(vaStates) To UBound(vaStates)
lStatePos = InStr(1, rCell.Value, vaStates(i))
If lStatePos > 0 Then
sCity = Trim(Mid$(rCell.Value, Len(sAddress) + 1, lStatePos - Len(sAddress) - 1))
sState = Trim(Mid$(rCell.Value, lStatePos + 1, Len(vaStates(i)) - 1))
sZip = Trim(Mid$(rCell.Value, lStatePos + Len(vaStates(i)), Len(rCell.Value)))
Exit For
End If
Next i
rCell.Offset(0, 1).Value = "'" & sAddress
rCell.Offset(0, 2).Value = "'" & sCity
rCell.Offset(0, 3).Value = "'" & sState
rCell.Offset(0, 4).Value = "'" & sZip
Next rCell
End Sub
In situations like this I try to forget that I'm programming, and just think about what sort of thought processes I would use to figure out what each was normally.
2701 NW 64TH TER MARGATE FL 33063-1703
901 NE 8 ST HALLANDALE BEACH FL 33009-2626
1840 DEWEY ST UNIT 305 HOLLYWOOD FL 33020
3049 NE 4 AVE WILTON MANORS FL 33334-2047
650 NE 56 CT OAKLAND PARK FL 33334-3528
At 1st things may seem hectic, but if you look closer there are patterns.
The addresses all start with a set of #s as the start of the street address
The street address always ends in a street type abbreviation
If there is a unit of the building it is after the street address
The addresses all end with the zip code
Before the zip code is the state abbreviation
The City name is smack dab in the middle for the taking if everything else is stripped away.
This makes the pattern as follows
Street # : Street Type : Unit {Optional} : City : State: Zip Code
Strip each piece off of a temporary string variable using string functions and you should be able to rebuild it fairly easily.
Hope that helps.
Here is some VBA code to get you started: you would need to add error handling
Option Explicit
Option Compare Text
Sub SplitAddress()
Dim vStates As Variant
Dim vCities As Variant
Dim vInput As Variant
Dim vAddress() As Variant
Dim j As Long
Dim str1 As String
' States/Cities/Inputs are named ranges containing the data
vStates = [States]
vCities = [Cities]
vInput = [Inputs]
ReDim vAddress(1 To UBound(vInput) - LBound(vInput) + 1, 1 To 4)
For j = 1 To UBound(vInput)
str1 = Trim(CStr(vInput(j, 1)))
If Len(str1) = 0 Then Exit For
FindSplit j, 3, str1, vStates, vAddress()
FindSplit j, 2, str1, vCities, vAddress()
Next j
ActiveSheet.Range("A2").Resize(UBound(vAddress), UBound(vAddress, 2)) = vAddress
End Sub
Sub FindSplit(j As Long, k As Long, str1 As String, vItems As Variant, vAddress() As Variant)
Dim iPos As Long
Dim jItem As Long
Dim strItem As String
For jItem = 1 To UBound(vItems)
strItem = Trim(CStr(vItems(jItem, 1)))
iPos = InStr(str1, " " & strItem & " ")
If iPos > 0 Then
vAddress(j, k) = Mid(str1, iPos + 1, Len(strItem))
If k = 3 Then
vAddress(j, k + 1) = Right(str1, Len(str1) - (iPos + 3))
str1 = Left(str1, iPos)
Else
vAddress(j, 1) = Left(str1, iPos - 1)
End If
Exit For
End If
Next jItem
End Sub
Related
In VBA I've made an userform. It contains multiple text boxes in which the user can write text. In one text box the user is supposed to type in his last name. I've made a variable called lastname and then did lastname = LastnameBox.Value.
My question is:
If someone for example types de Vries, how can I change this in Vries, de. Or if someone types van de Voort van Zijp, I need to change this in Voort van Zijp, van de.
How could I make this possible in VBA?
I would try something along these lines. Not sure how you are requiring separation, I used "de" as this
Function NamesTest(strNameIn As String)
Dim a() As String
a = Split(strNameIn, "de")
a(0) = a(0) & " de"
NamesTest = a(1) & "," & a(0)
End Function
Here are two options. The first will pick up the last word and do the swap. It pays no attention to letter case.
Sub LastFirst()
Debug.Print RevLast("de Vries")
Debug.Print RevLast("van der Straat")
Debug.Print RevLast("van de drake")
End Sub
Function RevLast(Name)
LastName = Trim(Right(Replace(Name, " ", String(99, " ")), 99))
LenLastName = Len(LastName)
FirstPart = Left(Name, Len(Name) - (LenLastName + 1))
RevLast = LastName + ", " + FirstPart
End Function
The second only swaps of there is an uppercase letter.
Sub UppercaseFirst()
Name = "de Vries"
Name = "van der Straat"
Debug.Print RevUpper("de Vries")
Debug.Print RevUpper("van der Straat")
Debug.Print RevUpper("van de drake")
End Sub
Function RevUpper(Name)
FirstUpper = -1
On Error Resume Next
xStr = Trim(Rg.Value)
For j = Len(Name) To 1 Step -1
If (Asc(Mid(Name, j, 1)) < 91) And (Asc(Mid(Name, j, 1)) > 64) Then
FirstUpper = Len(Name) - j + 1
Exit For
End If
Next
If FirstUpper > 0 Then
LastName = Right(Name, FirstUpper)
FirstPart = Left(Name, Len(Name) - (FirstUpper + 1))
NewName = LastName + ", " + FirstPart
RevUpper = NewName
Else
RevUpper = "Invalid"
End If
End Function
Function RevNm(Name)
FirstUpper = -1
On Error Resume Next
xStr = Trim(Rg.Value)
For j = Len(Name) To 1 Step -1
If (Asc(Mid(Name, j, 1)) < 91) And (Asc(Mid(Name, j, 1)) > 64) Then
FirstUpper = Len(Name) - j + 1
Exit For
End If
Next
If FirstUpper > 0 Then
LastName = Right(Name, FirstUpper)
FirstPart = Left(Name, Len(Name) - (FirstUpper + 1))
NewName = LastName + ", " + FirstPart
RevNm = NewName
Else
RevNm = "Invalid"
End If
End Function
Here's a more general-purpose solution for the problem stated in the title (won't handle the specifics of inverting first name / last name, which is a different problem):
Public Function ReverseWords(ByVal value As String) As String
Dim words As Variant
words = VBA.Strings.Split(value, " ")
Dim result As String, i As Long
For i = LBound(words) To UBound(words)
result = words(i) & " " & result
Next
ReverseWords = result
End Function
Usage:
Debug.Print ReverseWords("the quick brown fox jumps over the lazy dog")
Outputs:
dog lazy the over jumps fox brown quick the
To the OP though, this isn't about inverting the words in a string at all. The solution is to parse the given string.
The first capital letter is indeed where I want to swap
So you need to find the index of the first capital letter in the input string, then extract the first & last name, trim them, then concatenate them.
This works:
Public Function ReverseFullName(ByVal value As String) As String
Dim firstCapitalIndex As Long, i As Long
For i = 1 To Len(value)
If IsCapitalLetter(Mid$(value, i, 1)) Then
firstCapitalIndex = i
Exit For
End If
Next
If i = 1 Then
'already shaped as needed
ReverseFullName = value
Exit Function
End If
Dim firstName As String
firstName = Trim$(Left$(value, firstCapitalIndex - 1))
Dim lastName As String
lastName = Trim$(Mid$(value, firstCapitalIndex))
ReverseFullName = lastName & ", " & firstName
End Function
Private Function IsCapitalLetter(ByVal value As String) As Boolean
Dim asciiCode As Integer
asciiCode = Asc(value)
IsCapitalLetter = asciiCode >= Asc("A") And asciiCode <= Asc("Z")
End Function
Usage:
Debug.Print ReverseFullName("van de Voort van Zijp")
Debug.Print ReverseFullName("de Vries")
Debug.Print ReverseFullName("Voort van Zijp, van de")
Outputs:
Voort van Zijp, van de
Vries, de
Voort van Zijp, van de
I would like some direction/help on how to code a VBA-coded solution for my scenario, details follow. I am very comfortable with VBA coding - I am really looking for advise on how to approach the problem, not any specific solution.
My department bears the highly-enviable task of daily label-making. We receive a spreadsheet from Production that has a cell/cells of serial numbers to be printed (examples below). The numbers are often not contiguous, but the basic (human-generated) 'format' is the same (hyphens for ranges, commas for single numbers). The serial numbers in the example below are 6 digits, but often are different lengths, adding to the complexity. I am looking for feedback on how to ultimately parse the cell.text into a complete list of serial numbers that can be ultimately used as a source for our label printer's software.
Again, I think I have the ability to actually code this; I am asking how to approach parsing the cell.value(s), identifying spaces, commas, and hyphens as needed, and retrieving a list of serial numbers, in any usable format. I can SPLIT at commas, and I can code the range before and after a hyphen. How do I approach the 6 digit format, as well as the change to the first three characters (364-365, could be many).
EXAMPLE SPREADSHEET CELL.VALUE: "364701-703, 705, 706, 708-710, 365100-104, 121" is a request for 14 labels:
EXPECTED PARSED RESULT: 364701, 364702, 36703, 364705, 364706, 364708, 364709, 364710, 365100, 365101, 365102, 365013, 350104, 365121
It's just a matter of how you keep track of things.
Given your data, the following will output what you want. You will note I added a single serial number item as you only had ranges listed in your sample:
Option Explicit
Sub labelMaker()
Const sRequest As String = "364701-703, 705, 706, 708-710,364800, 365100-104, 121"
Dim V, W, X
Dim lFirstThree As Long, I As Long, J As Long
'Dim D As Dictionary 'early binding
Dim D As Object 'late binding
'Set D = New Dictionary 'early binding
Set D = CreateObject("Scripting.Dictionary") 'late binding
V = Split(Replace(sRequest, " ", ""), ",")
For Each W In V
X = Split(W, "-")
If Len(X(0)) = 6 Then lFirstThree = Left(X(0), 3) 'we start a new series
For I = Right(X(LBound(X)), 3) To Right(X(UBound(X)), 3)
D.Add lFirstThree & I, lFirstThree & I
Next I
Next W
'write the results to the worksheet
V = WorksheetFunction.Transpose(D.Keys)
With Cells(1, 1).Resize(D.Count) 'will be on active sheet
.EntireColumn.Clear
.Value = V
End With
End Sub
The above works only with six digit serial numbers, which is what you provided. I'm reasonably sure the variability can be coded for, but without knowing how they vary (which is the fixed part and which the variable part), it would be hard to provide a one-size fits all solution.
You might code it however you would and post that to https://codereview.stackexchange.com/ and then you could see how some other people might approach it.
I don't have any illuminating advice, so I'll just show you how I'd do it. The splitting is easy enough and you just have to keep track of the first three numbers for when they're missing.
Public Sub GenerateSerialNumbers(ByVal sNumbers As String)
Dim vaComma As Variant, vaHyph As Variant
Dim i As Long, j As Long
Dim lPrefix As Long, lStart As Long, lEnd As Long
Dim sInput As String
Dim dc As Scripting.Dictionary
Set dc = New Scripting.Dictionary
vaComma = Split(sNumbers, ",")
For i = LBound(vaComma) To UBound(vaComma)
sInput = Trim$(vaComma(i))
If InStr(1, sInput, "-") > 0 Then
vaHyph = Split(sInput, "-")
'If you get a full one, keep the first three
If Len(vaHyph(0)) = 6 Then lPrefix = Val(Left$(sInput, 3)) * 1000
'Add the prefix if needed
lStart = Val(vaHyph(0))
If lStart < 1000 Then lStart = lPrefix + lStart
lEnd = Val(vaHyph(1))
If lEnd < 1000 Then lEnd = lPrefix + lEnd
Else
If Len(sInput) = 6 Then lPrefix = Val(Left$(sInput, 3)) * 1000
lStart = Val(sInput)
If lStart < 1000 Then lStart = lPrefix + lStart
lEnd = lStart
End If
'Generate the list
For j = lStart To lEnd
dc.Add j, j
Next j
Next i
Sheet1.Range("a1").Resize(dc.Count, 1).Value = Application.Transpose(dc.Items)
End Sub
try this:
Function trlMyString(myString As String) As String
On Error GoTo trlMyStringError
Dim i As Integer
Dim j As Integer
Dim helpArray() As String
Dim strg As String
Dim label1 As String
Dim label2 As String
strg = ""
helpArray() = Split(myString, ", ")
For i = LBound(helpArray) To UBound(helpArray)
If Len(helpArray(i)) > 3 And InStr(1, helpArray(i), "-") <> 4 Then
label1 = Left$(helpArray(i), 3)
helpArray(i) = Right$(helpArray(i), Len(helpArray(i)) - 3)
End If
If InStr(1, helpArray(i), "-") > 0 Then
For j = CInt(Left$(helpArray(i), 3)) To CInt(Right$(helpArray(i), 3))
'Debug.Print CInt(Left$(helpArray(i), 3)), CInt(Right$(helpArray(i), 3))
label2 = Trim$(Str$(j))
strg = strg & label1 & label2 & ", "
Next j
Else
label2 = helpArray(i)
strg = strg & label1 & label2 & ", "
End If
Next i
'Debug.Print strg
trlMyStringExit:
trlMyString = strg
Exit Function
trlMyStringError:
Resume trlMyStringExit
End Function
I have sales report from e-shop and need to calculate cost of goods for each order line. Order line can look like one of these:
2x Lavazza Crema e Aroma 1kg - 1x Lavazza Dolce Caffe Crema 1kg
1x Lavazza Vending Aroma Top 1kg - 1x Arcaffe Roma 1Kg - 1x Kimbo - 100% Arabica Top Flavour
So, what I need Excel to do is to take each product, find its cost with vlookup function from another sheet and then multiply it with amount ordered. The issue is that nr of products ordered can vary from 1 to 10+.
I tried to calculate it with VBA, but the code is not working (I didn´t use multiplying at the moment, I know)
Maybe it is possible to solve this problem with excel formulas?
Function GoodsCost(str, Optional strDelim As String = " ")
larray = Split(str, strDelim)
Set lookup_range = Worksheets("Products").Range("B:E")
For i = LBound(larray) To UBound(larray)
skuarray = Split(larray(i), "x ")
skucost = Application.WorksheetFunction.VLookup(UBound(skuarray), lookup_range, 4, False)
cost = cost + skucost
Next i
GoodsCost = cost
End Function
Well, it seems like now the problem is solved. Of course, it works only if make an assumption that dashes(-) are not present in product descriptions. But it can be set up in product list. The other opportunity is to use another delimeter (for example "/"). We can use Ctrl+F to find all combinations like "x -" and replace them with "x /")
Function GoodsCost(str)
Dim answer As Double
Set Products = Worksheets("Products").Range("B:E")
larray = Split(str, " - ")
For i = LBound(larray) To UBound(larray)
sku = Split(larray(i), "x ")
Price = Application.WorksheetFunction.VLookup(sku(1), Products, 4, False) * sku(0)
answer = answer + Price
Next i
GoodsCost = answer
End Function
Below you find a UDF (User Defined Function) which you can use in your worksheet. After installing it in a standard code module (VBE names these like "Module1") you can call it from the worksheet like =CostOfGoods($A2) where A2 is the cell containing and order line as you have described.
Option Explicit
Function CostOfGoods(Cell As Range) As Single
' 15 Jan 2018
Const Delim As String = " - "
Dim Fun As Single ' function return value
Dim Sale As Variant
Dim Sp() As String
Dim i As Long
Dim PriceList As Range
Dim Qty As Single, Price As Single
Dim n As Integer
Sale = Trim(Cell.Value)
If Len(Sale) Then
Sp = Split(Sale, Delim)
Do While i <= UBound(Sp)
If InStr(Sp(i), "x ") = 0 Then
If Not ConcatSale(Sp, i, Delim) Then Exit Do
End If
i = i + 1
Loop
With Worksheets("Products")
i = .Cells(.Rows.Count, "B").End(xlUp).Row
' price list starts in row 2 (change as required)
Set PriceList = Range(.Cells(2, "B"), .Cells(i, "E"))
End With
For i = 0 To UBound(Sp)
Qty = Val(Sp(i))
n = InStr(Sp(i), " ")
Sp(i) = Trim(Mid(Sp(i), n))
On Error Resume Next
Price = Application.VLookup(Sp(i), PriceList, 4, False)
If Err Then
MsgBox "I couldn't find the price for" & vbCr & _
Sp(i) & "." & vbCr & _
"The total cost calculated excludes this item.", _
vbInformation, "Price not found"
Price = 0
End If
Fun = Fun + (Qty * Price)
Next i
End If
CostOfGoods = Fun
End Function
Private Function ConcatSale(Sale() As String, _
i As Long, _
Delim As String) As Boolean
' 15 Jan 2018
Dim Fun As Boolean ' function return value
Dim x As Long, f As Long
x = UBound(Sale)
If (i > 0) And (i <= x) Then
i = i - 1
Sale(i) = Sale(i) & Delim & Sale(i + 1)
For f = i + 1 To x - 1
Sale(f) = Sale(f + 1)
Next f
Fun = True
End If
If Fun Then ReDim Preserve Sale(x - 1)
ConcatSale = Fun
End Function
I have tested this and it works with dashes in product description:
Function GoodsCost(str, Optional strDelim As String = " ")
larray = Split(str, " ")
'split the cell contents by space
Set lookup_range = Worksheets("Products").Range("B:E")
'set lookup range
For i = LBound(larray) To UBound(larray) 'loop through array
nextproduct:
LPosition = InStr(larray(i), "x") 'find multiplier "x" in string
If LPosition = Len(larray(i)) Then 'if the last character is x
If Product <> "" Then GoTo lookitup 'lookup product
Quantity = larray(i) 'get quantity
Else
Product = Product & " " & larray(i) 'concatenate array until we get a full product description to lookup with
End If
Next i
lookitup:
If Right(Product, 2) = " -" Then Product = Left(Product, Len(Product) - 2)
If Left(Product, 1) = " " Then Product = Right(Product, Len(Product) - 1)
'above trim the Product description to remove unwanted spaces or dashes
cost = Application.WorksheetFunction.VLookup(Product, lookup_range, 4, False)
Quantity = Replace(Quantity, "x", "")
GoodsCost = cost * Quantity
MsgBox Product & " # Cost: " & GoodsCost
Product = ""
If i < UBound(larray) Then GoTo nextproduct
End Function
I'd use Regular Expressions to solve this. First it finds in the string were the 'delimiters' are by replacing the - with ; detecting only - that are next to a number followed by an x (i.e. a multiplier so ignoring - in product names). It then splits each of these results into a quantity and the product (again using RegEx). It then finds the product in your data and returns the cost of goods. If there is an error, or the product isn't in your data it returns a #Value error to show that there is an issue.
Public Function GoodsCost(str As String) As Double
Dim lookup_range As Range, ProductMatch As Range
Dim v, Match
Dim qty As Long
Dim prod As String
Dim tmp() As String
On Error GoTo err
Set lookup_range = Worksheets("Products").Range("B:E")
With CreateObject("vbscript.regexp")
.Global = True
.ignorecase = True
.pattern = "(\s\-\s)(?=[0-9]+x)"
If .test(str) Then
tmp = Split(.Replace(str, ";"), ";")
Else
ReDim tmp(0)
tmp(0) = str
End If
.pattern = "(?:([0-9]+)x\s(.+))"
For Each v In tmp
If .test(v) Then
Set Match = .Execute(v)
qty = Match.Item(0).submatches.Item(0)
prod = Trim(Match.Item(0).submatches.Item(1))
Set ProductMatch = lookup_range.Columns(1).Find(prod)
If Not ProductMatch Is Nothing Then
GoodsCost = GoodsCost + (qty * ProductMatch.Offset(0, 3))
Else
GoodsCost = CVErr(xlErrValue)
End If
End If
Next v
End With
Exit Function
err:
GoodsCost = CVErr(xlErrValue)
End Function
I have a single cell that is including all historical updates, each update displays a date/time stamp and then the user's name before their notes. I need to extract all the date/time/name stamps to total their occurrences. +EDIT+ I need to get the name and date portion from each stamp so that i am able to chart the information in a pivot table. Output of something like; "3/3/2016 Rachel Boyers; 3/2/2016 Rachel Boyers; 3/2/2016 James Dorty"
EX:
"3/3/2016 9:28:36 AM Rachel Boyers: EEHAW! Terri replied!!! Hello Rachel,
I cannot find a match using the 4232A or the 12319 part number. 3/2/2016 7:39:06 AM Rachel Boyers: Sent EM to Terri - Eng per EM reply. 3/2/2016 7:35:06 AM James Dorty: 2/29/16 sent another EM to Kim. Received Auto response as follows: Thank you for your mail. Kim 12/7/2015 12:26:25 PM Frank De La Torre: Again VM - pushing FU out until after the holidays.
Edited based on added information
Edit (5/16/2016): I made some changes to the code, as you'll find below. One change, based on the new information, allows you to use the JoinArrayWithSemiColons function as either a standard worksheet function, or as function to be used in a module. So, what does this mean? It means that (assuming your cell to parse is A1), in cell B1 you can write a function like =JoinArrayWithSemiColons(A1) just like you'd write a normal worksheet function. However, if you'd still like to perform the action over a range of cells using VBA, you can run a procedure like TestFunction() as found in the code posted below. Also note, the ExtractDateTimeUsers function doesn't necessarily ever need to be called directly by the user because it's now being used exclusively as a helper function for the JoinArray... function.
Let me know if this helps to clear things up a bit.
Old Post
You can accomplish this using some Regular Expressions. See the code below for an example. In my case, I have a function to return a multidimensional array of results. In my test procedure, I call this function, then assign the results to an EMPTY matrix of cells (in your test case, you will have to determine where to put it). You do NOT have to assign the result to a group of cells, but rather you can do whatever you want with the array.
Private Function ExtractDateTimeUsers(nInput As String) As Variant()
Dim oReg As Object
Dim aOutput() As Variant
Dim nMatchCount As Integer
Dim i As Integer
Dim vMatches As Object
Set oReg = CreateObject("VBScript.RegExp")
With oReg
.MultiLine = False
.Global = True
.Pattern = "([0-9]{1,2}/[0-9]{1,2}/[0-9]{2,4}) ([0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2} [AP]M) (.*?):"
End With
If oReg.Test(nInput) Then
Set vMatches = oReg.Execute(nInput)
nMatchCount = vMatches.Count
ReDim aOutput(0 To nMatchCount - 1, 0 To 2)
For i = 0 To nMatchCount - 1
aOutput(i, 0) = vMatches(i).Submatches(0)
aOutput(i, 1) = vMatches(i).Submatches(1)
aOutput(i, 2) = vMatches(i).Submatches(2)
Next i
Else
ReDim aOutput(0 To 0, 0 To 0)
aOutput(0, 0) = "No Matches"
End If
ExtractDateTimeUsers = aOutput
End Function
Function JoinArrayWithSemiColons(sInput As String) As String
Dim vArr As Variant
vArr = ExtractDateTimeUsers(sInput)
If vArr(0, 0) = "No Matches" Then
JoinArrayWithSemiColons = "No Matches"
Exit Function
End If
'Loop through array to build the output string
For i = LBound(vArr, 1) To UBound(vArr, 1)
sOutput = sOutput & "; " & vArr(i, 0) & " " & vArr(i, 2)
Next i
JoinArrayWithSemiColons = Mid(sOutput, 3)
End Function
Sub TestFunction()
'Assume the string we are parsing is in Column A
'(I defined a fixed range, but you can make it dynamic as you need)
Dim rngToJoin As Range
Dim rIterator As Range
Set rngToJoin = Range("A10:A11")
For Each rIterator In rngToJoin
rIterator.Offset(, 1).Value = JoinArrayWithSemiColons(rIterator.Value)
Next rIterator
End Sub
As simple (non-regex) function you can use something like this:
Public Function getCounts(str As String) As Variant
Dim output() As Variant, holder As Variant, i As Long
ReDim output(0, 0)
holder = Split(str, " ")
For i = 0 To UBound(holder) - 2
If IsDate(holder(i) & " " & holder(i + 1) & " " & holder(i + 2)) Then
If UBound(output) Then
ReDim Preserve output(1 To 3, 1 To UBound(output, 2) + 1)
Else
ReDim output(1 To 3, 1 To 1)
End If
output(1, UBound(output, 2)) = holder(i)
output(2, UBound(output, 2)) = holder(i + 1) & " " & holder(i + 2)
i = i + 3
While Right(holder(i), 1) <> ":" And i < UBound(holder)
output(3, UBound(output, 2)) = output(3, UBound(output, 2)) & " " & holder(i)
i = i + 1
Wend
output(3, UBound(output, 2)) = Trim(output(3, UBound(output, 2))) & " " & Left(holder(i), Len(holder(i)) - 1)
End If
Next
If Application.Caller.Rows.Count > UBound(output, 2) Then
i = UBound(output, 2)
ReDim Preserve output(1 To 3, 1 To Application.Caller.Rows.Count)
For i = i + 1 To UBound(output, 2)
output(1, i) = ""
output(2, i) = ""
output(3, i) = ""
Next
End If
getCounts = Application.Transpose(output)
End Function
Just put it in a module to use it as UDF. (Outputs a 3-column-table)
If you have any questions, just ask :)
Just another way to do it. Maybe a little slower, but short and easy to read...
Public Function DateCount(str As String) As Variant
Dim pos As Integer, endpos As Integer, namepos As Integer
Dim Text As String, Output() As String, counter As Integer
pos = InStr(pos + 1, str, "/")
Do While pos > 0
endpos = InStr(pos + 1, str, "M ")
Text = Mid(str, pos - 1, endpos - pos + 2)
If IsDate(Text) Then
counter = counter + 1
ReDim Preserve Output(1 To 2, 1 To counter)
namepos = InStr(endpos, str, ":")
Output(1, counter) = Text
Output(2, counter) = Mid(str, endpos + 2, namepos - endpos - 2)
pos = namepos
End If
pos = InStr(pos + 1, str, "/")
Loop
' Only Count
getCounts = counter
' complete List
getCounts = Output
End Function
Using VBA or A Standard formula, I need to edit the following from cells.
I need to remove everything up to and including "Path:",
Then I need it to find | and start over until it reaches the end of the Cell
Example:
Category Name: Ladies, Category Path: Ladies|Category Name: Sale, Category Path: Sale|Category Name: New, Category Path: New|
Goal:
Ladies|Sale|New
It can include NO "|" or it can include up to 20 "|"
Edit: Realized I needed to show my work AFTER the tour. :)
I have spent a day or two on this and so far this is only I can come up with...
Dim s As String
s = Range("Z7").Value
Dim indexOfPath As Integer
Dim indexOfPipe As Integer
Dim indexOfCat As Integer
indexOfPath = InStr(1, s, "Path:")
indexOfPipe = InStr(1, s, "|")
Dim finalString As String
Dim pipeString As String
finalString = Right(s, Len(s) - indexOfPath - 5)
indexOfCat = InStr(1, finalString, "Path:")
pipeString = Right(finalString, Len(finalString) - indexOfCat - 5)
Range("A47").Value = finalString
Range("A48").Value = pipeString
How ever I have got to the point where I am not confusing myself...
Split the cell value on "|", then split each value in the resulting array on "Path:" and take the second element from the result of that.
Like this:
Sub Tester()
Dim s As String, arr, v, arr2
s = "Category Name: Ladies, Category Path: Ladies|Category Name:" & _
" Sale, Category Path: Sale|Category Name: New, Category Path: New|"
arr = Split(s, "|")
For Each v In arr
v = Trim(v)
If Len(v) > 0 Then
arr2 = Split(v, "Path:")
If UBound(arr2) > 0 Then Debug.Print arr2(1)
End If
Next v
End Sub
Try this Function:
Function splitonbar(rng As Range) As String
Dim tempArr() As String
Dim temp As String
Dim i As Integer
tempArr = Split(rng.Value, "|")
For i = LBound(tempArr) To UBound(tempArr)
If Len(tempArr(i)) > 0 Then
temp = temp & "|" & Trim(Mid(tempArr(i), InStr(tempArr(i), "Path:") + 5))
End If
Next i
splitonbar = Mid(temp, 2)
End Function
It can be used as Formula on the sheet, or be called from another sub. To use as a UDF put in a module in the workbook then simply call it with a formula:
=splitonbar(Z7)
Or you can call it with a sub like this:
Sub splitstring()
Dim t as string
t = splitonbar(range("Z7"))
debug.print t
end sub
To directly fit your needs:
Public Function test(ByVal arg As Variant) As String
Dim i As Long
arg = Split(arg, "Category Name: ")
For i = 1 To UBound(arg)
arg(i) = Left(arg(i), InStr(arg(i), ",") - 1)
Next
test = Mid(Join(arg, "|"), 2)
End Function
The Split itself cuts everything in front of the keyword. The Left cuts everything after the comma (including the comma itself)
If you still have questons left, just ask :)