Pre defining multi dimensional array in Excel VBA - vba

I know we can define single dimension array in excel VBA using the following
GroupCols = Array("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L")
How can you predefine multi- dimensional array in the same manner?
Also I want to keep track of certain levels in the following manner
Level[16][0]
Level[16][1]
Level[16][2]
Level[8][0]
Level[8][1]
Level[8][2]
Level[7][0]
Level[7][1]
Level[7][2]
The first index defines the level and so may not be consecutive...like after 16 there is straight 8 and so on. For each i need 3 info which is 0,1,2 second indexes.
Can anyone guide me on how to achieve the same in excel VBA?

There is a way to define a 2D array by using evaluate() almost like using array() for 1D:
Sub Array2DWithEvaluate()
Dim Array2D As Variant
'[] ist a shorthand for evaluate()
'Arrays defined with evaluate start at 1 not 0
Array2D = [{"1,1","1,2","1,3";"2,1","2,2","2,3"}]
Debug.Print Array2D(2, 2) '=> 2,2
End Sub
If you want to use a string to define the array you have to use it like this
Sub Array2DWithEvaluateFromString()
Dim strValues As String
Dim Array2D As Variant
strValues = "{""1,1"",""1,2"",""1,3"";""2,1"",""2,2"",""2,3""}"
Array2D = Evaluate(strValues)
Debug.Print Array2D(2, 2) '=> 2,2
End Sub
If you want more info about other uses of the function Evaluate() check this great post.
http://www.ozgrid.com/forum/showthread.php?t=52372

You can't have non-consecutive indices in an array like that. If you do only use a non-consecutive subset of the indices, then all the other elements will be empty but still use up storage space, which is both inefficient and error-prone (LaunchMissile = Levels(17,1), whoops!).
What you're looking for is the Dictionary object. Before use, must set reference as follows: Tools > References > check Microsoft Scripting Runtime.
Example:
Dim Levels As Scripting.Dictionary
Set Levels = New Scripting.Dictionary
' Fill up the dictionary
Levels.Add Key:=16, Item:=Array("A", "B", "C")
Levels.Add Key:=8, Item:=Array("FAI", "CNT", "YES")
Levels.Add Key:=7, Item:=Array("Once", "Twice", "Thrice")
' Retrieve items from the dictionary
Debug.Print Levels.Item(8)(0)
Debug.Print Levels.Item(8)(1)
Debug.Print Levels.Item(8)(2)
Note that a Collection object could also do the trick. Advantage: native to VBA, so no need to set reference. Disadvantage: Key is write-only, which can be quite awkward.

You could use Array(Array()) for e.g.
data = Array(Array(1,2), Array(3,4))
To refer to the first element, use data(0)(0)
(copied from here)

There is no way to declare and populate a two-dimensional array like when you use the array() function. You can use ReDim to define the array dimensions, then populate it. Here's an example:
ReDim myArray(0 To 16, 0 To 3)
myArray(0, 0) = "A"
myArray(0, 1) = "B"
...
Unfortunately, when you create an array, an element will be created for each entry from the lower bound to upper bound. They will be empty, but they are there, so you need to be aware of it, especially if you plan to loop through it.

Related

Detect changes in array

Let's assume we have an array defined like the following:
Dim Data1 As Object()
Dim Data2 As Double()
I would like to find a way to manage changes in an Array, such as:
//Example #1
Redim Data2(5)
//Example #2
Data2(0) = 3.14
My idea was to create a CustomArray(of T), which inherits from System.Array, and raise an event at the right time. Unfortunately, however, the System.Array class cannot be inherited...
I know that for this kind of operations it would be better to use other data structures (ObservableCollection), but I need to keep the arrays functions (for example Redim and Ubound): by creating a new class (with all array functions), I would still not be able to use Ubound, which requires a System.Array.
Is there a solution?
Ubound and Redim can be simulated. A short example,
Dim foo As New List(Of String) From {"A", "B", "C", "Z"}
Dim uBnd As Integer = foo.Count - 1 ' UBound
' redim - grow
foo.AddRange({"d", "e", "f", "x"})
' redim - decrease
foo.RemoveRange(1, 3)

