I am trying to multiply two arrays placing the value of each iteration into a spreadsheet. Here is what I have so far:
Sub Test1()
Dim x As Long
Dim myArray
Dim myArrayAdj
myArray = Array(24800, 26300, 27900)
myArrayAdj = Array(1.0025, 1.005, 1.0075, 1.01)
For x = 1 To 1000
Cells(x, x) = myArray * myArrayAdj
Next x
End Sub
When I run this I get a Run-time13 error with the following highlighted:
Cells(x, x) = myArray * myArrayAdj
Can someone explain to me where I've gone wrong? Thank you!
Your have a few issues which I think I've corrected below. One of the things you'll notice about my code is that I use Variant variables to loop through the arrays instead of identifying the element by number (e.g. myArrayElm instead of myArray(x)). This is just my personal preference.
Sub Test1()
Dim x As Long
Dim myArray 'Your first array
Dim myArrayElm 'A variable for the elements in your first array
Dim myArrayAdj 'Your second array
Dim myArrayAdjElm 'A variable for the elements in your second array
'Add values to your arrays
myArray = Array(24800, 26300, 27900)
myArrayAdj = Array(1.0025, 1.005, 1.0075, 1.01)
'Loop through the elements in your first array
For Each myArrayElm In myArray
'Loop through the elements in your second array
For Each myArrayAdjElm In myArrayAdj
x = x + 1
'Multiply the two array elements together
Cells(x, 1) = myArrayElm * myArrayAdjElm
Next myArrayAdjElm
Next myArrayElm
End Sub
This code loops through each element in both arrays, multiplies the two elements, and stores the values in a list beginning in cell A1.
Now, if you have a large dataset you're working with, the below example will be more efficient and will finish quicker since it stores the results in another array and then pastes the results to a sheet all at once instead of individually:
Option Base 1
Sub Test1()
Dim x As Long
Dim myArray 'Your first array
Dim myArrayElm 'A variable for the elements in your first array
Dim myArrayAdj 'Your second array
Dim myArrayAdjElm 'A variable for the elements in your second array
Dim Results 'An array for your results
Dim r As Range 'Range to store values
'Add values to your arrays
myArray = Array(24800, 26300, 27900)
myArrayAdj = Array(1.0025, 1.005, 1.0075, 1.01)
'Set the size of the results array
ReDim Results(1 To UBound(myArray) * UBound(myArrayAdj))
'Loop through the elements in your first array
For Each myArrayElm In myArray
'Loop through the elements in your second array
For Each myArrayAdjElm In myArrayAdj
x = x + 1
'Multiply the two array elements together
Results(x) = myArrayElm * myArrayAdjElm
Next myArrayAdjElm
Next myArrayElm
'Set the destination range
Set r = Range("A1:A" & UBound(Results))
'Paste results to sheet
r = Application.Transpose(Results)
End Sub
Note the Option Base 1 at the top. This just means that all the arrays will now start at element 1 instead of element 0 which is the default.
The whole problem stems from your statement right here: "I want to multiply two arrays"
I assume by that you meant you would like to multiple the individual elements in the two arrays, one by one.
In that case you want to do something this:
Cells(x, x) = myArray(x) * myArrayAdj(x)
That said, I'm not sure whether your intention was to store the results of the multiplication in cells on the diagonal of the work sheet or in some other place
If it's the former then Cells(x,x) makes sense but if it's the latter, than you need to be more specific about your expectation in multiplying the two arrays.
Related
So, I have a form that I'm trying to populate the ComboBox with an Array that should be full of elements based on a loop. However when I'm attempting to initialize the form and populate the ComboBox I'm getting a Subscript out of Range Error
I'm not really sure why I'm running out of the range so I could use some help examining my code. I'm not the most familiar with VBA so I could really use a second set of eyes to tell me what I'm doing wrong.
Private Sub UserForm_Initialize()
Dim refConcentrations As Variant
Dim i As Long, j As Integer, LRow As Long
With Sheets(2)
LRow = .Cells(.Rows.Count, "E").End(xlUp).row + 1
End With
ReDim refConcentrations(1 To LRow) As Variant
j = 1
For i = 2 To LRow
'Check if Current Session User is equal to any of the Stored ECNs
If Sheets(2).Range("A" & i) = VBA.Environ("UserName") Then
'If So, Store that ECN in Array
refConcentrations(j) = Sheets(2).Range("E" & i).Value
j = j + 1
End If
Next i
ReDim Preserve refConcentrations(1 To j - 1) ' <-- resize array to number of elements found
ComboBox_PreviousECN.List = refConcentrations() ' <-- Set ComboBox Dropdown to equal the elements in the Array
End Sub
Adding () to the name of an array tells the compiler to look for an index.
The notation used to refer to an element of an array consists of the variable name followed by parentheses containing an index number indicating the desired element.
The Error 9: Subscript out of range error is thrown because:
You referenced a nonexistent array element.
that is, the null element called by ().
The VBA reference on the List property of the ComboBox element says:
Use List to copy an entire two-dimensional array of values to a control.
Similarly:
a = Array(1, 2, 3, 4)
For Each x In a
Debug.Print x 'works
Next x
Debug.Print a 'throws an error
Debug.Print a() 'throws an error
Debug.Print a(0) 'returns 1
Ref:
https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/array-function
https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/subscript-out-of-range-error-9
https://learn.microsoft.com/en-us/office/vba/api/outlook.combobox.list
I have to move data in my input files to another workbook. The data is structured in worksheets as hardcoded input as below where the column with all the identifiers is a named range called "INPUT_MARKER".
IQ_SALES 100 200 300
IS_MARGIN 20 30 40
IQ_EBITDA 50 30 20
I only have to move some of the data. So for instance in the above I would only have to move the IQ_SALES data and IQ_EBITDA data. So I need to understand how to create an array of arrays with only the data that is needed.
The code below compares the data in the INPUT_MARKER column with elements in the array called "identifierArray" and I then intend to insert all of the row data in the multidimensional array called "bigDataArray". I have tried several approaches but have not been able to make this work. Would much appreciate any help. I have left out some of the redundant code in the below such that only the code pertaining to this problem is included.
Sub Update()
Dim identifierArray(), bigDataArray() As Variant
Application.ScreenUpdating = False
Application.CutCopyMode = False
'Definition of the array of data that is to be transferred to the targetModel
identifierArray = Array("IQ_SALES", "IQ_EBITDA")
ReDim bigDataArray(1 To UBound(identifiersArray))
With Workbooks(sourceModel).Sheets("DATA")
For Each c In .Range("INPUT_MARKER")
For Each element In identifierArray
If element = c.Value Then
'To construct bigDataArray by inserting row data every time element equals c.Value
End If
Next element
Next c
End With
End Sub
I've tackled a similar issue recently. This can be handled with a multi-dimensional array from the look of things
Though as a predisposition I'd recommend checking reference on dynamic
multi-dimensional
arrays
Private Sub fill_array()
Dim arr() As String
Dim i As Integer: i = 0
Dim cell As Range
Dim ws As Worksheet: Set ws = Sheets("DATA")
For Each cell In ws.Range("INPUT_MARKER")
If ws.Cells(cell.Row, 1) = "IQ_SALES" Or ws.Cells(cell.Row, 1) = "IQ_EBITDA" Then
ReDim Preserve arr(0 To 2, i)
arr(0, i) = ws.Cells(cell.Row, 2)
arr(1, i) = ws.Cells(cell.Row, 3)
arr(2, i) = ws.Cells(cell.Row, 4)
i = i + 1
End If
Next cell
End Sub
So your array will have the structure ofarr(x, y), where:
x - [0;2] - will be the 3 columns of data you want to store
y - n - index of the array (with only IQ_SALES and IQ_EBITDA being added)
EDIT:
This is of course presuming, your data "INPUT_MARKER" starts at
Column A
Also as an extra tip, if you want to also store information of the arrays source - in resemblence of a primary key, you can increment the first dimension
ReDim Preserve arr(0 to 3, i)
arr(3, i) = cell.Row ' edited (instead of arr(3)= …)
and use example the cell.Row as a reference as to where the data was obtained from, in order to reverse trace the data
If you know the range of the values you want to pick you can shortcut using:
Dim bigDataArray() As Variant
bigDataArray = Range(A1:D4)
This will set up the array with the same size as the range you pick up,
Then you can output the specific values you want from the array.
I have a process I am trying to code in macros.
For every row in range:
I am trying to select non empty cells in a row.
For those cells, pick a minimum value n_1.
Given a multiplication factor a create an array (same length as the non empty row) of equally spaced numbers starting with the minimum value, i.e. (n_k = (a^k)*n_1).
Something along these lines
Dim a As Range, b As Range, number_of_elements as Integer
Set a = Range()
For Each b In a.Rows
Dim newarray as Variant 'initialize new array
arr = select_non_empty_cells(b) 'select non empty cells
number_of_elements = Ubound(arr) 'get number of elements
ReDim newarray(1 To number_of_elements) As Integer 'set the dimension
min_val = WorksheetFunction.Min(arr.Value) 'pick minimum value
For counter = 1 To number_of_elements 'create new array with equally spaced numbers
newarray(counter) = min_val*1.25^counter 'multiplying factor
Next counter
arr.Value = newarray.Value 'set the non empty range to new values
Next
And below is what my data will look like. So for the first row I would pick 1033.2 (the minimum value) and create new array of the same length of 5 elements evenly spaced. Same for the second row.
Perhaps something like:
Sub Korba()
Dim i As Long, mini As Long
Dim WhichRow As Long
Dim factr As Double
mini = 3
factr = 1.25
WhichRow = 5
For i = 1 To Columns.Count
With Cells(WhichRow, i)
If .Value <> "" Then Exit Sub
.Value = mini * factr ^ i
End With
Next i
End Sub
I have been working on this particular problem for sometime and am obviously missing something very simple. I ma trying to create an aray based on a dynamic range in Excel and using the individual elements to compare against another array. The only problem with the attached code is it continues to show empty elements. Any guidance would be appreciated.
Part of my overall code attached.
Sub Test_Again()
Dim R As Long
Dim C As Long
Dim List() As Variant
Dim i As Integer
List = Sheets("Sheet11").Range("A2:A17").Value
For R = 1 To UBound(List, 1) ' First array dimension is rows.
For C = 1 To UBound(List, 2) ' Second array dimension is columns.
Debug.Print List(R, C)
Next C
Next R
ReDim List(UBound(List, 1))
Do Until i = UBound(List)
If List(i) = Now() Then
End If
i = i + 1
Loop
End Sub
The normal Redim will clear your array - unless you use Redim Preserve. However, according to the help:
If you use the Preserve keyword, you can resize only the last array dimension and you can't change the number of dimensions at all. For example, if your array has only one dimension, you can resize that dimension because it is the last and only dimension. However, if your array has two or more dimensions, you can change the size of only the last dimension and still preserve the contents of the array.
Therefore, in your case Redim will not help you here. If you want to transfer a two dimensional array to a one dimensional array, you need to do this manually instead:
Sub Test_New()
Dim lRow As Long, lCol As Long
Dim vListSource() As Variant, vListTarget() As Variant
'Assign soure array
vListSource = Sheets("Sheet11").Range("A2:A17").Value
'Show full content for debug
For lRow = LBound(vListSource) To UBound(vListSource) ' First array dimension is rows.
For lCol = LBound(vListSource, 2) To LBound(vListSource, 2) ' Second array dimension is columns.
Debug.Print vListSource(lRow, lCol)
Next lCol
Next lRow
'Transfer array to one dimension
ReDim vListTarget(LBound(vListSource) To UBound(vListSource))
For lRow = LBound(vListSource) To UBound(vListSource)
vListTarget(lRow) = vListSource(lRow, LBound(vListSource, 2))
Next lRow
'Your check code
For lRow = LBound(vListTarget) To UBound(vListTarget)
If vListTarget(lRow) = Now() Then
'Do something here
End If
Next lRow
End Sub
This will copy the first row of your range/array to a one dimensional array and use this for further processing.
However, from your code and question I do not see the advantage of redimming it to one dimension - you could easily do your loop one the two dimensional array - and just look in the first and only column.
Could you please say- how a Excel Range("G2:AA1000") can be assigned to a 2D array? If possible how to return back that 2D array to the same range after performing some operation on that 2D array?After assignment a Range to an 2D array,How each row will be identified from that 2D matrix?
Thanks,
There is an easy way to make changes to an area using an array, and write it out to the same place, or somewhere else.
This example code will copy data from one area to another, using an array:
Sub example()
Dim testdata()
testdata = Range("A1:B13")
Range("D1:E13") = testdata ' simple copy
Range("G1") = testdata ' copy only 1 cell
Range("I1:K22") = testdata 'try to copy too much
End Sub
The testdata array starts from 1, and will extend to the number of columns and rows specified in the range. In this case, testdata(1,1) refers to the data obtained from A1, testdata(1,2) refers to B1, finishing up with testdata(13,1) referring to A13, and testdata(13,2) referring to B13.
Setting the range equal to the array in the next line copies the array into the specified location.
If the area is smaller than the original array, it will copy only enough of the array to fill that space, so Range("D1")=testdata will only place one cell on the sheet.
If you specify a larger area, then #N/A will fill the area that is not in the space covered by array elements, so Range("A1:A3")=testdata will fill A1 and A2 with data from the array, but A3 will have #N/A
Result of example program:
Note: A1:B13 is the original data, which gets copied with the subsequent range(??)=testdata
Here's a worked-out example of reading a range of data from a worksheet, operating on the array, and then writing it back out to the same worksheet.
Sub RangeArray()
Dim Rng As Range
Dim Arr()
Dim ArrItem
Dim i As Long, j As Long
Dim rUB as Long, cUB as Long
Set Rng = Worksheets("Sheet1").Range("A1:G19")
rUB = Rng.Rows.Count 'Row upper bound
cUB = Rng.Columns.Count ' Column upper bound
ReDim Arr(1 To rUB, 1 To cUB)
'Read worksheet range into array
For i = 1 To rUB
For j = 1 to cUB
Arr(i, j) = Rng.Cells(i, j).Value
Next
Next
'Do something to array
For i = 1 To rUB
For j = 1 To cUB
If i <> j Then
Arr(i, j) = Arr(i, j) / (i * j)
End If
Next
Next
'Write array back to worksheet
Set Rng = Worksheets("Sheet1").Range("I1")
For i = 1 To rUB
For j = 1 To cUB
Rng.Offset(i - 1, j - 1).Value = Arr(i, j)
Next
Next
End Sub
Yes, an Excel range can be assigned to a 2D array in one single assignment. In C++/CLI it looks like this:
cli::array<Object^, 2>^ arrData = safe_cast<cli::array<Object^, 2>^>(rg->Value[Excel::XlRangeValueDataType::xlRangeValueDefault]);
In c# or visual basic it would look considerably simpler (see here for example https://www.automateexcel.com/vba/assign-range-to-array/, btw in dotnet the object is now playing the role of the variant data type). Note that it must be a two dimensional array and the returned array has a one-based indexing, and not a zero based indexing.
For large data sets this method is a lot faster than the looping. The looping generates lots of COM objects. I compared the two methods with a 33000 row Excel range and the data import into the array was almost instantaneous while with the looping it took very long and it heated up the CPU.
One way to loop through a range is to use the For...Next loop with the Cells property. Using the Cells property, you can substitute the loop counter (or other variables or expressions) for the cell index numbers. In the following example, the variable counter is substituted for the row index. The procedure loops through the range C1:C20, setting to 0 (zero) any number whose absolute value is less than 0.01.
Sub RoundToZero1()
For Counter = 1 To 20
Set curCell = Worksheets("Sheet1").Cells(Counter, 3)
If Abs(curCell.Value) < 0.01 Then curCell.Value = 0
Next Counter
End Sub