Excel VBA: Unable to reassign value to array - excel

I have code that loops through a dictionary. the value for each key in the dictionary is a 2-item array (dictionary looks like name: [string, integer]).
when I reference the dictionary later on, I can see and print the string and integer in the array belonging to the dictionary entry, but I can't change the integer through a normal assignment like dictionary(name)(2) = 5; after doing so in the code, I print the array value to a debug file, and the array value is the original value, not its changed value. I have no idea why this doesn't work, and how I can get it to work. everything I've read on arrays says you just assign array(0) = something.
here is part of the code below:
defining the original dictionary:
dim Dict as Object
Set Dict = CreateObject("Scripting.Dictionary")
for i = 1 to 10
strnum = "str"&i
Dict.Add strnum, array("str"&i+1,0)
next
'printing each item in dict returns "Str1: [str2,0]", etc. as it should
working with the dictionary:
For each Cell in Range("a1:a11")
If Cell.Value <> "" And Dict.exists(Cell.Value) Then
name = Cell.Value
range = Dict(name)(0)
If Dict(name)(1) = 1 Then
'we have already located the name here
Else
Dict(name)(1) = 1
s = "Setting the found flag to " & Dict(name)(1)
debug.print s
'Dict(name)(1) returns 0 when it should return 1
end if
end if
next cell
Range a1:a11 is Str1,Str2,Str3,Str4,Str5...Str11.
What can I do to fix this?

You are creating some weird aliasing with
for i = 1 to 10
Str = "str"&i
arr(1) = "str"&i+1
arr(2) = 0
Dict.Add Str, arr
next
since you only created a single array when you dimensioned arr.
You could create a dictionary of dictionaries to do what you wanted:
Sub test()
Dim Dict As Object, Str, i, arr
Set Dict = CreateObject("Scripting.Dictionary")
For i = 1 To 10
Set arr = CreateObject("Scripting.Dictionary")
arr.Add 1, "str" & i + 1
arr.Add 2, 0
Str = "str" & i
Dict.Add Str, arr
Next
For i = 1 To 10
Debug.Print (Dict("str" & i)(1))
Next i
Dict("str1")(1) = "Bob"
Debug.Print Dict("str1")(1) 'prints Bob
End Sub

Related

Parse JSON to excel worksheet by JSONConverter