Declaring Array() in VBA-ACCESS does not work without upper limit

I am learning about declaring arrays. It works when I declare it by giving an upper limit using following code:
Dim arrayA(5) as String
I check it by assigning a random value:
arrayA(0) = 1
MsgBox arrayA(0)
MsgBox responds by giving a value of 1.
However, my actual intention is to create a dynamic array that I defined as below:
Dim arrayA() as String
I test it in the same way
arrayA(0) = 1
MsgBox arrayA(0)
But this time it does not work and MsgBox pops up empty. Would someone tell me if I need to load some libraries to work with dynamic array?
Arrays in VBA need to be initialized before they are used.
You can initialize an array with a Redim statement:
Dim arrayA() as String
Dim i As Integer
i = 0
Redim ArrayA (0 To i)
arrayA(0) = "1" 'String
MsgBox arrayA(0)
Alternatively, there are functions that return an initialized array. In that case, Redim is not needed as the initialization happens in the external function. You do need to make sure you match type with the array being returned, though, and the overhead is the same or more.
Dim arrayA() as Variant
arrayA = Array(1)
MsgBox arrayA(0)
You can declare an array without a limit, but must redim the array to the desired limit prior to using it:
Dim myArray() As Long
Redim myArray(0)
myArray(0) = 0 'etc...
So, you cannot use a "dynamic" array in VBA like this.
The best reference I've ever known for arrays (and much other VB/A related information), comes from the late Chip Pearson: http://www.cpearson.com/Excel/VBAArrays.htm

Creating a new String Array from another

I have an array of integers (dfArray) and would like to create a new second String array which consists of the integers and appending "G" to the beginning. What's the best way to go about this? I was thinking of For Each but couldn't get it to work. Thanks in advance.
Set dfArray = [dff]
Set dfArray2 = ["G" & dff] 'incorrect but you get the idea?
Dim dfArray() As Variant
Dim dfArray2() As String
dfArray = [dff].Value
ReDim dfArray2(UBound(dfArray)) As String
Dim i As Double
For i = 1 To UBound(dfArray) Step 1
dfArray2(i) = "G" & dfArray(i, 1)
Next i
Anyways, from my personal point of view, I don't like to asign a complete Range into Array, only if needed. I prefer to loop using Lbound or Ubound and control the array all the time. To asign a range into an Array, you need the Array variable to be Variant type, and also, you can't use Preserve easily. Check this question for more info
Issues about Variant Arrays

VB.NET Multi-Dimentional Array

Alright, so I'm used to PHP where I can declare a multi-level array like $something[0][1] = "test";. I need to be able to accomplish the same thing, but I'm using VB.NET. How would I do this?
And sorry if this isn't what a multi-dimentional array is, I might be wrong at what it's called but that's what I want to do.
Thanks!
Multidimensional array in VB.Net...
Dim twoDimensionalArray(10, 10) As String
twoDimensionalArray(0, 1) = "test"
I rarely use arrays, however. More elegant solutions can typically be achieved using Lists, Dictionaries, or combinations of the two.
Update .
The (10, 10) is the upper bound of the array (the size is actually 11, 0 through 10). If you don't specify the bounds, you have to Redim Preserve the array when you want to add to it. That's one good thing about lists, you don't have to specify an initial size and you can add to them freely.
Here's a quick example of a list of lists.
Dim listOfLists As New List(Of List(Of String))
listOfLists.Add(New List(Of String)(New String() {"a", "b", "c"}))
listOfLists.Add(New List(Of String)(New String() {"d", "e", "f"}))
listOfLists.Add(New List(Of String)(New String() {"g", "h", "i"}))
'listOfLists(0)(0) = "a"
'listOfLists(0)(1) = "b"
'listOfLists(2)(1) = "h"
Just a plain sample with dynamic resizing of the array
Dim arr(0)() As String '** array declaration
For i As Integer = 0 To 100 '** Outer loop (for the 1st dimension)
For j As Integer = 0 To 1 '** inner loop (for the 2nd dimension)
ReDim Preserve arr(i) '** Resize the first dimension array preserving the stored values
ReDim Preserve arr(i)(j) '** Resize the 2nd dimension array preserving the stored values
arr(i)(j) = String.Format("I={0},J={1}", i, j) '** Store a value
Next
Next
In .NET Arrays are usually static and won't be automatically resized. (As for example in Javascript etc.) Therefore it's necessary to manually resize the array each time you want to add a new item, or specify the size at the beginning.

