Excel VBA - How to search array and return value? - arrays

I have some data that looks like this (starting in A1):
Batman
Carl Reiner
Ford
Sushi
But I have a two dimensional array, storing those names, and their type:
Dim Names()
ReDim Names(4,1)
Names(0,0) = "Batman"
Names(0,1) = "Superhero"
Names(1,0) = "Carl Reiner"
Names(1,1) = "Comedian"
Names(2,0) = "Ford"
Names(2,1) = "Car Manufacturer"
Names(3,0) = "Sushi"
Names(3,1) = "Food"
Now, I would like to loop through column A and replace those data points with their equivalent type. I thought there would be a relatively simple way to essentially search Names(x,0) for "Batman" then just replace it with Names(x,1). What's the best way to do so?
I found somewhere that you could possibly use Vlookup with this, so I tried this, to no avail:
Evaluate(WorksheetFunction.Vlookup("A1",Names,1,False)
With my thought being it would look for "Batman" and return the value in Names in the "1" dimension...But that keeps giving me an error (Type 13 Mismatch).
Thanks for any ideas/help!
edit: I found this post and seem to be getting somewhere with Application.Match("A1",Application.Index(Names,0,1),0) which returns the position of "A1" in the array. Now I just need to return the offset and get the "1" position of that...
Edit 2: I think I got it. Using MATCH to find the position of "A1" then just use the array to find it:
Names(Application.Match("A1",Application.Index(Names,0,1),0),1)
So, when A1 is Batman, it finds that position in the index, then it finds the equivalent return in the second dimension.
(Sorry for the lack of semantics, I don't have a very good idea of how to refer to this, so please let me know if I can clarify).

It seems like this would be easier using a Dictionary.
Sub testing()
Dim dic As Object
Set dic = CreateObject("scripting.dictionary")
With dic
.Add "Batman", "Superhero"
.Add "Carl Reiner", "Comedian"
.Add "Ford", "Car Manufacturer"
End With
MsgBox "Batman is a " & dic.Item("Batman")
End Sub

I think if you want to use worksheet functions, you have to put the data into a worksheet (e.g. a column or pair of columns) instead of an array. This is pretty easy to do, though, and you can use a different worksheet, or even another workbook, and there are solutions to hide these, so your users don't have to see that data cluttering the other worksheets.

Related

Can't copy a value from one worksheet over to an array in another worksheet

In the same workbook, I've got two worksheets: Model and Results.
My goal is to copy the value of a cell in Model (for e.g., F8) over to a cell in an array (c4 to I23) in Results called ResultsArray (see code below).
When I run my module, no error appears, but the code doesnt seem to work either (the value of F8 doesnt get copied over to the specified cell in ResultsArray).
Appreciate any help.
Tried running different variations of the code below
Sub CopyTest()
Dim ResultsArray As Variant
ResultsArray = Worksheets("Results").Range("C4:I23")
ResultsArray(1, 1) = Worksheets("Model").Range("F8").Value
End Sub
I'm using ResultsArray(1,1) because I am hoping to introduce a loop into the code to populate cells in the array based on the loop counter, e.g., ResultsArray(loopcounter,1)
So turns out I just needed to add "Set" in the 2nd line before "ResultsArray" when assigning the range from the worksheet "Model" to it:
Sub CopyTest()
Dim ResultsArray As Variant
Set ResultsArray = Worksheets("Results").Range("C4:I23")
ResultsArray(1, 1) = Worksheets("Model").Range("F8").Value
End Sub
I've tested this addition and it works

Create reference table or array inside of the code

I am trying to create a function that allows me to put in a location number and the result will give me a unique location code. The problem is I want all of the referencing done inside of the macro code, not to get the information from somewhere in the spreadsheet. (this code is going into an add-in so there is no worksheet to reference from). I basically want to do a vlookup but inside of the code, not in a worksheet.
I haven't been able to find out how to do this, the code below is something like what I am looking for, I am thinking maybe the use of an array but I can't figure out how to use it the way I want.
I know this doesn't work but I am trying to do something like this below so that when I type in =GetCode(415) the result is 001
Function GetCode(LocationNum As String) As String
Dim Result As String
'Built in reference table
'
'{ "415" : "001"
' "500" : "002"
' "605" : "003"
' }
Dim varData(2) As Variant
varData("415") = "001"
varData("500") = "002"
varData("605") = "003"
Result = varData(LocationNum)
GetCode = Result
End Function
As Nathan_Sav has already mentioned, you can use a collection or dictionary instead, which are much more efficient. Here's an example using the dictionary object. Note that it uses early binding, so you'll need to set a reference to the Microsoft Scripting Runtime library (Visual Basic Editor >> Tools >> Reference).
Option Explicit
Sub test()
'set a reference (VBE >> Tools >> Reference) to the Microsoft Scripting Runtime library
'declare and create an instance of the dictionary object
Dim dic As Scripting.Dictionary
Set dic = New Scripting.Dictionary
'set the comparison mode for the dictionary to a case-insensitive match
dic.CompareMode = TextCompare
'add keys and associated items to the dictionary
dic.Add Key:="415", Item:="001"
dic.Add Key:="500", Item:="002"
dic.Add Key:="605", Item:="003"
'print to the immediate window the item associated with the specified key
Debug.Print dic("415")
'clear from memory
Set dic = Nothing
End Sub

Is there a way to make an empty array and growing it as it gets data in vb.net?

I have been working on a project, and am attempting to make a new array for data. I have tried making an empty array with Dim Name() As String = {}. I am using a ListView, and the way I have done it there are blank spots where I have gotten rid of data. This is my current code:
Sub English(ByVal Country() As String, ByVal Language() As String)
rbDisplayallData.Checked = False
lstResults.Visible = True
lstResults.Items.Clear()
lstResults.Columns.Clear()
With lstResults
.View = View.Details
.Columns.Add("English Speaking Countries", 200, HorizontalAlignment.Left)
End With
For i = 0 To 181
Dim EnglishSpeakingCountries(i) As String
If Language(i) = "English" Then
EnglishSpeakingCountries(i) = Country(i)
End If
lstResults.Items.Add(New ListViewItem({EnglishSpeakingCountries(i)}))
Next
End Sub
I am trying to get rid of these spaces.
I Was thinking if I were to compact the array or make a new one with the same data going into a new array it would fix the issue.
If you have a solution please let me know.
There are two things that could be considered an empty array
An array with no elements, i.e. a Length of zero.
An array where every element is Nothing.
All arrays are fixed-length. Once you create an array with a particular number of elements, it always has that number of elements. You can use ReDim Preserve or Array.Resize but, in both those cases, what actually happens is that a new array is created and the elements copied from the old array. The new array is assigned to the same variable but anywhere the old array is referenced, it will still have that same number of elements. Try running this code to see that in action:
Dim a1 As String() = {}
Dim a2 As String() = {"First", "Second", "Third"}
Dim b1 = a1
Dim b2 = a2
Console.WriteLine(a1.Length)
Console.WriteLine(a2.Length)
Console.WriteLine(b1.Length)
Console.WriteLine(b2.Length)
Console.WriteLine()
ReDim Preserve a1(2)
Array.Resize(a2, 6)
Console.WriteLine(a1.Length)
Console.WriteLine(a2.Length)
Console.WriteLine(b1.Length)
Console.WriteLine(b2.Length)
Console.ReadLine()
Output:
0
3
0
3
3
6
0
3
As you'll be able to see, a1 and a2 end up referring to new arrays with the specified lengths but the original arrays with the original lengths still exist and are still accessible via b1 and b2.
If you start with an array with no elements then you can use ReDim Preserve or Array.Resize to give the appearance of resizing the array but that's not really what's happening and that should generally be avoided. If you know how many elements you'll end up with then you could create an array of that size and then set each element in turn. You'd need to keep track of the next element index though, so that's still a bit tedious.
Generally speaking, if you want an array-like data structure but you want it to be able to grow and shrink as required, you should use a collection. The most common collection is the List(Of T), where T is any type you care to specify in your code. If you want to store String objects then use a List(Of String). You can call Add to append a new item to the end of the list, as well as Insert, Remove and RemoveAt methods. You can also get or set an item by index, just as you can do for array elements.
Note that a List(Of T) actually uses an array internally and uses the aforementioned method of "resizing" that array. It optimises the process somewhat though, which makes the code easier for you to write and large collections more efficient to use.
It's worth noting that, in your own code, the Columns and Items properties of your ListView are both collections, although they are slightly different to the List(Of T) class.
Looking at your original code, this:
For i = 0 To 181
Dim EnglishSpeakingCountries(i) As String
If Language(i) = "English" Then
EnglishSpeakingCountries(i) = Country(i)
End If
lstResults.Items.Add(New ListViewItem({EnglishSpeakingCountries(i)}))
Next
could be changed to this:
Dim englishSpeakingCountries As New List(Of String)
For i = 0 To 181
If Language(i) = "English" Then
englishSpeakingCountries.Add(Country(i))
lstResults.Items.Add(Countries(i))
End If
Next
Note that you're just adding items to two collections. I guess the question is whether you actually need this extra collection at all. If you do want to use it later then you need to assign it to a member variable rather than a local variable. If you don't need it later then don't create it at all. As I said, you're already adding items to a collection in the ListView. Maybe that's all you need, but you haven't provided enough info for us to know.

How to parse and address a json matrix array in Excel-VBA?

I need to parse JSON text to a JSON object in Excel-VBA. The JSON text includes a matrix/array. Then I need to address it (set a VBA variable to the value).
My code had been working parsing a nested/keyed JSON text with "JsonConverter.parseJSON" method. But I do not know how to address new array object (or technically if the "parse" is working correctly.
Dim jsonResults As String
Dim jsonObj As Dictionary
Set travelDist As Number
Set jsonResults = '{"distances":[[0,97641],[97415,0]],"times":[[0,4189],[4183,0]],"weights":[[0.0,5653.726],[5644.176,0.0]],"info":{"copyrights":["GraphHopper","OpenStreetMap contributors"]}}'
Set jsonObj = JsonConverter.ParseJson(jsonResults) \This worked with the old JSON text keyed value structure.
travelDist = VBA.Val(jsonObj.Item("distances")(1)) \This DOESN'T work. It worked with Keyed Object Values. The goal is to set travelDist to in this example, 97641.
The current code seems to have a type mismatch.
The goal is to set a VBA variable to 97641. Please let me know how to include required files/definitions etc. if the solution is including additional types or methods.
There's no reason to declare jsonObj as a dictionary and unless Number is a well defined user-defined type of some sort, I don't think declaring travelDist as Number will work. Use Double instead. And always use Option Explicit on the very top.
Also the double quotes in the JSON string need to be escaped somehow. You can either double them:
jsonResults = "{""distances"":[[0,97641],[97415,0]],""times"":[[0,4189],[4183,0]],""weights"":[[0.0,5653.726],[5644.176,0.0]],""info"":{""copyrights"":[""GraphHopper"",""OpenStreetMap contributors""]}}"
or replace them with single quotes:
jsonResults = "{'distances':[[0,97641],[97415,0]],'times':[[0,4189],[4183,0]],'weights':[[0.0,5653.726],[5644.176,0.0]],'info':{'copyrights':['GraphHopper','OpenStreetMap contributors']}}"
or you can just store the string in a cell in one of your worksheets and load it from there:
Dim sht As Worksheet
Set sht = ThisWorkbook.Worksheets("Name of your Worksheet")
Set jsonObj = JsonConverter.ParseJson(sht.Range("A1"))
Visualizing the JSON structure might help you understand it better:
So basically what you need to do is access the 2nd item, of the 1st item, of the distances array/collection, keeping in mind that the 1st item of the distances array is also an array/collection itself.
The way to do this would be the following:
Option Explicit
Sub test()
Dim jsonObj As Object
Dim jsonResults As String
Dim travelDist As Double
jsonResults = "{""distances"":[[0,97641],[97415,0]],""times"":[[0,4189],[4183,0]],""weights"":[[0.0,5653.726],[5644.176,0.0]],""info"":{""copyrights"":[""GraphHopper"",""OpenStreetMap contributors""]}}"
Set jsonObj = JsonConverter.ParseJson(jsonResults)
travelDist = jsonObj("distances")(1)(2)
Debug.Print travelDist 'the result is printed in the immediate window
End Sub
Finally, I assume that since you've used this before, you know you need to add this JSON parser to your project, as well as a reference to Microsoft Scripting Runtime (VBE>Tools>References>...)

Autofilter Criteria with Array

I have been looking to find a solution and cant find anything online that fully explains what is going on. I looked at some other posts but they all seem to fall a bit short.
When I run this bit of code it works perfectly (as it was recorded).
ActiveSheet.Range("$A$1:$AL$1002").AutoFilter Field:=17, Criteria1:=Array( _
"73578", "78759", "78765"), Operator:=xlFilterValues
But then when I try to make it more robust it fails. I want to change the Criteria1 argument to an already stored array.
I am trying to get the following to work.
ActiveSheet.Range("$A$1:$AL$1002").AutoFilter Field:=17, _
Criteria1:=Array(StoredArray.Values), Operator:=xlFilterValues
I have the array stored and I manipulate it anyway but I have yet to get anything to work. I have also tried to create a string to be exactly like the recorded macro but that does not work either.
Dim StoredArrayString as Variant
StoredArrayString = "73578"", ""78759"", ""78765"
ActiveSheet.Range("$A$1:$AL$1002").AutoFilter Field:=17, _
Criteria1:=StoredArrayString, Operator:=xlFilterValues
Thanks for the help here I have spent lots of time on MSDN trying to research this issue but can't find a solution.
I think your issue is with how you're defining your array. Try this instead.
Dim StoredArrayString As Variant
StoredArrayString = Array("73578", "78759", "78765")
ActiveSheet.Range("$A$1:$AL$1002").AutoFilter Field:=17, _
Criteria1:=StoredArrayString, Operator:=xlFilterValues
Starting with:
and run:
Sub Macro7()
Dim ary(1 To 3) As String
ary(1) = "Alice"
ary(2) = "Boris"
ary(3) = "James"
ActiveSheet.Range("$A$1:$D$22").AutoFilter Field:=3, Criteria1:=ary, Operator:=xlFilterValues
End Sub
will produce:

Resources