I am trying to populate different portions of an array based on different locations within a spreadsheet.
For example
Dim prices(50) as double
prices(0, 1, 2) = Range("i35", "i38")
prices(3,4,5,6,7,8) = Range("b7","b12")
I'm getting a wrong number of dimensions error, because I'm assuming you have to populate the whole thing at once. Is there another syntax to do this better, or is it just a limitation of VBA?
Possibly a for each loop? But that doesn't really help since there are a lot of different ranges.
Thanks for your help
It looks like you are trying to tell VBA that the list of numbers in the parenthesis of the array are positions within an array that will receive multiple items.
That's not what it is. Instead, you can only do one at a time. For example:
Dim prices(50) as double
' My range syntax might be wrong here.
prices(0) = Range("i35", "i35")
prices(1) = Range("i36", "i36")
prices(2) = Range("i37", "i37")
prices(3) = Range("i38", "i38")
..etc. Or, if you're new to programming, you'll find this really cool:
for CounterVariable = 0 to 3
prices(CounterVariable) = Range("i" & 35 + CounterVariable)
next CounterVariable
To Wit ...
an array defined like Dim prices(50) will have one 'dimension', like a straight line.
Picture one row in a spreadsheet, 50 columns long.
If you said Dim prices(50,60) you would have a 2-dimensional array, like a cartesian plane (x,y) or a 50x60 cell spreadsheet where the first number is like the letters, the second, like the numbered rows.
Some languages support 3-dimensional arrays - VariableName(x,y,z). Don't know if VBA does
Therefore, using commas in parenthesis after an array is supported by VBA
BUT it means something completely different than you were hoping (and you can't change that): the values for the dimensions.
So your last line of code is looking for some value in a 6-dimensional universe! So we've got, height, width, depth, time, tesseract, and uh, you've beat me there!!!
Related
A truncated version of my data is in the form shown in the screenshot below: three columns of 5 unique names. The names appear in any order and in any position but never repeat in a single row.
My goal is to create an array that contains the number of times Adam appears in each row. I can fill down the formula=countif(A2:C2,$I$2) in a new column, or if I write the array manually for each row, it looks like:
={countif(A2:C2,$I$2);countif(A3:C3,$I$2);countif(A4:C4,$I$2);countif(A5:C5,$I$2);countif(A6:C6,$I$2)}
Where cell I2 contains "Adam". Of course, this is not feasible for large data sets.
I know that arrays are effectively cells turned into ranges, but my main issue is that the cell I'm trying to transform already references a range, and I don't know how to tell the software to apply the countif down each row (i.e. I intuitively would like to do something like countif((A2:C2):(A99:C99),"Adam") but understand that's not how spreadsheets work).
My goal is ultimately to perform some operations on the corresponding array but I think I'm comfortable enough with that once I can get the array formula I'm looking for.
try:
=ARRAYFORMULA(IF(A2:A="",,MMULT(IF(A2:C="Adam", 1, 0), {1;1;1})))
I'm trying to get the values from an array formula into VBA as an array. Simple example: I have a cell (let's say D1) which has an array formula in it such as
=A1:A10*B1:B10
when I highlight this on the spreadsheet and press F9 I'll get an array of 10 numbers, say, ={5;12;15;24;25;24;42;40;54;70}
I want to be able to store these values in a VBA array: a(0)=5, a(1)=12, a(3)=15 etc; you get the idea.
Tried hunting for an answer on this one, but can't find anything relevant even on MSFT. Lots of answers about how to go the other way from VBA to the worksheet range (I know that one) but not this way. Looked at trying to do it via a ParamArray (the number of elements won't be fixed), by assigning directly to a undimensioned array and via EVALUATE(range) but I can't get any of these to work. I feel I must be missing something obvious.
Not sure what you have tried already. But Evaluate should work.
If I have the following:
The code:
Sub getArrayFormulaResult()
sFormula = Range("D1").FormulaArray
aResult = Evaluate(sFormula)
MsgBox Join(Application.Transpose(aResult), ",")
End Sub
will result in:
try this
Dim var as variant
var=Worksheets("MySheet").Evaluate(Worksheets("MySheet").Range("D1").formula)
Note you should use Worksheet.Evaluate to ensure this works when Mysheet is not the active sheet. The result will always be a 2_D array (with one column for your example array formula)
I am creating a Word userform using VBA. I store several configuration using array in the program code, such as the following:
Public arrConfiguration[2, 3] as Integer
where index 2 represent type 0 to 2, and index 3 represent properties 0 to 3 for each type.
However, I planned to modify the program for larger amount of data (such as for 100 different types of data and 50 properties for each data).
My question is,
should I keep storing the data using array in the program, so that it will be
Public arrConfiguration[99, 49] as Integer
or store it in an Excel file, and make the program open the Excel file and access the cells repeatedly? Which one is better?
Thank you.
Please prefer excel. Sample example data image is appended here-under.
For creating two dimensional dynamic array in excel, follow the steps below:
◾Declare the two dimensional Array
◾Resize the array
◾Store values in array
◾Retrieve values from array
Sub FnTwoDimentionDynamic()
Dim arrTwoD()
Dim intRows
Dim intCols
intRows = Sheet1.UsedRange.Rows.Count
intCols = Sheet1.UsedRange.Columns.Count
ReDim Preserve arrTwoD(1 To intRows, 1 To intCols)
For i = 1 To UBound(arrTwoD, 1)
For j = 1 To UBound(arrTwoD, 2)
arrTwoD(i, j) = Sheet1.Cells(i, j)
Next
Next
MsgBox "The value is B5 is " & arrTwoD(5, 2)
End Sub
In the Message Box you will get the following output.
Further To visualize a two dimensional array we could picture a row of CD racks. To make things easier, we can imagine that each CD rack could be for a different artist. Like the CDs, the racks would be identifiable by number. Below we'll define a two dimensional array representing a row of CD racks. The strings inside of the array will represent album titles.
For multidimensional arrays it should be noted that only the last dimension can be resized. That means that given our example above, once we created the array with two CD racks, we would not be able to add more racks, we would only be able to change the number of CDs each rack held.
You can simplify #skkakkar code:
dim x as variant
x = range("A1").CurrentRegion
No Redim, no loops.
Depending on how you see things evolving, you might want to consider accessing your Excel data via ADO, rather than OLE Automation. That way, if you decide to change your storage system to Access, SQL Server or something else, you will have less work to do.
How To Use ADO with Excel Data from Visual Basic or VBA (Microsoft)
https://support.microsoft.com/en-gb/kb/257819
Read and Write Excel Documents Using OLEDB (Codeproject)
http://www.codeproject.com/Tips/705470/Read-and-Write-Excel-Documents-Using-OLEDB
I have a case in an Excel macro (VBA) where I'd like to dimension an array where the number of dimensions and the bounds of each dimension are determined at runtime. I'm letting the user specify a series of combinatorial options by creating a column for each option type and filling in the possibilities below. The number of columns and the number of options is determined at run time by inspecting the sheet.
Some code needs to run through each combination (one selection from each column) and I'd like to store the results in a multidimensional array.
The number of dimensions will probably be between about 2 to 6 so I can always fall back to a bunch of if else blocks if I have to but it feels like there should be a better way.
I was thinking it would be possible to do if I could construct the Redim statement at runtime as a string and execute the string, but this doesn't seem possible.
Is there any way to dynamically Redim with a varying number of dimensions?
I'm pretty sure there is no way of doing this in a single ReDim statement. Select Case may be marginally neater than "a bunch of If...Else blocks", but you're still writing out a lot of separate ReDims.
Working with arrays in VBA where you don't know in advance how many dimensions they will have is a bit of a PITA - as well as ReDim not being very flexible, there is also no neat way of testing an array to see how many dimensions it has (you have to loop through attempts to access higher dimensions and trap errors, or hack around in the underlying memory structure - see this question). So you will need to keep track of the number of dimensions, and write long Case statements every time you need to access the array as well, since the syntax will be different.
I would suggest creating the array with the largest number of dimensions you think you'll need, then setting the number of elements in any unused dimensions to 1 - that way you always have the same syntax every time you access the array, and if you need to you can check for this using UBound(). This is the approach taken by the Excel developers themselves for the Range.Value property - which always returns a 2-dimensional array even for a 1-dimensional Range.
As I understood your users can specify dimensions and their seize by filling in the excel-sheet. This means you have to get the last row containing a value and the last column.
Therefore, have a look at: Excel VBA- Finding the last column with data
Use Redim to change the array's size. If you want to keep some kind of entries use Redim Preserve
"Some code needs to run through each combination (one selection from
each column) and I'd like to store the results in a multidimensional
array."
To begin with, I would transpose desired Range object into a Variant.
Dim vArray as Variant
'--as per your defined Sheet, range
'this creates a two dimensional array
vArray = ActiveWorkbook.Sheets("Sheet1").Range("A1:Z300").Value2
Then you could iterate through this array to possible find the size and data you need, which you may save it to an array (with the dimensions) you need.
Little Background:
Redim: Reallocates storage space for an array variable.
You are not allowed to Redim an array, if you are defining an array with a Dim statement initially. (e.g. Dim vArray(1 to 6) As Variant).
UPDATE: to show explicitly what's allowed and what's not under Redim.
Each time you use Redim it resets your original Array object to the dimensions you are defining next.
There's a way to preserve your data using Redim Preserve but that only allows you to change the last dimension of a multidimensional array, where first dimension remains as the original.
I'm trying to write a simulator in VB (Excel macro) where the input to a simulation is taken from cells in one sheet. The input will be placed in a number of arrays, for example timePerUser(10) and bytesPerUser(10). Then there will be some simple if/for/while stuff to make calculations based on the arrays and finally I will write the results back to Excel. SO, Excel will only be used to provide input data and to display the results, everything else is happening inside of the macro, including changing values in the arrays.
I am used working with Matlab but can't use it for this simulator, so here are my questions:
Are there any existing matrix/array operations I could use within an Excel macro? For example, is there some command to check the smallest or the next smallest value in an array? The Excel function "SMALL" would be perfect, but it doesn't seem to work in macros. Or do I simply need to solve this with for-loops?
Are there any other suggestions on how to create the arrays? Is it better to have one big matrix where each row corresponds to time, data, user etc (an NxM matrix) or is it better to have separate arrays for each parameter?
How to speed up matrix/array operations? Any general suggestions?
Thanks!
Oscar
Excel does have some simple array operations - SUMPRODUCT (Matlab equivalent: A*B'), MIN, MAX, ... Most of these, including SMALL, need to be called using Application.WorksheetFunction.xxx ; for example, the function SMALL can be called in VBA with the following:
nthSmallest = Application.WorksheetFunction.Small(r, n)
where r is an array or range, and n is an integer between 1 and r.Cells.Count
I would strongly urge you to use one variable per conceptual variable, rather than trying to be clever with multiple entities in one 2D array. Speedwise I suspect the difference is small; but the opportunity to write unreadable code is significant.
As for speeding things up: it really helps to define your variables (and especially your arrays) as some fixed type (rather than Variant), e.g.:
Dim a(1 To 10) as Double
Dim ii as Integer
and it may help a little bit to have all of the arrays use the same (default) base. Since I'm a Matlab junkie myself, I often work with array indices starting at 1 - you enforce this in VBA by adding
Option Base 1
at the start of your module. At this point, if you declare
Dim a(10) as Double
it is equivalent to
Dim a(1 To 10) as Double
Is also good practice for someone who is not proficient in VBA, to add
Option Explicit
at the start of the module, as it will ensure that any variables you did not declare (or more often, that you misspelled) will generate an interpreter error.
One other thing about arrays: as a Matlab person you are used to Matlab increasing array sizes as needed. In VBA it is possible to change array size dynamically at runtime, using, for example
ReDim Preserve a(1 To 20)
which will make the array a 20 elements in size. If it was shorter, it will be padded with zeros; if it was longer, it will be trimmed. This CAN be useful, but is QUITE expensive, since often the entire array needs to be copied to a new location. When you don't know in advance how large your array will be, it's better to overestimate (and check bounds) than to ReDim every time it needs to get bigger - and do a final ReDim at the end to get it to the right size.
Hope this is enough to get your started!