For Each loop on a 2D array in VB.NET

I'm writing a loop to go through the first array of a 2D loop, and I currently have it like this:
For Each Dir_path In MasterIndex(, 0)
'do some stuff here
Next
But it's giving me an error, saying it expects an expression in the first field. But that's what I'm trying to do, loop through the first field. How do I fix this? What would I put in there?
EDIT: to clarify, I'm specifically looking for the 0th element in the subarray of each array, that's why that second field is constantly 0.
You can accomplish this with nested For loops
Note: When using a For Each loop to iterate over elements in an array, the placeholder generated on each iteration is a copy of the value in the actual array. Changes to that value will not be reflected in the original array. If you want to do anything other than read the information you will need to use a For loop to address the array elements directly.
Assuming a two dimension array the following code example will assign a value to each element in each dimension.
Dim MasterIndex(5, 2) As String
For iOuter As Integer = MasterIndex.GetLowerBound(0) To MasterIndex.GetUpperBound(0)
'iOuter represents the first dimension
For iInner As Integer = MasterIndex.GetLowerBound(1) To MasterIndex.GetUpperBound(1)
'iInner represents the second dimension
MasterIndex(iOuter, iInner) = "This Isn't Nothing" 'Set the value
Next 'iInner
'If you are only interested in the first element you don't need the inner loop
MasterIndex(iOuter, 0) = "This is the first element in the second dimension"
Next 'iOuter
'MasterIndex is now filled completely
You could optionally use the .Rank property to dynamically iterate over each dimension
If you want to loop over a jagged array like Konrad Rudolph was suggesting (This functionally more closely matches array implementations in other more loosely typed languages like PHP)you could go about it like so:
'This is a jagged array (array of arrays) populated with three arrays each with three elements
Dim JaggedIndex()() As String = {
New String() {"1", "2", "3"},
New String() {"1", "2", "3"},
New String() {"1", "2", "3"}
}
For Each aOuter As String() In JaggedIndex
'If you are only interested in the first element you don't need the inner for each loop
Dim sDesiredValue As String = aOuter(0) 'This is the first element in the inner array (second dimension)
For Each sElement As String In aOuter
Dim sCatch As String = sElement 'Assign the value of each element in the inner array to sCatch
sElement = "This Won't Stick" 'This will only hold value within the context of this loop iteration
Next 'sElement
Next 'aOuter
'JaggedIndex is still the same as when it was declared
You simply can’t. Multi-dimensional arrays aren’t really supported in the .NET framework infrastructure. They seem to be tagged on as an afterthought. The best solution is often not to use them, and to use jagged arrays instead (arrays of arrays – Integer()() instead of Integer(,)).
You can use Enumerable.Range recursively to iterate the dimensions of an array.
Lets say we have a two dimensional grid (rows and columns) of Int.
We can iterate it as follows:
using System.Linq;
[TestMethod]
public void TestTwoDimensionalEnumeration()
{
int rowcount = 9;
int columncount = 9;
int[,] grid = new int[rowcount, columncount];
var enumerated =
Enumerable.Range(0, rowcount - 1).
SelectMany(ri => Enumerable.Range(0, columncount - 1).
Select(ci => new {
RowIndex = ri,
ColumnIndex = ci,
Value = grid[ri,ci]
}));
foreach (var item in enumerated)
{
System.Diagnostics.Trace.WriteLine("Row:" + item.RowIndex +
",Column:" + item.ColumnIndex +
",Value:" + item.Value);
}
}
The same logic can be applied to any number of dimensions.