I created a VBA code that enables me to get a string from a website. The string looks like that (json format)
[
{
"CD":151,
"nID":111,
"sNM":"PNAME1",
"GDR":"MA",
"RGN":"MM",
"reID":1,
"status":"RSB",
"NTY":"EG",
"CLNUM":1,
"CLNM":"THIR",
"YER":2022,
"SCHOD":1718,
"STID":2,
"THNM":"BRYYY",
"SCHNO":"HTBAN",
"rCD":6,
"schooL_TYPE":1,
"SGT":1,
"CLCD":3,
"NG1":1,
"NG2":2,
"YCOD":null,
"general":10,
"special":1,
"naT_ID":1,
"GDR_ID":1,
"sGCOD":8,
"sTTY":1,
"obNM":"NTF",
"obID":0,
"STYN":"NTHMY",
"PSNUM":null
},
{
"CD":153,
"nID":222,
"sNM":"ALIIKK",
"GDR":"MA",
"RGN":"MM",
"reID":1,
"status":"RSB",
"NTY":"EG",
"CLNUM":1,
"CLNM":"THIR",
"YER":2022,
"SCHOD":1718,
"STID":2,
"THNM":"SYYYYY",
"SCHNO":"HTBAN",
"rCD":6,
"schooL_TYPE":1,
"SGT":1,
"CLCD":3,
"NG1":1,
"NG2":2,
"YCOD":null,
"general":10,
"special":1,
"naT_ID":1,
"GDR_ID":1,
"sGCOD":8,
"sTTY":1,
"obNM":"NTF",
"obID":0,
"STYN":"NTHMY",
"PSNUM":null
}
]
I am trying to store the data into a collection and arrays
This is my try but I am confused about the line of setting the collection
Dim json As Object, col As Collection
Set json = JSONConverter.ParseJson(sResp)
Set col = json("CD")
Dim a, i As Long
ReDim a(1 To col.Count, 1 To 1)
For i = 1 To col.Count
'a(i, 1) = col.Item(i)("")
Next i
The string should have two persons' data. How can I pares all the data included into two rows?
I got an error Dictionary Key Not Found at this line of JSONConverter module
Option Explicit
Sub demo()
Dim sResp
sResp = "[{'CD':151,'nID':111,'sNM':'PNAME1','GDR':'MA','RGN':'MM','reID':1,'status':'RSB','NTY':'EG','CLNUM':1,'CLNM':'THIR','YER':2022,'SCHOD':1718,'STID':2,'THNM':'BRYYY','SCHNO':'HTBAN','rCD':6,'schooL_TYPE':1,'SGT':1,'CLCD':3,'NG1':1,'NG2':2,'YCOD':null,'general':10,'special':1,'naT_ID':1,'GDR_ID':1,'sGCOD':8,'sTTY':1,'obNM':'NTF','obID':0,'STYN':'NTHMY','PSNUM':null},{'CD':153,'nID':222,'sNM':'ALIIKK','GDR':'MA','RGN':'MM','reID':1,'status':'RSB','NTY':'EG','CLNUM':1,'CLNM':'THIR','YER':2022,'SCHOD':1718,'STID':2,'THNM':'SYYYYY','SCHNO':'HTBAN','rCD':6,'schooL_TYPE':1,'SGT':1,'CLCD':3,'NG1':1,'NG2':2,'YCOD':null,'general':10,'special':1,'naT_ID':1,'GDR_ID':1,'sGCOD':8,'sTTY':1,'obNM':'NTF','obID':0,'STYN':'NTHMY','PSNUM':null}]"
sResp = Replace(sResp, "'", Chr(34))
Dim data As Object, rec As Object, key, fields, arData
Dim n As Long, m As Long, i As Long, j As Long
Set data = JsonConverter.ParseJson(sResp)
fields = data(1).Keys
m = UBound(fields) + 1
n = data.Count ' records
' fill array
ReDim arData(1 To n, 1 To m)
For i = 1 To n
For j = 1 To m
arData(i, j) = data(i)(fields(j - 1))
Next
Next
' dump array
With Sheet1
.Range("A1").Resize(1, m) = fields ' header
.Range("A2").Resize(n, m) = arData
End With
End Sub
I was wrong. The converter also accepts square brackets as the first element, a collection. The suggested additional brackets would not work either, because they create an invalid JSON.
Now CDP1802 has already answered, but I also looked for a correct solution because of my wrong statement (I deleted it so no one else would see it as the truth):
Sub TestJSON()
Dim sResp As String
Dim json As Collection
Dim col As Dictionary
Dim key As Variant
Dim dicts As Long
Dim row As String
sResp = "[{""CD"":151,""nID"":111,""sNM"":""PNAME1"",""GDR"":""MA"",""RGN"":""MM"",""reID"":1,""status"":""RSB"",""NTY"":""EG"",""CLNUM"":1,""CLNM"":""THIR"",""YER"":2022,""SCHOD"":1718,""STID"":2,""THNM"":""BRYYY"",""SCHNO"":""HTBAN"",""rCD"":6,""schooL_TYPE"":1,""SGT"":1,""CLCD"":3,""NG1"":1,""NG2"":2,""YCOD"":null,""general"":10,""special"":1,""naT_ID"":1,""GDR_ID"":1,""sGCOD"":8,""sTTY"":1,""obNM"":""NTF"",""obID"":0,""STYN"":""NTHMY"",""PSNUM"":null},{""CD"":153,""nID"":222,""sNM"":""ALIIKK"",""GDR"":""MA"",""RGN"":""MM"",""reID"":1,""status"":""RSB"",""NTY"":""EG"",""CLNUM"":1,""CLNM"":""THIR"",""YER"":2022,""SCHOD"":1718,""STID"":2,""THNM"":""SYYYYY"",""SCHNO"":""HTBAN"",""rCD"":6,""schooL_TYPE"":1,""SGT"":1,""CLCD"":3,""NG1"":1,""NG2"":2,""YCOD"":null,""general"":10,""special"":1,""naT_ID"":1,""GDR_ID"":1,""sGCOD"":8,""sTTY"":1,""obNM"":""NTF"",""obID"":0,""STYN"":""NTHMY"",""PSNUM"":null}]"
Set json = JsonConverter.ParseJson(sResp)
For dicts = 1 To json.Count
Set col = json(dicts)
For Each key In col.Keys()
'Debug.Print key & ":" & col(key) & ", "
row = row & col(key) & Chr(9)
Next key
Debug.Print row
row = ""
Next dicts
End Sub

Disregarding Value of Zero on a Cell when Comparing for > 0

