Nested JSON VBA (records and fields) - arrays

I have this JSON file but I can't figure out if it's nested or what, if yes should I use collections? My goal is to parse this to an Excel sheet with VBA. So far I've been only able to parse the names. Any useful links for this type of data? I only found normal JSON but nothing like this. I'm new to JSON but have to do it during my internship.
Sample JSON:
{"records":[{"id":"rec1B74TQVtWwU6cW","fields":{"Name":"compriband","Materiaux":["recPqxRrg3tFC5o6T"]},"createdTime":"2021-03-07T18:22:47.000Z"},{"id":"rec3ZdAlSQhXCVG4c","fields":{"Name":"velux","Materiaux":["recig1Rh8WFpqe0wD","recAha2bQ5BTNED9V","recWAj3FZRsPj65Gz","recfv8V2t0Pje2Llg"]},"createdTime":"2021-03-07T18:27:27.000Z"}
My code attempt:
Private Sub CommandButton1_Click()
Dim W As Worksheet
Set W = ActiveSheet
'Read column names from row 1. Should match Airtable column names. No empty columns.
Dim fields As String
colCount = 2
Do Until IsEmpty(W.Cells(2, colCount))
fields = fields & "&fields[]=" & W.Cells(2, colCount).Value
colCount = colCount + 1
Loop
'Get the data from airtable
Dim http As New WinHttpRequest
Dim resp As String
Dim url As String
url = "https://api.airtable.com/v0/appY6Wo3AmLHqHkjr/categorie?api_key=key_here" & fields
http.Open "GET", url, False
http.Send
Debug.Print "Resultats " + CStr(http.ResponseText)
Dim json As Object
Set json = JsonConverter.ParseJson(http.ResponseText)
respRecord = 1
On Error GoTo Exit_Loop
Do Until json("records")(respRecord)("fields")(W.Cells(1, 1).Value) = ""
For respCol = 1 To colCount - 1
cellValue = json("records")(respRecord)("fields")(W.Cells(1, respCol).Value)
W.Cells(respRecord + 1, respCol).Value = cellValue
Next
respRecord = respRecord + 1
Loop
Exit_Loop:
End Sub

As I said in the comments you haven't done bad so far. You are missing however some degree of understanding in how the JSON is structured.
A tool like this might help you understand the structure.
Also, I see that you have used the VBA JSON parser . The examples in the documentation should help you gain a better understanding of how to parse a JSON string.
Having said that, I will point out some stuff that should help you:
Your JSON looks like so:
Basically you have an array called records, designated by [] consisting of two items 0 and 1. These 2 items are nested JSONs. So your initial JSON is an array of JSONs.
To explicitly access the individual items of the array you can do this:
json("records")(1)
json("records")(2)
...
json("records")(n)
Now, each item consists of elements of its own. These elements could be arrays, nested jsons etc. Your items consist of two parameters id and createdTime and a nested JSON fields.
To go furhter into each item you can use this syntax:
json("records")(1)("nameOfParameter")
or
json("records")(1)("nameOfNestedJSON")
So if you wanted the 1st items id you would do:
Debug.Print json("records")(1)("id")
To loop through all the items of the array you can do this:
Dim item As Object
For Each item In json("records")
Debug.Print item("id")
Next item
The nested JSON fields consists of two parameters Name and createdTime and an array Materiaux.
You can get the parameters like so:
Debug.Print json("records")(1)("fields")("Name")
And of course you can also do:
Dim item As Object
For Each item In json("records")
Debug.Print item("fields")("Name")
Next item
You can go even deeper to access the elements of the Materiaux array:
Debug.Print json("records")(2)("fields")("Materiaux")(1)
And even loop through them:
Dim arrayItem As Variant
For Each arrayItem In json("records")(2)("fields")("Materiaux")
Debug.Print arrayItem
Next arrayItem

Related

VBA userform combobox - how to customize date format from number format?

I am trying to display an array in a combobox of a userform. The array is populated with a column of a listobject whose entries are formatted as dates. Within the array the dates are formatted into numbers. I would like the array entries to be displayed as "normal" date formats in the combobox, however, all I get is the date in the number format.
Here's the code I used including a couple of different versions I tried to convert the format (all failed).
How I put the date array together. Below you see the main sub from which I call the array function and the userform:
Sub master()
Dim dates_collection As Variant
dates_collection = dates_count()
'--------------------------------------------------------------------------
With uf_selection 'userform name
'cbo_delivery = combobox
.cbo_delivery.RowSource = ""
.cbo_delivery.List = dates_collection 'adding array to combobox
.cbo_delivery.Text = Format(.cbo_delivery.Text, "dd.mmm.yyyy") '>> not working
.cbo_delivery.ListIndex = 0
.Show
End With
End Sub
The corresponding function looks like below. It is deleting all duplicates in the listobject column:
Public Function dates_count() As Variant()
Dim data() As Variant
Dim r As Long
Dim dict As New Scripting.Dictionary: Set dict = CreateObject("Scripting.Dictionary")
data = Thisworkbook.Sheets("Raw Data").ListObjects("tbl_raw").ListColumns("Week End Date").DataBodyRange.Value2
For r = 1 To UBound(data)
dict(data(r, 1)) = Empty
Next
data = WorksheetFunction.Transpose(dict.keys())
dates_count = data
End Function
Note that as another approach I manipulated the initialize sequence of the userform as shown below. Then doing so, I outcommented the with sequence in the code above.
Private Sub UserForm_Initialize()
cbo_delivery.RowSource = ""
cbo_delivery.List = dates_collection
cbo_delivery.value = Format(DateValue(cbo_delivery.Text), "dd.mmm.yyyy")
cbo_delivery.ListIndex = 0
End Sub
Additionally, I tried the approach presented here: click link

Declaring Arrays Dynamically in VBA

I am trying to create arrays of specific length dynamically, so that I can use them in a bigger procedure.
Sample Data:
The below code using the Dictionary Gives me the Count and Unique File Extensions in the Data.
Code:
Dim dict As New Scripting.Dictionary
For Each cel In Range("B1:B8")
I = 1
If Not dict.Exists(cel.Text) Then
dict.Add cel.Text, I
Else
temp = dict(cel.Text) + 1
dict.Remove cel.Text
dict.Add cel.Text, temp
End If
Next cel
For Each varKey In dict.Keys
Debug.Print varKey & ":" & dict.Item(varKey)
Next
Result:
What I am trying to do is create 3 (in this sample) arrays pdf(4),xlsx(3),docm(1)
Using the results from Dictionary.
But the line Dim varkey(dict.Item(varKey)) As Variant gives me Compile Error.
Constant Expression Required
Is there a way to do it ? I searched google for ways to achieve this, but with no luck.
Basically what I want is to use these different extension names to declare Arrays. But these extension names will vary so I need to declare them dynamically. Array should have same name as the Extension.
So pick the name from sheet or from Dictionary and declare that as Array of a specified Length. Length can be Redim'ed afterwards also, but the main problem is declaring them from a variable.
As BrakNicku commented a Dictionary of Dictionaries will get you the answer that you want.
Sub PrintExtensionCount()
Dim Cell As Range
Dim Map As New Scripting.Dictionary, subMap As New Scripting.Dictionary
For Each Cell In Range("B1:B8")
If Not Map.Exists(Cell.Value) Then Map.Add Cell.Text, New Dictionary
Set subMap = Map(Cell.Value)
subMap.Add Cell.Offset(0, -1).Value, vbNullString
Next
Dim Key As Variant
For Each Key In Map
Set subMap = Map(Key)
Debug.Print Key; ":"; subMap.Count
Next
End Sub
Result
Not to confuse things but I like to use a Dictionary of ArrayList.
Sub PrintExtensionCount()
Dim Cell As Range
Dim Map As New Scripting.Dictionary, list As Object
For Each Cell In Range("B1:B8")
If Not Map.Exists(Cell.Value) Then Map.Add Cell.Text, CreateObject("System.Collections.ArrayList")
Set list = Map(Cell.Value)
list.Add Cell.Offset(0, -1).Value
Next
Dim Key As Variant
For Each Key In Map
Set list = Map(Key)
Debug.Print Key; ":"; list.Count
Next
End Sub
I'm not sure exactly what the task at hand is, but this is an X-Y problem, if I understand your comments.
Dim statements - declarative statements - are not executable. This is regardless of the type (String, Long, Variant array, whatever.) Your question title might have been bit misleading in that regard, since it seems like essentially you're trying to dynamically declare variables - the fact they are arrays is coincidental.
You can avoid the compile error by ReDimming an array based on the count from your dictionary, but you can't come up with a dynamic list of variables.

VB Convert 2 DataRows to a Single String in an Array

My Goal is to take two rows(FirstName and Surname) Convert them to a single Array of "FirstName, Surname".
This is my terrible code i eventually put together
Private Sub Search_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'TODO: This line of code loads data into the 'DbaPatientDataSet.tblPatientData' table. You can move, or remove it, as needed.
Me.TblPatientDataTableAdapter.Fill(Me.DbaPatientDataSet.tblPatientData)
listFirst.DataSource = Me.TblPatientDataBindingSource
listFirst.DisplayMember = "FirstName"
listLast.DataSource = Me.TblPatientDataBindingSource
listLast.DisplayMember = "Surname"
Dim Lenth As Integer = Me.listFirst.Items.Count - 1
Dim count As Integer = 1
Dim ArrFirst(Lenth) As String
Dim ArrLast(Lenth) As String
For count = 1 To Lenth
ArrFirst(count) = listFirst.Items(count).ToString
ArrLast(count) = listLast.Items(count).ToString
Next count
count = 1
For count = 1 To Lenth
arrFullName(count) = ArrLast(count) & ", " & ArrFirst(count)
Next count
'Arrays Set =====================================================
But with this code i get an Array of
`"Sytem.Data.DataRowView, Sytem.Data.DataRowView"
"Sytem.Data.DataRowView, Sytem.Data.DataRowView"
"Sytem.Data.DataRowView, Sytem.Data.DataRowView"
"Sytem.Data.DataRowView, Sytem.Data.DataRowView"
`
As you can see
Here
There must be an easy way to convert both DataRows to strings then concatenate them together in an array
I am going to search this array using a Binary Search to find a desired name
Thanks
First, I think you are confusing your rows and your columns. You have 2 columns. I went directly to full name but I think you can break it out if you need to.
Dim arrNames(ListBox1.Items.Count - 1) As String
For i As Integer = 0 To ListBox1.Items.Count - 1
arrNames(i) = $"{ListBox1.Items(i)} {ListBox2.Items(i)}"
Next
For Each item In arrNames
Debug.Print(item)
Next
The string with the $ in front is an interpolated string. Sort of an improvement to String.Format.
I know there is an answer but for now you could go direct to the data table to get what you need.
Dim arrNames(ListBox1.Items.Count - 1) As String
Dim i As Integer = 0
Dim dt As DataTable = DbaPatientDataSet.Tables(0)
For Each row As DataRow In dt.Rows
arrNames(i) = $"{row("Surname")}, {row("FirstName")}"
i += 1
Next
For Each item In arrNames
Debug.Print(item)
Next
'assume the names of your columns are Surname and FirstName
If I run your code up, I get the result you are looking for, so I'm not sure what you are missing. In saying that though, you are making things hard on yourself by messing around with arrays :). Just use the dataset rows directly - they are strongly typed and you can check for nulls etc as needed... something like this;
Dim fullNames As New List(Of String) '-- or you could fill your array.
For Each row As DbaPatientDataSet.tblPatientDataRow In ds.tblPatientData
fullNames.Add(row.Surname & ", " & row.FirstName)
Next
Just looking at what you are trying to achieve, if it was me, I would be bringing back the formatted data in my query that fills the dataset i.e. a third, FullName, column.
It has been in the back of my mind. Finally got it for the List Box directly.
Dim arrFullNames(ListBox1.Items.Count - 1) As String
Dim i As Integer = 0
For Each item As DataRowView In ListBox1.Items
arrFullNames(i) = $"{DirectCast(item("Surname"), String)}, {DirectCast(item("Firstname"), String)}"
i += 1
Next
For Each item As String In arrFullNames
Debug.Print(item)
Next

How to store array names in array in VBA

I have a list of arrays. I am looping through to write the data in the arrays into a text file. When I loop through each time, I would like to use a different array to access the data.
I am thinking of storing the names of these arrays in an different array and as I loop through, I can access this array using the current loop index. But I am not sure how to do this in VBA.
Need some guidance on this. I am welcome to other suggestions as well.
You could also store them in a collection. This will also allow you to add a key to each array that you store in the collection. You can than even call a specific array using this key. Just a short example to get you started:
Sub CreateCollection()
Dim col As Collection
Dim arr As Variant
Dim MyArray1(1) As String
Dim MyArray2(1) As String
MyArray1(0) = "FirstItemArr1"
MyArray1(1) = "SecondItemArr1"
MyArray2(0) = "FirstItemArr2"
MyArray2(1) = "SecondItemArr2"
Set col = New Collection
col.Add MyArray1, "ArrayName1"
col.Add MyArray2, "ArrayName2"
For Each arr In col
Debug.Print arr(1)
Next
Debug.Print col("ArrayName2")(1)
Set col = Nothing
End Sub

Creating an array in VBA from spreadsheet data

I have a spreadsheet of data that I want to put into a VBA array which then outputs unique values to a new sheet. I have got that to work so far. However, some of the cells in the original data have text separated by commas, and I want to add those to the array as well. I can't quite get that bit to work.
After the various 'dims', my code is
'Grabs the data to work with
Set rTable = Worksheets("Data Entry").Range("N1:N100", "P1:P100")
'Puts it into an array
MyArray = rTable.Value
'Sets where the data will end up
Set rCell = Worksheets("TestSheet").Range("A1:A100")
'Each unique entry gets added to the new array
On Error Resume Next
For Each a In MyArray
UnqArray.Add a, a
Next
'Add unique data to new location
For i = 1 To UnqArray.Count
rCell(i, 1) = UnqArray(i)
Next
I have tried doing a new variant to store the split data
SpArray = split(MyArray,", ")
and then have that here
MyArray = rTable.Value
SpArray = split(MyArray,", ")
and then refer to SpArray for the rest of the code
I've also tried to have as part of
For Each a in SpArray
but it doesn't work for me.
Do I need to do a separate loop on each cell of the array before I filter out the unique ones?
Yes, you need another loop. But if you set a reference to Microsoft Scripting Runtime and use a Dictionary object, you can eliminate the loop that writes to the range because Dictionary.Keys returns an array.
In this example, it attempts to split every entry on a comma and treats each of those as a unique. If there is no comma, Split returns the one value so it works in both cases. There's probably a small cost to splitting things that don't need to be split, but you won't notice until your range is much larger. And it makes the code cleaner, I think.
Sub WriteUniques()
Dim dcUnique As Scripting.Dictionary
Dim vaData As Variant
Dim vaSplit As Variant
Dim i As Long, j As Long
vaData = Sheet1.Range("$I$12:$I$62").Value
Set dcUnique = New Scripting.Dictionary
For i = LBound(vaData, 1) To UBound(vaData, 1)
vaSplit = Split(vaData(i, 1), ",")
For j = LBound(vaSplit) To UBound(vaSplit)
If Not dcUnique.Exists(vaSplit(j)) Then
dcUnique.Add vaSplit(j), vaSplit(j)
End If
Next j
Next i
Sheet1.Range("J12").Resize(dcUnique.Count, 1).Value = Application.Transpose(dcUnique.Keys)
End Sub
The code tweak that worked for me was to put the Split at the end.
'Add unique data to new location
For i = 1 To UnqArray.Count
rCell(i, 1) = Split(UnqArray(i), ",")
Next
This then built up an array using data from different ranges and splitting up comma separated ones before outputting only the unique ones.

Resources