this is my first foray into VBA. the follow subroutine computes a t-test for two columns of data on Sheet1.
the problem is this subroutine returns a value different from what i get when i manually run "=T.TEST(A1:A41,B1:B96,2,3)" in, say, cell D1 on the worksheet. (the numbers in the table don't really matter. i've tested with real data as well as 1 to 41 in column A1:A41 and 1 to 96 in column B1:B96.) can you confirm this? is there a bug in the code? thanks.
Sub dummy_ttest()
Dim rng0 As Range
Dim rng1 As Range
Set rng0 = Sheets("Sheet1").Range("A1:A41")
Set rng1 = Sheets("Sheet1").Range("B1:B96")
Dim td0() As Double
Dim td1() As Double
ReDim td0(rng0.Count) As Double
ReDim td1(rng1.Count) As Double
Dim i As Integer
Dim v As Variant
'copy rng0 to td0
i = 0
For Each v In rng0
td0(i) = v.value
i = i + 1
Next v
'copy rng1 to td1
i = 0
For Each v In rng1
td1(i) = v.value
i = i + 1
Next v
Dim myttest As Double
myttest = Application.WorksheetFunction.T_Test(td0, td1, 2, 3)
MsgBox myttest
End Sub
Use variant arrays and bulk load them:
Sub dummy_ttest()
Dim rng0 As Range
Dim rng1 As Range
Set rng0 = Sheets("Sheet1").Range("A1:A41")
Set rng1 = Sheets("Sheet1").Range("B1:B96")
Dim td0() As Variant
Dim td1() As Variant
td0 = rng0.Value
td1 = rng1.Value
Dim myttest As Double
myttest = Application.WorksheetFunction.T_Test(td0, td1, 2, 3)
MsgBox myttest
End Sub
Scott has an excellent answer but adding context / converting my comment to an answer:
One thing I can see is that your arrays are one element bigger than your ranges
The issue is that your arrays are 0-based, but the range is one-based. Your array is equivalent to ReDim td0(0 to rng0.Count) As Double, but the range has 1 to rng0.Count cells. It's not an issue of ReDim at all.
The range A1:A41 has 41 cells, but your array has 42 elements; 0 to 41 means you have one too many. So in your current approach, you never actually populate the last element of the array and thus it is 0 by default.
You can (and should) specify the lower bound of your arrays, i.e.
ReDim td0(0 to rng0.Count - 1) As Double '<~ 0-based
or
ReDim td0(1 to rng0.Count) As Double '<~ 1-based
From the ReDim docs:
When not explicitly stated in lower, the lower bound of an array is controlled by the Option Base statement. The lower bound is zero if no Option Base statement is present.
Related
Code worked fine with fixed dim array but when I have added line to Redim to make Results array dynamic the code will not allow results to be added to array coming up with "Type Mismatch" error. I am sure a simple fix, have played around but can I see it..
GP (Gross Profit) Range of values picked up form single column range GPRange and Price Change its from single row PriceRange).
Option Explicit
Sub CalcVolTable()
'Macro to produce table that shows relationship between price and volume change
Dim PriceChangeAr As Variant, GPAr As Variant
Dim GPList, GPNum, PriceIndex, PriceChNum As Integer
Dim GP As Double
With ThisWorkbook
' Read all PriceChanges into a 1-dimensional array
PriceChangeAr = (.Worksheets("Results").Range("PriceRange").Value2)
' Read all GP range into a 1-dimensional array
GPAr = Application.Transpose(.Worksheets("Results").Range("GPRange").Value)
'Clear Previous Results
Range("VolTable").ClearContents
Range("Output").Select
'Set up Results Array
'Dim VolResultsAr(1 To 8, 1 To 7) As Variant - this worked before I tried to make array dynamic
Dim VolResultsAr As Variant
PriceChNum = UBound(PriceChangeAr, 2)
GPNum = UBound(GPAr)
ReDim VolResults(1 To GPNum, 1 To PriceChNum) As Variant
For GPList = LBound(GPAr) To UBound(GPAr)
GP = GPAr(GPList)
'Set Cost per Unit value to get right GP in calc
Range("CostPerUnit").Value = 100 * (1 - GP)
' Now loop through each pricechange
For PriceIndex = LBound(PriceChangeAr, 2) To UBound(PriceChangeAr, 2)
'Reset Price and Vol adjt cell to zero
Range("ChPrice") = 0
Range("Chvol") = 0
'enter new Price Cahnge value
Range("ChPrice").Value = PriceChangeAr(1, PriceIndex)
'Use goal seek to calc vol chage req'd to bring GP back to same preset value
Range("GP").GoalSeek Goal:=GP, ChangingCell:=Range("ChVol")
'Writes result to cells in table in spreadsheet
Range("Output").Offset(GPList - 1, PriceIndex - 1).Value = Range("ChVol").Value
'CODE FALLING DOWN HERE - TRYING TO WRITE EACH RESULT INTO ARRAY
VolResultsAr(GPList, PriceIndex) = Range("ChVol").Value
Next PriceIndex
Next GPList
Range("Output2").Resize(UBound(VolResultsAr, 1), UBound(VolResultsAr, 2)) = VolResultsAr
Range("Output").Select
End With
MsgBox "done!"
End Sub
Assuming the arrays need to match you need to ReDim VolResultsAr
ReDim VolResults(1 To GPNum, 1 To PriceChNum) As Variant
ReDim VolResultsAr(1 To GPNum, 1 To PriceChNum) As Variant
And for lines like:
Dim GPList, GPNum, PriceIndex, PriceChNum As Integer .
Only PriceChNum is Integer, the others are variant. Is that what you intended? And Integer should be replaced with Long to avoid potential overflow
This is my first time using array in VBA. I was trying to check the value of my array based on certain condition.
I check my array value through the Locals Window. The window is empty. What did I do wrong?
Option Explicit
Sub test()
'define dynamic array
Dim sn As Variant
Dim i As Long
'Loop through all the row
For i = 1 To Rows.Count
If Cells(i, 12).Value = "Renewal Reminder" And Not IsEmpty(Cells(i, 12).Value) Then
'assign cell value to array
sn = Cells(i, 1).Value
Debug.Print "aaa" ' there are 8 cell values that meet the condition
End If
Next i
End Sub
Update
Dim sn as Varient was highlighted with Error
user-defined type not defined
Apart from the typo showing in the error message, you are not actually using sn as an array - you are simply storing each value in a scalar variable, replacing what was previously in that variable.
The following should work for you:
Option Explicit
Sub test()
'define dynamic array
Dim sn As Variant
Dim cnt As Long
Dim i As Long
ReDim sn(1 To 1)
cnt = 0
'Loop through all the row
For i = 1 To Cells(Rows.Count, "L").End(xlUp).Row
If Cells(i, 12).Value = "Renewal Reminder" Then
'assign cell value to array
cnt = cnt + 1
ReDim Preserve sn(1 To cnt)
sn(cnt) = Cells(i, 1).Value
Debug.Print "aaa" ' there are 8 cell values that meet the condition
End If
Next i
For i = 1 To cnt
Debug.Print sn(i)
Next
End Sub
As mentioned in the answer by Chemiadel, it is better to declare your variables using the appropriate base type if you know what that is.
So, if you know that column A contains text, replace Dim sn As Variant with
Dim sn() As String
or, if it is a double-precision number, use
Dim sn() As Double
etc. If column A could contain various different types, using Variant could be appropriate.
Note: You don't have to include the () when using Variant because Variant variables can switch happily between being scalars, arrays, objects, etc.
You need to declare Array with this way and avoid Variant data type :
Static Array : fixed-size array
dim sn(10) as String
Dynamic Array : you can size the array while the code is running.
dim sn() as String
Use ReDim Preserve to expand an array while preserving existing values
ReDim Preserve sn(UBound(sn) + 10)
Check the reference
I'm very new to VBA, to bear with me here.
I want to assign a set of variables the value of a set of ranges ie. run a brief code to simplify the following
Dim Sample 1 as string
Sample1 = activeworksheet.range("C17").value
Dim Sample 2 as string
Sample2 = activeworksheet.range("C18").value}
and so on
Following an excelfunctions.net tutorial, I know that I can shorten the declaration to
Dim Sample(1 to 20) as a string
But the tutorial drops it there(because it's a tutorial about names), suggesting I populate it as follows
sample(1)=activesheet.range("C7").value
sample(2)=activesheet.range("C7").value
and so on
I found the discussion below to be on the right track to answer my quest, but I am having trouble applying it to my situation. (Excel VBA Array Ranges for a loop)
As a follow up note, I am ultimately trying to assign values to these variables for use in the following procedures, rather than declaring and assigning them each time.
Thanks!
Try something like this:
Sub test()
Dim sampleArr(1 To 20) As String
Dim i As Integer
Dim rng As Range, cel As Range
i = 1
Set rng = Range("C1:C20")
For Each cel In rng
sampleArr(i) = cel.Value
i = i + 1
Next cel
For i = LBound(sampleArr) To UBound(sampleArr)
Debug.Print sampleArr(i)
Next i
Also, if you know the range you want to put into an array, you can simply set an array to that range:
Sub test()
Dim sampleArr() As Variant
Dim i As Integer
Dim rng As Range, cel As Range
i = 1
Set rng = Range("C1:C20") ' Note, this creates a 2 Dimensional array
sampleArr = rng ' Right here, this sets the values in the range to this array.
For i = LBound(sampleArr) To UBound(sampleArr)
Debug.Print sampleArr(i, 1) ' you need the ",1" since this is 2D.
Next i
End Sub
You should :
Define the range you want to retrieve data
For each cell of the range, retrieve your datas
dim tab() As string, cell as range, i as integer
i = 0
redim tab(0)
for each cell in ActiveWorksheet.Range("C1:C20")
tab(i) = cell
i = i + 1
redim preserve tab(i)
next
edit : I indent the code to display it correctly
Additional way to the above you can only use:
Arr = ActiveWorksheet.Range("C1:C20").Value
Then you can directly use:
Arr(i,1) where i is C1 to C20 range!
Using Excel (2010) VBA, I am trying to copy (pass) a constant range of cells (whose values recalculate) to an array. Then I am trying to pass that array to a new range of cells, directly below it. After I have done this, I want to again copy (pass) the constant range's new values to the array, and pass these new values to a range directly below the one I previously passed.
I know this code is atrocious (I am new to arrays in VBA).
Sub ARRAYER()
Dim anARRAY(5) As Variant
Number_of_Sims = 10
For i = 1 To Number_of_Sims
anARRAY = Range("C4:G4")
Range("C4").Select
ActiveCell.Offset(Number_of_Sims, 0).Select
ActiveCell = anARRAY
Range("C4").Select
Next
End Sub
I sure do appreciate your help!
Thank you.
Respectfully,
Jonathan
You are off slightly on a few things here, so hopefully the following helps.
Firstly, you don't need to select ranges to access their properties, you can just specify their address etc. Secondly, unless you are manipulating the values within the range, you don't actually need to set them to a variant. If you do want to manipulate the values, you can leave out the bounds of the array as it will be set when you define the range.
It's also good practice to use Option Explicit at the top of your modules to force variable declaration.
The following will do what you are after:
Sub ARRAYER()
Dim Number_of_Sims As Integer, i As Integer
Number_of_Sims = 10
For i = 1 To Number_of_Sims
'Do your calculation here to update C4 to G4
Range(Cells(4 + i, "C"), Cells(4 + i, "G")).Value = Range("C4:G4").Value
Next
End Sub
If you do want to manipulate the values within the array then do this:
Sub ARRAYER()
Dim Number_of_Sims As Integer, i As Integer
Dim anARRAY as Variant
Number_of_Sims = 10
For i = 1 To Number_of_Sims
'Do your calculation here to update C4 to G4
anARRAY= Range("C4:G4").Value
'You can loop through the array and manipulate it here
Range(Cells(4 + i, "C"), Cells(4 + i, "G")).Value = anARRAY
Next
End Sub
No need for array. Just use something like this:
Sub ARRAYER()
Dim Rng As Range
Dim Number_of_Sims As Long
Dim i As Long
Number_of_Sims = 10
Set Rng = Range("C4:G4")
For i = 1 To Number_of_Sims
Rng.Offset(i, 0).Value = Rng.Value
Worksheets("Sheetname").Calculate 'replacing Sheetname with name of your sheet
Next
End Sub
Since you are copying tha same data to all rows, you don't actually need to loop at all. Try this:
Sub ARRAYER()
Dim Number_of_Sims As Long
Dim rng As Range
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Number_of_Sims = 100000
Set rng = Range("C4:G4")
rng.Offset(1, 0).Resize(Number_of_Sims) = rng.Value
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
When i Tried your Code i got en Error when i wanted to fill the Array.
you can try to fill the Array like This.
Sub Testing_Data()
Dim k As Long, S2 As Worksheet, VArray
Application.ScreenUpdating = False
Set S2 = ThisWorkbook.Sheets("Sheet1")
With S2
VArray = .Range("A1:A" & .Cells(Rows.Count, "A").End(xlUp).Row)
End With
For k = 2 To UBound(VArray, 1)
S2.Cells(k, "B") = VArray(k, 1) / 100
S2.Cells(k, "C") = VArray(k, 1) * S2.Cells(k, "B")
Next
End Sub
Im coming from a Unix world where I never had to develop something for Office with VBA, I have to do some now and Im having a hard time! Please help me! :)
So I've got 2 Excel Sheets(lets call them Sheet1 and Sheet2) and 2 forms(Form1 and Form2) to edit/add data.
In Sheet1, the first two columns are MovieId and MovieName. We dont know how many rows they will be in this columns.
Form1 controls data in Sheet1, and Form2... in Sheet2.
At Form2 initialization, I want to create a 2 Dimensional Array that will be like (MovieId1,MovieName1;MovieId2,MovieName2;...,...;MovieIdN,MovieNameN), where this data has been extracted from Sheet1, like a sort of Map in Java if you will...
It would actually be ok for me if it was like: (0,"MovieId0;MovieName0";1,"MovieId1,MovieName1";..,"..";N,"MovieIdN,MovieNameN")
I dont know how to create the array with an variable last row number, since the compiler seems to always want a constant to initialize an Array...
Please enlighten me!
Look at the Value method or Value2 property.
e.g. Range("$A$2:$B$4").Value2(1,1)
or
Range("$A$2:$B$4").Value()(1,1)
Array's lower bound start from 1.
lbound(Range("$A$2:$B$4").Value2, 1) - row element starts from
ubound(Range("$A$2:$B$4").Value2, 2) - row element ends
lbound(Range("$A$2:$B$4").Value2, 2) - column element starts from
ubound(Range("$A$2:$B$4").Value2, 2) - column element ends
EDIT: Code to traverse through the array
Dim myAddress As String
Dim dataArray As Variant
Dim rowStart As Long, rowEnd As Long
Dim colStart As Long, colEnd As Long
Dim rowCtr As Long
Dim colCtr As Long
myAddress = "$A$2:$B$4"
dataArray = Range(myAddress).Value2
rowStart = LBound(dataArray, 1)
rowEnd = UBound(dataArray, 1)
colStart = LBound(dataArray, 2)
colEnd = UBound(dataArray, 2)
For rowCtr = rowStart To rowEnd
For colCtr = colStart To colEnd
Debug.Print rowCtr & ":" & colCtr, vbTab & dataArray(rowCtr, colCtr)
Next
Next
EDIT2: In my example, I have assumed the address to be $A$2:$B$4.
You can prefix it with sheet name. e.g. Sheet1!$A$2:$B$4 or Sheet2!$A$2:$B$4
On a side note, array can be defined dynamic (if it is 1 dimensional).
e.g dim my1DArray() as Integer
For double dimension array, see the following code
Dim myArray
Dim dynamicRows As Integer
dynamicRows = 2
ReDim myArray(0 To dynamicRows, 0 To dynamicRows)
myArray(0, 0) = "hello"
dynamicRows = 20
ReDim myArray(0 To dynamicRows, 0 To dynamicRows)
MsgBox myArray(0, 0)
myArray(0, 0) = "hello"
ReDim Preserve myArray(0 To dynamicRows, 0 To dynamicRows)
MsgBox myArray(0, 0)
Rather use the Range object, with this you can also use the UsedRange from the sheet
Sub Macro1()
Dim sheet As Worksheet
Dim range As range
Dim row As Integer
Set sheet = Worksheets("Sheet1")
Set range = sheet.UsedRange
For row = 1 To range.Rows.Count
Next row
End Sub
assuming the data starts in A1
Dim vArr as variant
vArr=worksheets("Sheet1").range("A1").resize(worksheets("Sheet1").range("A65535").end(xlup).row,2)
Do you mean:
Dim thearray() As Variant
ReDim thearray(1, range.Rows.Count)
You can also use a recordset and GetRows to return an array from a worksheet.
Slight mod to Charles' answer:
Dim vArr as variant
vArr = Worksheets("Sheet1").Range("A1").CurrentRegion.Value
Assuming of course that there isn't any stray data in Sheet1.