would like to ask if there is a way to disregard 0 value when comparing?
to be specific i need to compare a value greater than zero if they are equal..and if so, a msgbox "" will appear ... to be honest i am not knowledgeable so i just used if statement... and need to compare 7 cells... i have to redundantly use if then statement for ever possible 0 and non zero combinations. (which ofc. alot)
example all zeroes will be neglected, if for instance cellA = 2 and CellB = 0 and CellC = 2 then it will show a msgbox because cellA an cellC is > 0 and have the same value(which is correct). but when i turned cellA = 0 and CellB = 0 and Cell3 = 0 ...it still showing a msgbox.. i was hoping that when all the cells turned 0 it will neglect the condition "if A = B, B = C then appear msgbox" but will consider the condition if it is > 0.
Advance thanks
This will do it:
Option Explicit
Sub msgBx()
'Set some vars
Dim sourceArr, dict As Object, j As Long, i As Long
'get data in memory = array
sourceArr = Sheet1.Range("A1:A7").Value2
'build a dict to compare quickly
Set dict = CreateObject("Scripting.Dictionary") 'create dictionary lateB
For j = 1 To UBound(sourceArr, 2) 'traverse source
dict(sourceArr(1, j)) = Empty
Next j
'check if key exists in dict and copy data
For j = 1 To UBound(sourceArr)
For i = 1 To UBound(sourceArr, 2)
If sourceArr(j, i) <> 0 And dict.Exists(sourceArr(j, i)) Then
MsgBox "gotya"
End If
Next i
Next j
End Sub

find max/min value in dictionary values in VBA

I am using VBA to store key and value associated with the key into dictionary.
Sub Dict_Example()
Set dict = CreateObject("Scripting.Dictionary")
For i = 1 to 5
dict.Add i, some number
Next i
I would like to find the highest value and its associated key in dict.
For example, if dict = {1: 5, 2: 10, 3: 6, 4: 11, 5: 3} where 1,2,3,4,5 are keys and 5, 10, 6, 11, 3 are values then it should return 4:11.
How do I do this in VBA?
I would generate an array from the dict.items and use Max/Min function on that. Then loop keys and compare items against that.
Option Explicit
Public Sub Dict_Example()
Dim dict As Object, max As Long, min As Long, arr(), key As Variant, i As Long
Set dict = CreateObject("Scripting.Dictionary")
For i = 1 To 5
dict.Add i, i * Application.WorksheetFunction.RandBetween(0, 100)
Next i
max = Application.max(dict.items)
min = Application.min(dict.items)
For Each key In dict
Debug.Print key, dict(key)
If dict(key) = max Then Debug.Print "max= " & dict(key) & vbTab & "key= " & key
Next
Stop
End Sub
You could accomplish this by using a couple arrays to temporarily store your high and low values and associated keys as you iterated through the dictionary like this:
Sub test()
Dim dict As New Dictionary
Dim low(1 To 2)
Dim high(1 To 2)
Dim i As Long
Dim key
For i = 1 To 5
dict.Add i, 'some number
Next i
low(1) = dict.Keys(0)
low(2) = dict(dict.Keys(0))
high(1) = dict.Keys(0)
high(2) = dict(dict.Keys(0))
For i = 0 To dict.Count - 1
If dict(dict.Keys(i)) < low(2) Then
low(1) = dict.Keys(i)
low(2) = dict(dict.Keys(i))
ElseIf dict(dict.Keys(i)) > high(2) Then
high(1) = dict.Keys(i)
high(2) = dict(dict.Keys(i))
End If
Next i
Debug.Print low(1) & ":" & low(2) & vbCrLf & high(1) & ":" & high(2)
End Sub
But sorting like this would only work correctly for numeric values. #Ryan Wildry's comment is the way to go for generally sorting a dictionary, then you would grab your values using dict(dict.Keys(0)) and dict(dict.Keys(dict.Count - 1)) respectively where dict references your sorted dictionary.
EDIT:
You'll need to add a library reference to the Microsoft Scripting Runtime for this to work.

Adding a table of data to a dictionary excel vba

