Answered by:
Splitting field

Question
-
hi,
I have an address field in access database which has street, state, city and zip in it.
They are structured in this way:
Name Address
xxx [{"street":"343 13th st", "state":"WI", "zip:"23232", "city":"xxxx"}, {"street":"455 13th st", "state":"WI", "zip:"64564", "city":"yyyy"}]
A person can have two addresses. Now, how can I split these fields in address field to separate columns? Someone please guide me. Thanks for any help!
Thursday, August 17, 2017 2:56 PM
Answers
-
Here's a little code that might help. Obviously you will need to adapt it to your circumstances ... After you do this for a while, you begin to recognize the "seams" in text patterns.
Option Compare Database Option Explicit Private rst As Recordset ' declared here so we can use it anywhere Private Torst As Recordset ' declared here so we can use it anywhere Private theName As String ' the name of the person Public Sub aaaRunParser() Dim r As Boolean r = ParseAddresses End Sub ' Private Function ParseAddresses() As Boolean ' Purpose: ParseAddresses - split hairy addresses On Error GoTo errorexit Dim result As Boolean result = False '================================ Dim db As DAO.Database Set db = CurrentDb Set rst = db.OpenRecordset("Table1", dbOpenDynaset) Set Torst = db.OpenRecordset("Table2", dbOpenDynaset) Do While Not rst.EOF '================================ Dim addresses As String theName = rst!theName addresses = rst!Field2 SplitThem addresses ' Debug.Print addresses '================================ continue: rst.MoveNext Loop '================================ rst.Close Set rst = Nothing Torst.Close Set Torst = Nothing db.Close Set db = Nothing result = True exitprocessing: ParseAddresses = result Exit Function errorexit: Debug.Print Err, "ParseAddresses", Erl #If PNRDEV Then Stop Resume #End If Resume exitprocessing Resume End Function Public Sub SplitThem(addresses As String) Dim address As String Do While AnotherAddress(addresses, address) Parse (address) Loop End Sub Public Function AnotherAddress(addresses As String, address As String) As Boolean ' find the start position Dim first As Long first = InStr(1, addresses, "{") If first = 0 Then ' no more addresses AnotherAddress = False address = "" Exit Function End If ' find the last position Dim last As Long last = InStr(1, addresses, "}") ' position of the } Dim length As Integer: length = last - 1 - first ' always a little tricky address = Mid(addresses, first + 1, length) ' Debug.Print address ' eat the address from addresses addresses = Mid(addresses, last + 1) ' skip over the first } ' Debug.Print addresses AnotherAddress = True End Function Public Sub Parse(address As String) ' let's dump the quotes address = Replace(address, """", "") ' and the spaces following the commas address = Replace(address, ", ", ",") ' now let's split the fields Dim flds() As String ' 0-based flds = Split(address, ",") ' store them in the appropriate fields in the database ' in the same table, or elsewhere ' some tedium here Dim nv() As String ' name, value Torst.AddNew Torst.Fields("TheName") = theName nv = Split(flds(0), ":") Torst.Fields(nv(0)) = nv(1) nv = Split(flds(1), ":") Torst.Fields(nv(0)) = nv(1) nv = Split(flds(2), ":") Torst.Fields(nv(0)) = nv(1) nv = Split(flds(3), ":") Torst.Fields(nv(0)) = nv(1) Torst.Update End Sub
peter n roth - http://PNR1.com, Maybe some useful stuff
- Proposed as answer by Terry Xu - MSFT Wednesday, August 23, 2017 7:48 AM
- Unproposed as answer by Terry Xu - MSFT Wednesday, August 23, 2017 7:49 AM
- Proposed as answer by Terry Xu - MSFT Wednesday, August 23, 2017 7:50 AM
- Marked as answer by roxannaappleby Thursday, August 24, 2017 1:12 PM
Wednesday, August 23, 2017 1:10 AM
All replies
-
Hi,
This looks like a JSON format. You might try using a JSON parser.
Just my 2 cents...
Thursday, August 17, 2017 3:28 PM -
You might try LEFT, InStr, & MID function --
---- UNTESTED ---
FirstAddr: Left([YourTable].[YourAddrField], InStr([YourTable].[YourAddrField], "{") -3, & "]"
2ndAddr: "[" & Mid([YourTable].[YourAddrField], InStr(9,[YourTable].[YourAddrField], "{"))
Build a little, test a little
- Edited by QA Guy CommElec Thursday, August 17, 2017 5:28 PM
Thursday, August 17, 2017 5:26 PM -
Thanks QA Guy,
your FirstAddr gives me just this: [ instead of 343 13th st
this is what I've written.
Where: Left([Contact].[Address],InStr([Contact].[Address],"{")-3)
Please guide me through this.
Monday, August 21, 2017 7:20 PM -
Thanks .theDBguy,
Could you please refer some articles? I've been trying split functions in VBA but its getting very hard with all the apostrophes to start where and end where. Extracting data from the apostrophes especially when there are multiple commas is very challenging.
Monday, August 21, 2017 8:57 PM -
Monday, August 21, 2017 9:05 PM
-
Post how you want the results to appear.
again --
FirstAddr: Left([YourTable].[YourAddrField], InStr([YourTable].[YourAddrField], "},") -1)
Build a little, test a little
Monday, August 21, 2017 9:28 PM -
QA Guy CommElec,
This is the result I am expecting
Street State ZipCode City 343 13th st WI 23232 xxxx 455 13th st WI 64564 yyyy
Thank you.
Tuesday, August 22, 2017 1:24 PM -
Here's a little code that might help. Obviously you will need to adapt it to your circumstances ... After you do this for a while, you begin to recognize the "seams" in text patterns.
Option Compare Database Option Explicit Private rst As Recordset ' declared here so we can use it anywhere Private Torst As Recordset ' declared here so we can use it anywhere Private theName As String ' the name of the person Public Sub aaaRunParser() Dim r As Boolean r = ParseAddresses End Sub ' Private Function ParseAddresses() As Boolean ' Purpose: ParseAddresses - split hairy addresses On Error GoTo errorexit Dim result As Boolean result = False '================================ Dim db As DAO.Database Set db = CurrentDb Set rst = db.OpenRecordset("Table1", dbOpenDynaset) Set Torst = db.OpenRecordset("Table2", dbOpenDynaset) Do While Not rst.EOF '================================ Dim addresses As String theName = rst!theName addresses = rst!Field2 SplitThem addresses ' Debug.Print addresses '================================ continue: rst.MoveNext Loop '================================ rst.Close Set rst = Nothing Torst.Close Set Torst = Nothing db.Close Set db = Nothing result = True exitprocessing: ParseAddresses = result Exit Function errorexit: Debug.Print Err, "ParseAddresses", Erl #If PNRDEV Then Stop Resume #End If Resume exitprocessing Resume End Function Public Sub SplitThem(addresses As String) Dim address As String Do While AnotherAddress(addresses, address) Parse (address) Loop End Sub Public Function AnotherAddress(addresses As String, address As String) As Boolean ' find the start position Dim first As Long first = InStr(1, addresses, "{") If first = 0 Then ' no more addresses AnotherAddress = False address = "" Exit Function End If ' find the last position Dim last As Long last = InStr(1, addresses, "}") ' position of the } Dim length As Integer: length = last - 1 - first ' always a little tricky address = Mid(addresses, first + 1, length) ' Debug.Print address ' eat the address from addresses addresses = Mid(addresses, last + 1) ' skip over the first } ' Debug.Print addresses AnotherAddress = True End Function Public Sub Parse(address As String) ' let's dump the quotes address = Replace(address, """", "") ' and the spaces following the commas address = Replace(address, ", ", ",") ' now let's split the fields Dim flds() As String ' 0-based flds = Split(address, ",") ' store them in the appropriate fields in the database ' in the same table, or elsewhere ' some tedium here Dim nv() As String ' name, value Torst.AddNew Torst.Fields("TheName") = theName nv = Split(flds(0), ":") Torst.Fields(nv(0)) = nv(1) nv = Split(flds(1), ":") Torst.Fields(nv(0)) = nv(1) nv = Split(flds(2), ":") Torst.Fields(nv(0)) = nv(1) nv = Split(flds(3), ":") Torst.Fields(nv(0)) = nv(1) Torst.Update End Sub
peter n roth - http://PNR1.com, Maybe some useful stuff
- Proposed as answer by Terry Xu - MSFT Wednesday, August 23, 2017 7:48 AM
- Unproposed as answer by Terry Xu - MSFT Wednesday, August 23, 2017 7:49 AM
- Proposed as answer by Terry Xu - MSFT Wednesday, August 23, 2017 7:50 AM
- Marked as answer by roxannaappleby Thursday, August 24, 2017 1:12 PM
Wednesday, August 23, 2017 1:10 AM -
Hi roxannaappleby,
If the address is a json string, you could decode it as json object to get the data and insert them to where you want.
Here is the example.
Private Sub Command0_Click() Dim objJSControl, objJSTypeInfos, objJSTypeInfo Set objJSControl = CreateObject("MSScriptControl.ScriptControl") Dim db As DAO.Database Dim rstSource As DAO.Recordset Dim rstTarget As DAO.Recordset Set db = CurrentDb Set rstSource = db.OpenRecordset("Table2", dbOpenDynaset) Set rstTarget = db.OpenRecordset("Table3", dbOpenDynaset) If Not (rstSource.EOF And rstSource.BOF) Then rstSource.MoveFirst Do Until rstSource.EOF = True jsStr = "var mydata=" & rstSource.Fields("Address").Value With objJSControl .Language = "javascript" On Error GoTo ErrorHandler .addcode jsStr Set objJSTypeInfos = .codeobject For Each objJSTypeInfo In CallByName(objJSTypeInfos, "mydata", VbGet) City = CallByName(objJSTypeInfo, "city", VbGet) State = CallByName(objJSTypeInfo, "state", VbGet) street = CallByName(objJSTypeInfo, "street", VbGet) zip = CallByName(objJSTypeInfo, "zip", VbGet) rstTarget.AddNew rstTarget.Fields("Name") = rstSource.Fields("Name") rstTarget.Fields("City") = City rstTarget.Fields("State") = State rstTarget.Fields("Street") = street rstTarget.Fields("ZipCode") = zip rstTarget.Update Next objJSTypeInfo End With rstSource.MoveNext Loop End If rstSource.Close rstTarget.Close Exit Sub ErrorHandler: MsgBox "Json string format is not correct: " & Err.Description End Sub
The reason why I said if the address is Json string is that the string you shared is not a correct json string format. It missed a " after zip and in this situation, I think you have to decode it as normal string.
Best Regards,
Terry
Wednesday, August 23, 2017 9:49 AM -
Thanks Peter for the detailed explanation of the code. I really appreciate it.
I will try it and see if that works for me. Meanwhile, I have some questions as I am still learning Access & VBA.
What is recordset Torst used for in this example? Are we going to create a whole new table to insert these newly created fields?
What will happen to 'Address' field after splitting into multiple fields. Can I just hide it or do something with it? This Address field is currently in a query and after writing this module, I am thinking of using the functions in the newly created fields. Am I understanding it correct? Please excuse if I am being dumb as I just want to clear my questions and dive into code with more clarity.
Thanks again.
- Edited by roxannaappleby Wednesday, August 23, 2017 1:36 PM
Wednesday, August 23, 2017 1:22 PM -
Hi terry,
sorry, that must be a typo, not sure how it missed a ". Thanks for pointing it out.
[{"street":"343 13th st", "state":"WI", "zip":"23232", "city":"xxxx"}, {"street":"455 13th st", "state":"WI", "zip":"64564", "city":"yyyy"}]
I do like the option to decode it into normal string. I will probably try your suggestion as well. Will keep you posted.
Thank you.
Wednesday, August 23, 2017 1:24 PM -
The new table should have the fields ID, theName, street, etc. ID could be an autonumber, since now you may have records in which theName is duplicated. That’s easier to do in a new table than by writing into the original. That’s what Torst references, the new table.
The ‘Address’ field in the original table is up for grabs. You can delete the column, but don’t be hasty. If there’s a problem with an address, you don’t want to throw away useful info.
So your source for the address info is a query on a table? In that case, open the first rst on that query.
peter n roth - http://PNR1.com, Maybe some useful stuff
Wednesday, August 23, 2017 2:09 PM -
Yes, I have a query which has address field in it and thanks for the tip. I will open the first rst on the query. The query has joins to three different tables and has a total of 16 fields in it.
Also, thanks for the Torst table explanation. It definitely seems to be easier to have a new table.
I will probably keep the 'Address' field as I am going to display the results in new table.
I did not understand the do while loop though. I hope you will clear this for me.
Do While Not rst.EOF
'================================
Dim addresses As String
theName = rst!theName
addresses = rst!Field2
SplitThem addresses
' Debug.Print addresses
'================================
continue:
rst.MoveNext
Loop
'================================Should I be assigning the person's full name to 'theName' variable? To be specific I did not quite understand this line.
theName=rst!theName
Thank you.
- Edited by roxannaappleby Wednesday, August 23, 2017 2:25 PM
Wednesday, August 23, 2017 2:19 PM -
The Do loop starts out like this:
Do While Not rst.EOF '================================ '================================ continue: rst.MoveNext Loop
It is generated by one of my tools. The "continue:" label is there because sometimes I need to exit from what the code is doing and proceed to the next record.
I’ve defined theName as one of the variables global to this module. I assume you have a similar name for the query field because Name is a reserved word. So you could write it
theName = rst!YourName
Suppose there are 3 fields associated with the name. rst points to the record, so you can do what you want with those 3 fields: perhaps
theName = rst!firstName & “ “ & rst!lastName
Field2 is a convenient way for me to refer to the address column of the query.
peter n roth - http://PNR1.com, Maybe some useful stuff
Wednesday, August 23, 2017 2:38 PM -
ok. got it. I kinda figured it out myself but your explanation clarified it for me better.
I've actually done something like this, as it kept throwing compile error:
theName = rst.Fields("Full Name") instead of theName = rst!theName
That's the name of my field(person's full name)
and was able to clear that compile error.
Stuck with another:
While adding new fields to Torst, I am getting to 'errorexit:' for some reason.
Is it because, I haven't created field names manually in the new table? From the code, I got the impression that
Torst.AddNew will be creating the fields for me. Am I wrong in thinking so?
Should I create all the 16 fields manually in the new table along with the newly split fields?
Thanks a ton, I've learnt a lot so far. :)
Wednesday, August 23, 2017 2:57 PM -
I usually avoid blanks in field names, for convenience sake.
Yes, you have to name your fields manually. ‘AddNew’ appends data to an already defined field.
Also, from VBA, open the help file … it’s different from the Access help file.
peter n roth - http://PNR1.com, Maybe some useful stuff
Wednesday, August 23, 2017 3:10 PM -
All right, Thank you so much for all the help.
One last question, regarding rst.AddNew
why are we inserting into nv(0) for each new field? It exists to error after this line:
nv = Split(flds(1), ":")
rst.Fields(nv(0)) = nv(1) // error after this line hereFor now, I am trying to insert into same table for testing purposes.
Wednesday, August 23, 2017 3:33 PM -
There are 4 fields saved into the “flds()” array.
Each fld(i), i=0..3 is split at the “:” into the nv() array.
nv(0) is the name of the field in your table, from the input pattern, supposedly like “street”. nv(1) is the value of that field.
- In VBA, put a breakpoint on the line beginning with rst.Fields, the one after the Split.
- On the View menu, open the Locals window. This will show you all the local variables and their values. nv will appear with a box with a + to the left of it. Click the plus to expand the display.
You will find that nv(0) does NOT match the name of a field in your table. Why that is depends on how good the data is (any extra blanks?) and what has happened to it (or not) in the parsing process.
peter n roth - http://PNR1.com, Maybe some useful stuff
Wednesday, August 23, 2017 6:07 PM -
Yes, I did debug and found that nv(1) holds the field value but I guess it's failing to insert into the newly created field. There must be something wrong with the way I am creating these new fields.
So, I created new field in the existing query instead of a new table. I went into query design view and inserted new column and clicked zoom and gave a name to it. Is that the right way of doing? Or should I better go with the new table? It seems tedious to me to create new table with all the fields I want and then write code to fill values into them. So instead I am trying to insert into the existing query which is my source.
Also, should I be calling the function on every new field that I am going to create?
Thanks for being patient with my questions.
- Edited by roxannaappleby Wednesday, August 23, 2017 8:15 PM
Wednesday, August 23, 2017 6:19 PM -
Aha! Now you’re asking yourself questions and giving yourself the right answers! ;o)
Read over the VBA help file page devoted to the Recordset.AddNew method.
peter n roth - http://PNR1.com, Maybe some useful stuff
Thursday, August 24, 2017 2:32 AM -
Alright, thanks Peter. You've helped me a lot.
It's not fair to leave me at the edge though. ;)
How do I run this function, Peter? On address field? Or on each new field as an expression? That's one last question, please :)
Thank you.
- Edited by roxannaappleby Thursday, August 24, 2017 2:02 PM
Thursday, August 24, 2017 1:12 PM -
Upon further debugging, I noticed that both nv(0) and nv(1) are pulling in right values i.e. nv(0) gives me correct field name for example Street and nv(1) gives me street address. THat part looks correct to me.
So, all I am failing is how to invoke this function? I am not sure how to call this function, is it on address field? Also, the newly created four fields seem to be not working properly for some reason. I am creating them manually in the query design view.
Please give some direction to follow. Thanks.
Thursday, August 24, 2017 5:22 PM -
we're all "on the edge", doncha know.
A query is not updateable; therefore, you have to use a table.
peter n roth - http://PNR1.com, Maybe some useful stuff
Thursday, August 24, 2017 6:27 PM