Question detail
My code below stores the results from a calculation in array funds()
and repeats that process for the selected range. By the end there will be a one dimensional array with 170 values. I need to access the array from a certain point every loop to fill the new row with different values.
Problem in detail
The core problem I am having is printing that array to a range on the workbook which is made up of 10 rows by 17 columns.
I managed to get it to go down a row once the "for each cell in selected range" loop exits, but now it fills the new row with the same initial array values!
Here is the current output:
What have i tried?
- I have tried the Redim but get overwhelmed by the length of examples.
- I have tried manual copy and paste but feel like that is cheating...
- I have researched how to delete elements through the process of copying..
Overall I am sure there is a simple way that everyone knows how to use! but what is it?
In a nutshell...
Every loop remove the initial 17 values, then print the next 17 array values to new row in range. Repeat 10 times.
The code
Option Explicit
Public funds(0 To 170) As Variant
Sub cumulativeperformance()
Dim selectedrange As Range
Dim cell As Range
Dim value1 As Double
Dim Value2 As Long
Dim i, x, d, c As Integer
Dim total(0 To 170) As Double
'Copy the table to report
Worksheets("Data").Range("C3:T13").Copy
Sheets("Report").Range("B39").PasteSpecial
Worksheets("Data").Range("B3:T13").Copy
Sheets("Report").Range("A39").PasteSpecial xlPasteValues
'Repeat 9 times
c = 39
For d = 0 To 9
c = c + 1
Set selectedrange = Worksheets("Report").Range(Cells(c, 3), Cells(c, 19))
For Each cell In selectedrange
value1 = cell.Value
'get the value of cell to the left of current cell
Value2 = cell.Offset(0, -1).Value
'get the difference to previous month
value1 = value1 / Value2 - 1
'assign result + 1 to array
total(x) = value1 + 1
'If initial fund slot is 0, then store first result of calculation in that slot
If i = 0 Then
funds(i) = total(0) - 1
ElseIf i > 0 Then
'Do calculation on remaining values and store in fund array
funds(i) = (funds(i - 1) + 1) * total(i) - 1
End If
'MsgBox "cumulative performance: " & funds(I) & " Cell address: " & cell.Address
i = i + 1
x = x + 1
Next
'Write from one dimensional Array To The worksheet
Dim Destination As Range
Dim j As Integer
Set Destination = Range(Cells(c, 3), Cells(c, 3)) 'starts at
Set Destination = Destination.Resize(1, 17) 'UBound(funds))
Destination.Value = funds
'MsgBox "Hi there"
Next d
'one-off commands in here
Range("C40:S49").NumberFormat = "0.00%"
Call portfoliomay
End Sub
The destination range and the source array should have the same dimensions to be able to assign the values correctly, as commented by Ron Rosenfeld. This is possible by either using a 1-dimension array to reuse 10 times for just one row at a time
array(columns)
, or a 2-dimensions array for the full destination range (10x17)array(rows, columns)
.Method #1: 1-dimension array
Use a 1-dimension array of 17 values, for a row by row operation, one row at a time. Initially declare the array as a dynamic array
Dim funds() ...
, so you'll be able to easily reset it. Then set its zero based lengthReDim funds(16) ...
at the beginning of eachFor d = 0 To 9
iteration. The rest of your code will stay the same. With this method your original destination assignment should work as expectedDestination.Value = funds
(or with an equivalent shorter statementCells(c, 3).Resize(1, 17) = funds
).Method #2: 2-dimensions array
You can declare funds as a zero based 2-dimensions array
Dim funds(9, 16) ...
. But then there is no straight forward way to put the data in row by row. The destination assignment will be to the whole range at onceCells(40, 3).Resize(10, 17) = funds
after your calculation loops end. You will also need to adjust thefunds
directives to indicate the rowfunds(d, i) = ...
. This may be the most efficient way (performance wise) to put in the data in your sheet, as putting data in cells is relatively time consuming.*To do it row by row with a 2-dimensions array you'll have to use a workaround like the ones described here return an entire row of a multidimensional array in VBA to a one dimensional array.
Other adjustments
You will need to adjust your
total
array to have the same dimensions and directives as thefunds
, or adjusti
andx
calculations. To adjusti
andx
and leavetotal
as is, addi = 0
at the beginning of yourFor d
iteration, and use onlytotal(x)
.edited after OP's confirmation his goal is optimizing code (see at the end)
I'm adding a different "flavor" of array/ranges use and showing some possible code enhancements
Variant
variable as arraythere's no need to
Dim
orRedim
any array, just declare ita as a pure Variant variable and fill it with the range values that will host the final resultssome thing like
where
repRng
is theRange
of the "Report" sheet that you want to fill withfunds
array itselfreduce variables
there's no need for a total array at all. just use a simple
Double
variableDim
appropriatelywould result in declaring
i
,x
andd
variables as ofVariant
type and onlyc
as ofInteger
typeto have all those variables declared as integer you must type:
but we'll use much less of them
reduce code
since you're assigning
and then
you could merge those two statements into the single
which, for what above said, we'll change to:
copy/paste
these statements:
actually do the same as:
which can also be written as:
this approach both reduces time (not an issue for such little a range) and doesn't use the clipboard (which at least you'd take care releasing with
Application.CutCopyMode = False
)for what above said, this statement will be used to initialize
repRng
Range
variable, tooReduce variables (part2)
your
d
variable is used only for iterating through rows you just previously copied and pasted, but you're using hard coded values for its span and then making it relative to another hard coded reference row index (c = 39
)you'd better exploit consistente reference to the range you're actually dealing with, like (pseudo code)
where
repRng
is aRange
object referencing relevant cells of sheet "Report" you want to loop throughThe final outcome will be the following code:
further optimization would avoid the
If jCol = 1 Then
check for every row, since it's not up to some unknown condition: we know for sure that every new row will start with a column index 1so, for every row, we can
act on its initial column:
relying on a specific
GetTotal()
functionwhere we collected the code to calculate
total
value "attached" to a singlecell
do calculation for subsequent columns
exploiting the same
GetTotal()
functionFinally the updated code would be:
some final(?) notes:
A.
Public
variablesthese are used to share variables among different subs/function across different modules
but it's usually a bad practice using them, being preferable put those variables in subs/function parameters to carry them along where needed
with the code as in the question, there is no other sub/function using
funds
, so it better move its declaration into cumulativeperformance():B. simplify GetTotal()