I'm brand new to using dictionaries and could do with a bit of help. I've got a table of data in range A1:C4
A B C
1 4 7
2 5 8
3 6 9
Is there any way of adding this tables directly into a dictionary?
Thanks in advance
Dim d As Scripting.Dictionary
Dim r As Excel.Range
Dim c As Excel.Range
Set d = New Scripting.Dictionary
Set r = Range("a1:c4")
For Each c In r.Cells
d.Add CStr(c.Address), c.Value
Next c
I think what you're probably looking for is a Multidimensional Array
A standard Array will hold a series of values in a list, and the value of any point in this list can be referenced, for example:
myArray = Array("One", "Two", "Three")
'The first value in an array is at position 0 unless otherwise specified
MsgBox myArray(0) 'Will open a message box with the value "One"
MsgBox myArray(1) 'Will open a message box with the value "Two"
MsgBox myArray(2) 'Will open a message box with the value "Three"
Whereas a standard array is one dimensional, using a Multidimensional Array allows you to add more than one dimension to this list. Put simply a two dimensional array will let you create a table of data.
dim myArray(1 to 3, 1 to 3) as Variant will create a two dimensional array, by also specifying '1 to 3' will allocate a set size and range of items that can be referenced in the array. Take for example this table:
A    B    C
D    E    F
G    H    I
To put this into a multidimensional array would be the following
Dim myArray(1 To 3, 1 To 3) As Variant
myArray(1, 1) = "A"
myArray(1, 2) = "B"
myArray(1, 3) = "C"
myArray(2, 1) = "D"
myArray(2, 2) = "E"
myArray(2, 3) = "F"
myArray(3, 1) = "G"
myArray(3, 2) = "H"
myArray(3, 3) = "I"
MsgBox myArray(2, 2) 'Will open a message box with the value "E"
Given you are looking to produce this from Range("A1:C4") you could use a loop to go through each cell create this:
Dim myArray(1 To 4, 1 To 3) As Variant
For Each c In Range("A1:C4")
myArray(c.Row, c.Column) = c.Value
Next c

Recursively print next dictonaries in VBA

I'd like to print nested dictionaries in VBA. Basically I have a Dictionary where every key is a String, but each value can either be a String or another Dictionary.
Say my Dictionary has got the value
{ "FOO" => "BAR" , "HELLO" => { "WORLD => ":)", "OTHER" => ":(" } }
I want to display in an Excel spreadsheet:
FOO |BAR |
HELLO|WORLD|:)
HELLO|OTHER|:(
My issue is that I need to find a way to guess what is the type of the value under each key, so when I call dict("HELLO") I can either display the value if it's a string or if it's a dictionary call the same function again.
In order to do so I need to know:
if there is a way to know what is the type of a value stored in a dictionary
if there is a way to cast that value to a target type (string or dictionary)
So here is what I've tried
Function display_dictionary(dict As Scripting.Dictionary, out As Range) As Integer
Dim vkey As Variant
Dim key As String
Dim row_offset As Integer
Dim value As Object
Dim svalue As String
Dim dvalue As Dictionary
Dim each_offset As Integer
row_offset = 0
For Each vkey In dict.Keys()
key = vkey
Set value = dict(key)
if value is String then
svalue = ???
out.offset(row_offset, 0).value = key
out.offset(row_offset, 1).value = svalue
row_offset = row_offset + 1
else if value is Dictionary
dvalue = ???
each_offset = display_dictionary(dvalue, out.offset(row_offset, 1))
For each_row = 0 To each_offset - 1
out.offset(row_offset + each_row) = key
Next
row_offset = row_offset + each_offset
End If
Next
End
I am actually going to propose a bit different way for displaying the results. I think it's more logical but you are welcome to modify it to suit your specific needs. Just as a hint print logical tree of nodes, like below, and then manipulate the results if ever needed.
So the tree would look like this for example (Note I added one more depth level)
and the code to reproduce
Private i As Long
Private depth As Long
Sub Main()
Cells.ClearContents
Dim dict As New Dictionary
Dim subDict As New Dictionary
Dim lvlDict As New Dictionary
lvlDict.Add "LVL KEY", "LVL ITEM"
subDict.Add "HELLO", ":)"
subDict.Add "WORLD", ":("
subDict.Add "OTHER", lvlDict
dict.Add "FOO", "BAR"
dict.Add "BOO", subDict
i = 1
depth = 0
TraverseDictionary dict
Columns.AutoFit
End Sub
Private Sub TraverseDictionary(d As Dictionary)
For Each Key In d.Keys
Range("A" & i).Offset(0, depth) = "KEY: " & Key
If VarType(d(Key)) = 9 Then
depth = depth + 1
TraverseDictionary d(Key)
Else
Range("B" & i).Offset(0, depth) = "ITEM: " & d(Key)
End If
i = i + 1
Next
End Sub
and spreadsheet result:
To get the variable type or its type name you can use this:
Debug.Print TypeName(dict("HELLO")), VarType(dict("HELLO"))

Resources