Suppose I have a VBA one dimension array (or dict or collection) with X values. I need to perform an action with these values in batches of Y.
So if X = 55 and Y = 25, I would need to loop 3 times:
Pick values 1 to 25 and perform action
Pick values 26 to 50 and perform action
Pick last 5 values and perform action
Any ideas with good performance will be greatly appreciated :)
Edit:
I came up with the code below. It works although doesn't look very concise
Sub test()
Dim arr As Variant
Dim temparr As Variant
Dim sippno As Integer
Dim loopend As Integer
Dim loopstart As Integer
Dim batchsize As Integer
Dim i As Integer
'Storing main array with all values
arr = Sheet1.Range("A1:A" & Sheet1.Range("A" & Rows.Count).End(xlUp).Row).Value
'Setting count of values, batch size and starting step for loop
sippno = WorksheetFunction.CountA(arr)
loopstart = 1
batchsize = 10
Do Until sippno = 0
If sippno < batchsize Then
loopend = loopstart + sippno - 1
Else
loopend = loopstart + batchsize - 1
End If
ReDim temparr(loopstart To loopend)
For i = loopstart To loopend
temparr(i) = WorksheetFunction.Index(arr, i, 0)
sippno = sippno - 1
Next
loopstart = loopend + 1
'Action to be performed with batch of values stored in second array
Debug.Print WorksheetFunction.TextJoin(", ", True, temparr)
Loop
End Sub
Slicing via Application.Index()
Just for the sake of the art I demonstrate in this late post how to slice a 'vertical' array in one go into several 'flat' arrays in batches of e.g. 10 elements.
This approach benefits from the advanced rearranging features & pecularities of Application.Index()
allowing to pass entire row/column number arrays as arguments; here suffices a vertical array of desired row numbers, e.g. by filtering only rows 11 to 20 via Application.Index(data, Evaluate("Row(11:20)"), 0). .. c.f. see section 2 a)
Further notes:
evaluating a tabular row formula is one quick way to get consecutive row numbers.
transposing the function result changes the array dimension to a 1-dim array
reducing the array boundaries by -1 via ReDim Preserve ar(0 To UBound(ar) - 1) produces a zero-based array (optional)
Option Explicit
Sub splice()
Const batch = 10 ' act in units of 10 elements
With Sheet1
'1) get data (1-based 2-dim array)
Dim lastRow As Long
lastRow = .Cells(Rows.Count, "A").End(xlUp).Row
Dim data: data = .Range("A1:A" & lastRow).Value2
'2) slice
Dim i As Long, nxt As Long, ar As Variant
For i = 1 To UBound(data) Step batch
nxt = Application.min(i + batch - 1, UBound(data))
'2a) assign sliced data to 1- dim array (with optional redim to 0-base)
With Application
ar = .Transpose(.Index(data, Evaluate("row(" & i & ":" & nxt & ")")))
End With
'optional redimming to zero-base
ReDim Preserve ar(0 To UBound(ar) - 1)
'2b) perform some action
Debug.Print _
"batch " & i \ batch + 1 & ": " & _
"ar(" & LBound(ar) & " To " & UBound(ar) & ") ~~> " & _
Join(ar, "|")
Next
End With
End Sub
Slicing a 'flat' 1-dim array
If, however you want to slice a 1-dim array, like e.g. dictionary keys, it suffices to transpose the data input: data = Application.Transpose(...)
Option Explicit
Sub splice()
Const batch = 10
Dim data, ar()
Dim lastrow As Long, n As Long, i As Long
Dim j As Long, r As Long
With Sheet1
lastrow = .Cells(Rows.Count, "A").End(xlUp).Row
data = .Range("A1:A" & lastrow).Value2
End With
i = Int(lastrow / batch)
For n = 0 To i
r = batch
If n = i Then
r = lastrow Mod batch
End If
If r > 0 Then
ReDim ar(r - 1)
For j = 1 To r
ar(j - 1) = data(j + n * batch, 1)
Next
' do something
Debug.Print Join(ar, ",")
End If
Next
End Sub
2d array because to lazy to encode 1d but same idea with 1d:
Sub test()
arr = Sheet3.Range("A1").CurrentRegion.Value2
x = UBound(arr)
y = 5
jj = y
For j = 1 To UBound(arr)
sumaction = sumaction + arr(j, 1)
If (UBound(arr) - jj) < 0 Then
jj = UBound(arr)
sumaction = 0
End If
If j = jj Then
dosomething = sumaction * 2
sumaction = 0
jj = jj + y
End If
Next j
End Sub
Related
Could someone explain my why my code display values at columns instead of rows ? Thanks
Function ShiftVector(rng As Range, n As Integer)
Dim i As Integer
Dim j As Integer
Dim B As Variant
Dim A() As Variant
Dim nr As Integer
nr = rng.Rows.Count
ReDim B(nr)
ReDim A(nr)
For i = 1 To nr - n
B(i) = rng(i + n)
Next i
For i = nr - n + 1 To nr
B(i) = rng(i - nr + n)
Next i
ShiftVector = B
End Function
Your code loads a 1D array, which does not have rows by definition... Then, you do not explain what n means and I will make abstraction of its 'contribution' in the function... The next interpretation assumes that rng is a range containing only a column.
There are two ways of solving it:
Let the code as it is, but finally use:
ShiftVector = Application.Transpose(B)
ReDim and load a 2D array:
ReDim B(1 to nr, 1 to 1)
'and load it in the next way:
B(i, 1) = rng(i + n)
You can place a range directly in a (2D) array:
B = rng.value
If you will explain what n wants to be, I can adapt the answer to somehow take it in consideration...
Edited:
Please, play with the next function, able to make slices from a 2D array and 'mount' them in a different order:
Function ShiftVector2D(rng As Range, n As Integer) As Variant
Dim nr As Long, arr, arrSl1, arrSl2
nr = rng.rows.count - rng.row + 1 'the number of the range rows, even if it does not start from the first row...
arr = rng.Value 'place the range in a 2D array
With Application
arrSl1 = .Index(arr, Evaluate("row(1:" & n & ")"), 1) 'obtain an array slice of the first n rows
arrSl2 = .Index(arr, Evaluate("row(" & n + 1 & ":" & nr & ")"), 1) 'obtain an array slice of the rows after n up to the last row
arr = Split(Join(.Transpose(arrSl2), "|") & "|" & Join(.Transpose(arrSl1), "|"), "|") 'created a 1 D array by joinning the two arrays and split them by "|"
ShiftVector2D = .Transpose(arr) 'return the 2D necessary shifted array
End With
End Function
You can test it placing some strings in the range "A1:A10" and run the next code:
Sub testShiftVector2D()
Dim rng As Range, arr
Set rng = Range("A1:A10")
arr = ShiftVector2D(rng, 4)
Debug.Print Join(Application.Transpose(arr), "|")
End Sub
I’m in a situation where I need to reproduce something in VBA and a bit stuck given my lack of understanding of object oriented language and VBA in general.
Problem:
I need to produce an array or vector based on a 2 column table.
The first range (column) contains unit counts.
The second range (column) contains numeric values.
I need to replicate the value based on the number of units.
For example,
if the first row contains 3 units with a value of $100
I need the array to contain $100, $100, $100.
This will need to be looped thru each row containing units.
So if row 2 contains 2 units with a value of $50
I need to complete array to be $100, $100, $100, $50, $50, and so on.
I understand this situation will require ReDim the array based on the total values. My struggle is I’ve been unable to figure out the nested for loops.
I get how to replicate the value based on the number of “units” like the below...
ReDim arr(0 To x - 1)
For i = 0 To x - 1
arr(i) = rng.Offset(0, 1).Value
Next
What is the best way to loop thru each row and replicate the values for each row in the range based on the unit count?
If anyone is familiar with R, I'm essentially looking for something that achieves the rep() function (e.g., rep(df$b, df$a)) and return the values in a single array.
Any help is greatly appreciated. Thanks
Or a one liner which uses the REPT function as you would have used in r :)
This assumes your data is in A1:B10 - the length can be made variable
s = Split(Join(Application.Transpose(Evaluate("=INDEx(REPT(B1:B10&"","",A1:A10),,1)"))), ",")
An an example, to dump the new to array to C1
s = Split(Join(Application.Transpose(Evaluate("=INDEx(REPT(B1:B10&"","",A1:A10),,1)"))), ",")
[c1].Resize(UBound(s), 1) = Application.Transpose(s)
When you say Row contains 3 units, do you mean the cell has value 3 or 3 Units? If it is 3 then you may not have to Redim the array in the loop. Simply find the sum of values in Col A which has units and Redim it in one go as shown below.
Sub Sample()
Dim ws As Worksheet
Dim Ar() As String
Dim n As Long, i As Long, lRow As Long
'~~> Change this to the relevant sheet
Set ws = Sheet6
With ws
n = Application.WorksheetFunction.Sum(.Columns(1))
ReDim Ar(t To n)
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
n = 1
For i = 1 To lRow
If Len(Trim(.Range("A" & i).Value)) <> 0 Then
For j = 1 To .Range("A" & i).Value
Ar(n) = .Range("B" & i).Value
n = n + 1
Next j
End If
Next i
For i = LBound(Ar) To UBound(Ar)
Debug.Print Ar(i)
Next i
End With
End Sub
Screenshot
And if the cell has 3 Units then you will have to store the values of Col A in an array, do a replace on Unit/Units, find the sum and finally use the above code. Here is an example
Sub Sample()
Dim ws As Worksheet
Dim Ar() As String, tmpAr As Variant
Dim n As Long, i As Long, j As Long, k As Long, lRow As Long
'~~> Change this to the relevant sheet
Set ws = Sheet6
With ws
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
tmpAr = .Range("A1:A" & lRow).Value
For i = LBound(tmpAr) To UBound(tmpAr)
tmpAr(i, 1) = Replace(tmpAr(i, 1), "Units", "")
tmpAr(i, 1) = Trim(Replace(tmpAr(i, 1), "Unit", ""))
n = n + Val(tmpAr(i, 1))
Next i
ReDim Ar(t To n)
n = 1
For i = 1 To lRow
If Len(Trim(.Range("A" & i).Value)) <> 0 Then
k = Val(Trim(Replace(Replace(.Range("A" & i).Value, "Units", ""), "Unit", "")))
For j = 1 To k
Ar(n) = .Range("B" & i).Value
n = n + 1
Next j
End If
Next i
For i = 1 To UBound(Ar)
Debug.Print Ar(i)
Next i
End With
End Sub
Screenshot
if your data is already in an array then ReDim'ing will delete it's contents. You can ReDim Preserve but it's an expensive operation, better to create a new array to put the results into.
I have assumed the data is contained within a Named Range called "Data" with Units being the first column and Values being the second column.
if your data changes regularly you can create a dynamic range using the OFFSET function i.e. =OFFSET(Sheet1!$A$1,0,0,COUNTA(Sheet1!$A:$A),2) assuming your data starts in cell A1 and there is no header row.
Sub ProcessData()
Dim DataArr() As Variant
Dim QtyColArr() As Variant
Dim ResultArr() As Variant
Dim TotalQty As Long
Dim i As Long, j As Long, k As Long
'store data into array
DataArr = Range("Data") 'assume data stored in named range called "Data"
'store Qty col into 1D array
QtyColArr = Range("Data").Resize(, 1)
'sum all qty vals
TotalQty = Application.Sum(QtyColArr)
're-size ResultsArray
ReDim ResultArr(1 To TotalQty)
'Initialize ResultsArr counter
k = LBound(ResultArr)
'loop DataArr
For i = LBound(DataArr) To UBound(DataArr)
'loop qty for current row
For j = 1 To DataArr(i, 1)
'copy value
ResultArr(k) = DataArr(i, 2)
'iterate ResultsArr counter
k = k + 1
Next j
Next i
'output to intermediate window
Debug.Print "{" & Join(ResultArr) & "}"
End Sub
I have a giant dataset that looks like this
I am trying to go down the list of different companies and grab 3 per company and combine them. Based on the photo above, I would have 2 different lists with 3 companies each (except TH Repair which will have 2 in the final list).
My real dataset contains hundreds of different companies, each with dozens/hundreds of entries so I would finish with dozens of lists (each potentially hundreds long).
I tried to record a macro and ended up with this code
Sub Loop1()
'
' Loop1 Macro
'
'
Range("A4:E6").Select
Selection.Copy
Sheets("Sheet3").Select
Range("A18").Select
ActiveSheet.Paste
Sheets("Sheet2").Select
Range("A11:E13").Select
Application.CutCopyMode = False
Selection.Copy
Sheets("Sheet3").Select
Range("A21").Select
ActiveSheet.Paste
Sheets("Sheet2").Select
Range("A17:E19").Select
Application.CutCopyMode = False
Selection.Copy
Sheets("Sheet3").Select
Range("A24").Select
ActiveSheet.Paste
End Sub
However, this turned out to be WAY more complicated then I expected.
I am looking for the end result to look like this
See if something like this works for you. I only ran one scenario through it so you will want to test it more.
This makes the assumption that the data is sorted by column B on the original sheet
This procedure makes an assumption that there is either headers or no data on row 1.
You will need to change the "Sheet1" in this line Set ws1 = ActiveWorkbook.Worksheets("Sheet1") to the name of the sheet you are starting with.
Option Explicit
Public Sub MoveData()
Dim ws1 As Worksheet
Set ws1 = ActiveWorkbook.Worksheets("Sheet1")
Dim ws2 As Worksheet
Set ws2 = ActiveWorkbook.Worksheets.Add()
Dim rw As Long
Dim match_count As Integer
Dim list_multiplier As Integer
list_multiplier = 7
Dim list_row() As Long
ReDim list_row(0)
list_row(0) = 2
For rw = 2 To ws1.Range("A" & ws1.Rows.Count).End(xlUp).Row
If ws1.Range("B" & rw).Value <> ws1.Range("B" & rw).Offset(-1, 0).Value Then
match_count = 0
Else
match_count = match_count + 1
End If
Dim list_num As Integer
list_num = match_count \ 3
If list_num > UBound(list_row, 1) Then
ReDim Preserve list_row(list_num)
list_row(list_num) = 2
End If
ws2.Cells(list_row(list_num), 1 + list_multiplier * list_num).Value = ws1.Range("A" & rw).Value
ws2.Cells(list_row(list_num), 2 + list_multiplier * list_num).Value = ws1.Range("B" & rw).Value
ws2.Cells(list_row(list_num), 3 + list_multiplier * list_num).Value = ws1.Range("C" & rw).Value
ws2.Cells(list_row(list_num), 4 + list_multiplier * list_num).Value = ws1.Range("D" & rw).Value
ws2.Cells(list_row(list_num), 5 + list_multiplier * list_num).Value = ws1.Range("E" & rw).Value
list_row(list_num) = list_row(list_num) + 1
Next rw
End Sub
When you record your macro, ensure that "Use Relative References" on the Developer Ribbon tab is enabled, :)
assuming row 3 has your data headers, you could try this:
Option Explicit
Sub main()
Dim nLists As Long, iList As Long
Dim data As Variant
Dim dataToDelete As Range
With Range("F3", Cells(Rows.Count, 1).End(xlUp))
data = .Value
nLists = WorksheetFunction.Max(.Resize(,1))
nLists = nLists \ 3 + IIf(nLists - 3 * (nLists \ 3) = 0, -1, 0)
End With
With Range("A3").Resize(, 6)
For iList = 0 To nLists
Set dataToDelete = Nothing
With .Offset(, iList * 6).Resize(UBound(data))
.Value = data
.AutoFilter Field:=1, Criteria1:="<=" & iList * 3, Criteria2:=">" & (iList + 1) * 3, Operator:=xlOr
If Application.WorksheetFunction.Subtotal(103, .Cells) > 1 Then Set dataToDelete = .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible)
.Parent.AutoFilterMode = False
If Not dataToDelete Is Nothing Then dataToDelete.Delete xlShiftUp
End With
Next
End With
End Sub
Your task is actually slightly trickier than your online advice suggests. Basically, you have to do the following:
Find out how many unique 'keys' (ie unique items in column B) you have. This will tell you the total number of rows you need (ie number of unique keys * 3)
Count the number of items for each 'key'. This will tell you how many columns you need (ie max item count / 3 * number of columns in array [A:E = 5])
Loop through each line of data and it put on appropriate row for that 'key'. Once three has been reached, jump the column for that key 6 columns to the right, and continue.
If you were to use a Class object and Collection type of object, this could be really quite concise code, but judging by your post you are at the beginning of your programming journey in VBA. Therefore, I've broken down each task into separate chunks of code so you will hopefully see how arrays can work for you. Once you practise with arrays a little, perhaps you could have a go at making this code more efficient by combining some of the loops:
Public Sub RunMe()
Dim data As Variant
Dim r As Long, c As Long, i As Long, dataRows As Long, dataCols As Long, keyLen As Long, maxCount As Long
Dim keys As String
Dim k As Variant
Dim keyArray() As String
Dim keyCount() As Long, threeCount() As Long, rowNum() As Long, colNum() As Long
Dim output() As Variant
'Read the data - change "Sheet1" to your sheet name.
'Shows how to write range values into a variant to
'create an array of variants.
data = ThisWorkbook.Worksheets("Sheet1").UsedRange.Value2
dataRows = UBound(data, 1)
dataCols = UBound(data, 2)
'Create a list of unique keys.
'Note: not the most efficient way, but shows how to
'create an array from a value-separated string.
For r = 1 To dataRows
If InStr(keys, CStr(data(r, 2))) = 0 Then
If Len(keys) > 0 Then keys = keys & "|"
keys = keys & CStr(data(r, 2))
End If
Next
keyArray = Split(keys, "|")
keyLen = UBound(keyArray)
'Initialise the row and column numbers for each key.
'Shows how to iterate an array using For Each loop.
ReDim rowNum(keyLen)
ReDim colNum(keyLen)
r = 1
i = 0
For Each k In keyArray
rowNum(i) = r
colNum(i) = 1
r = r + 3
i = i + 1
Next
'Count the number of items for each key.
'Shows how to iterate an array using For [index] loop.
ReDim keyCount(keyLen)
For r = 1 To dataRows
i = IndexOfKey(keyArray, CStr(data(r, 2)))
keyCount(i) = keyCount(i) + 1
If keyCount(i) > maxCount Then maxCount = keyCount(i)
Next
'Size the output array.
c = WorksheetFunction.Ceiling(maxCount / 3, 1)
ReDim output(1 To (keyLen + 1) * 3, 1 To c * dataCols + c - 1)
'Populate the output array.
ReDim threeCount(keyLen)
For r = 1 To dataRows
i = IndexOfKey(keyArray, CStr(data(r, 2)))
'Copy the columns for this row.
For c = 1 To dataCols
output(rowNum(i), colNum(i) + c - 1) = data(r, c)
Next
'Increment the count and if it's equals 3 then
'reset the row num and increase the column number.
threeCount(i) = threeCount(i) + 1
rowNum(i) = rowNum(i) + 1
If threeCount(i) = 3 Then
rowNum(i) = rowNum(i) - 3
colNum(i) = colNum(i) + dataCols + 1
threeCount(i) = 0
End If
Next
'Write the data - change "Sheet2" to your sheet name.
'Shows how to write an array to a Range.
ThisWorkbook.Worksheets("Sheet2").Range("A3") _
.Resize(UBound(output, 1), UBound(output, 2)).Value = output
End Sub
Private Function IndexOfKey(list() As String, key As String) As Long
Dim i As Long
Dim k As Variant
'Helper function to find index position of key in array.
For Each k In list
If key = k Then
IndexOfKey = i
Exit Function
End If
i = i + 1
Next
IndexOfKey = -1
End Function
Please look at my sample data and code to understand what I'm trying to do.
I need to use the value of Cells(, 3) to define a range to populate a Trialnumber(18) array. I need the array to iterate through a For loop, to count filled cells in column H for each trial and print the count to column T in the last row of each trial. I will also need the array for further data analysis in future(Unless someone can come up with a better solution).
At the moment I am experimenting with 3 modules of code, trying to get the desired solution.
Module 2 is the only one with no errors, and prints the value in the right cell, but it is printing the total filled cell count (562), rather than per trial (expected value = 1 or 2).
Module 1 is as follows:
Sub dotcountanalysis()
Dim startpoint As Long
startpoint = 1
Dim lastrow As Long
lastrow = Cells(Rows.Count, 3).End(xlUp).Row
Dim i As Long
With Worksheets("full test")
For i = 1 To 18
For n = startpoint To lastrow + 1
If Cells(n, 3).Value <> "Trial, " & CStr(i) Then
Dim nMinusOne As Long
nMinusOne = n - 1
Dim trialCount As Long
'Set Trialnumber(i-1) = Range(cells(startpoint, 3), cells(n-1, 3))
trialCount = Application.WorksheetFunction.CountA(Range("H" & CStr(startpoint) & ":" & "H" & CStr(nMinusOne)))
Range("T" & CStr(startpoint) & ":" & "T" & CStr(nMinusOne)).Value = trialCount
startpoint = n
Exit For
End If
Next n
Next i
End With
End Sub
It returns a "method _range of object _global falied" error on line: trialCount = Application.WorksheetFunction.CountA(Range("H" & CStr(startpoint) & ":" & "H" & CStr(nMinusOne)))
Module 3 is as follows:
Sub dotcountanalysis3()
Dim pressedCount As Long
Dim myCell As Range
Dim pressedRange As Range
'create trials array
Dim t(18) As Range
'set range for trialnumber (t)
Dim startpoint As Long
startpoint = 1
Dim lastrow As Long
lastrow = Cells(Rows.Count, 3).End(xlUp).Row
For i = 1 To 18
For n = startpoint To lastrow
startpoint = 7
If Cells(n, 3).Value <> "Trial, " & CStr(i) Then
Set t(i - 1) = Range(Cells(startpoint, 3), Cells(n, 3))
n = n + 1
startpoint = n
Exit For
End If
Next n
Next i
'count presses in each trial
With Worksheets("full test")
For i = 0 To 17
pressedCount = Application.WorksheetFunction.CountA _
(.Range(.Cells(t(), "H"), .Cells(.Rows.Count, "H")))
If pressedCount = 0 Then Exit Sub
'make sure there are cells or else the next line will fail
Set pressedRange = .Columns("H").SpecialCells(xlCellTypeConstants)
For Each myCell In pressedRange.Cells
'only loop through the cells containing something
.Cells(myCell.Row, "T").Value = pressedCount
Next myCell
Next i
End With
End Sub
It returns a run-time "type mismatch" error on line: pressedCount = Application.WorksheetFunction.CountA _
(.Range(.Cells(t(), "H"), .Cells(.Rows.Count, "H")))
Edit: I have updated code in mod 3 and updated error.
When counting things I like to use a dictionary object, and arrays are faster than going row by row on the sheet.
This will count unique combinations of Block+Trial: to count only by trial you would just use k = d(r, COL_TRIAL)
Dim dBT As Object 'global dictionary
Sub dotcountanalysis()
'constants for column positions
Const COL_BLOCK As Long = 1
Const COL_TRIAL As Long = 2
Const COL_ACT As Long = 7
Dim rng As Range, lastrow As Long, sht As Worksheet
Dim d, r As Long, k, resBT()
Set sht = Worksheets("full test")
lastrow = Cells(Rows.Count, 3).End(xlUp).Row
Set dBT = CreateObject("scripting.dictionary")
Set rng = sht.Range("B7:H" & lastrow)
d = rng.Value 'get the data into an array
ReDim resBT(1 To UBound(d), 1 To 1) 'resize the array which will
' be placed in ColT
'get unique combinations of Block and Trial and counts for each
For r = 1 To UBound(d, 1)
k = d(r, COL_BLOCK) & "|" & d(r, COL_TRIAL) 'create key
dBT(k) = dBT(k) + IIf(d(r, COL_ACT) <> "", 1, 0)
Next r
'populate array with appropriate counts for each row
For r = 1 To UBound(d, 1)
k = d(r, 1) & "|" & d(r, 2) 'create key
resBT(r, 1) = dBT(k) 'get the count
Next r
'place array to sheet
sht.Range("T7").Resize(UBound(resBT, 1), 1) = resBT
'show the counts in the Immediate pane (for debugging)
For Each k In dBT
Debug.Print k, dBT(k)
Next k
End Sub
I want to assign and store two calculated values to a single scenario ("i") in an array. Then I want to dump one of those values (for each "i") in one column and the other value in another column, once the loops are completed. If you look under 'UI, that's what I want to essentially accomplish, but I want them all to spit out at once after the loops are completed, instead of each one spitting out one at a time. I heard an array would be the best/fastest way to do this, but I don't know how to even go about using one.
Thanks
Sub Test_Scenarios()
Dim i As Long, Scenario_Count As Long
Dim j As Integer
'Delete current values on "Testing Output" tab
Sheets("Testing Output").Range("B1:B3").ClearContents
Sheets("Testing Output").Range("A6:AA1000000").ClearContents
'Test scenarios
Scenario_Count = Sheets("Testing Input").Range("B1").Value
For i = 1 To Scenario_Count
For j = 1 To 2
If j = 1 Then Sheets("AA").Range("ZC").Value = "No"
If j = 2 Then Sheets("AA").Range("ZC").Value = "Yes"
Calculate
'UI
If j = 1 Then Sheets("Testing Output").Range("R" & 5 + i).Value = Sheets("User Input").Range("B26").Value
If j = 2 Then Sheets("Testing Output").Range("S" & 5 + i).Value = Sheets("User Input").Range("B26").Value
Next j
Next i
End Sub
There's a good discussion of using Arrays to with Excel ranges at http://www.cpearson.com/excel/ArraysAndRanges.aspx, but I'll include some basics here.
To read data from an Excel Range in to an array:
Dim Arr() As Variant
Arr = Range("A1:B10")
To write data from an array to an Excel Range:
Range("E1:F10").Value = Arr
When writing the array back to the range, the size of the array must match the size of the Range. You can check the size of the array using UBound:
myRange.Resize(UBound(Arr, 1), UBound(Arr, 2))
You access data in the array by specifying the position in each dimension:
Arr(2, 3) = 7
Edit due to extra info about the question:
The example below creates an empty array and sizes it according to the number of scenarios, then stores values as it goes through the loop. The values from the loop are written to the output range after the loops are complete:
Option Base 1
Sub Test_Scenarios()
Dim i As Long, Scenario_Count As Long
Dim j As Integer
'Delete current values on "Testing Output" tab
Sheets("Testing Output").Range("B1:B3").ClearContents
Sheets("Testing Output").Range("A6:AA1000000").ClearContents
'Test scenarios
Scenario_Count = Sheets("Testing Input").Range("B1").Value
Dim arr() As Variant
ReDim arr(Scenario_Count, 2)
Dim outputRange As Range
Set outputRange = Sheets("Testing Output").Range("R5")
Set outputRange = outputRange.Resize(Scenario_Count, 2)
For i = 1 To Scenario_Count
For j = 1 To 2
'Calculate
Sheets("User Input").Range("B26").Value = Sheets("User Input").Range("B26").Value + i + j
'UI
arr(i, j) = Sheets("User Input").Range("B26").Value
Debug.Print "i: " & i & " j: " & j & " value: " & arr(i, j)
Next j
Next i
outputRange.Value = arr
End Sub
The loops are still reading and writing to the spreadsheet, as we don't have any other information about the calculations.