Moving an Array to a Range VARIABLE - arrays

I am trying to find this but I can't! Is it possible to move/copy and VBA Array to a Range Variable, I am not saying a Range cells, it's a Range that you declare (ex: DIM ran as Range)
I want something like this:
Public Function test()
Dim ran As Range
Dim myArray(4) As Integer
myArray(1) = 1
myArray(2) = 2
myArray(3) = 3
myArray(4) = 4
'If I do this, it works!
Range("A1:A4") = myArray
'But I want this and it does not work!
ran = myArray
End Function
Thanks!

Even though the Range is declared in code, it's still a Range, not an array. If myArray is actually a Range object (in which case you should re-think your naming conventions) you should be able to do what your example showed.
However if your variable myArray is some other type of object (like and array) you can't just set it like that, you would have to write a method that converts myArray to a range.
EDIT:
I'm guessing that the reason that Range("A1:A4") = myArray works is because the assignment operator has been overloaded to support it as short-hand for Range("A1:A4").Value = myArray.
A Range however is not just an array, it's a data structure specific to Ranges of a workbook
When you declare Dim ran As Range you haven't actually initialized your Range object yet. I'm guessing that if you did the following it would work:
Dim ran As Range("A1:A4")
Dim myArray(4) As Integer
myArray(1) = 1
myArray(2) = 2
myArray(3) = 3
myArray(4) = 4
ran = myArray

If you set a variant to a worksheet range you get a 2 dimensional array. Try this
Sub MoveArray()
Dim myArray
myArray = Range("A1:B3").Value
Range("A6:B8").Value = myArray
End Sub
Or this
Sub MoveArray2()
Dim myArray
Dim myArray2
Dim myRange
Dim i As Integer
Dim j As Integer
myArray = Range("A1:B3").Value
Set myRange = Range("A6:B8")
For i = 1 To myRange.Rows.Count
For j = 1 To myRange.Columns.Count
myRange.Cells(i, j) = myArray(i, j)
Next j
Next i
myArray2 = myRange
End Sub
In either case the variant becomes an array rather than a range object and the loops set the values of the range from the array.
Edit
As you can see from the comments in the questions/answers in this thread a range is not an array, it is a complex object that has many properties and methods. One of the properties (I think the default property) is value. This property is in fact a 2 dimensional array (or at least behaves like one) of the values of the cells in the range. As such if myArray is also a 2 dimsensional aray the code myRange = myArray will execute. What will happen is the value of the cells that the range refers to will be set to the values in the array. The range refers to these cells, but it is the values in the cells that change rather than the range object.
As such if if you transfer a range to a diferent set of cells the new set of cells retains it's values.
On a practical side if you want to change the values of the cells that a range referes to you can do it as shown below:
Sub Test1()
Dim myArray(1 To 4, 1 To 1)
Dim myRange As Range
myArray(1, 1) = 1
myArray(2, 1) = 2
myArray(3, 1) = 3
myArray(4, 1) = 4
Set myRange = Range("A1:A4")
myRange = myArray
End Sub

Related

different results between VBA t_test and native excel t_test

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.

Creating an array from named range

I'm trying to create few arrays from named range in my workbook instead of defining that arrays manually.
Example:
This is the array I want to create
Array("bardzo lekka", "lekka", "średnia", "ciężka")
The same data is definied in named range $A$5:$A$8. I want to load that range into array like above.
I've tried to do this like that:
kategoria_a = ActiveWorkbook.Names("kategoria_agronomiczna_gleby").RefersToRange(1, 1)
It creates only one object array.
kategoria_a = ActiveWorkbook.Names("kategoria_agronomiczna_gleby")
That makes
kategoria_a = ='Dane wyjściowe'!$A$5:$A$8
Thanks in advance.
if your values are listed in along a column you could use:
kategoria_a = Application.Transpose(ActiveWorkbook.Names("kategoria_agronomiczna_gleby").RefersToRange.Value)
while if they are listed along a row then you could use
kategoria_a = Application.Transpose(Application.Transpose(ActiveWorkbook.Names("kategoria_agronomiczna_gleby").RefersToRange.Value))
to obtain a 1D array
Try this:
Dim a As Variant
Dim i As Long
Dim rng As Range, r As Range
a = Array("bardzo lekka", "lekka", "średnia", "ciężka")
For i = LBound(a) to UBound(a)
If i = LBound(a) Then
Set rng = Range(a(i))
Else
Set rng = Union(rng, a(i))
End If
Next i
a contains all named ranges. To access the values you could use this:
For Each r In rng
debug.print r.value
Next r
This will take all the named ranges, and put them in an array (myArr):
Sub get_named_ranges()
Dim rng As Range
Dim nm
Dim myArr() As Variant
ReDim myArr(1 To ThisWorkbook.Names.Count)
Dim i As Long
i = 1
For Each nm In ThisWorkbook.Names
myArr(i) = nm.Name
i = i + 1
Next nm
For i = LBound(myArr) To UBound(myArr)
Debug.Print (myArr(i))
Next i
End Sub
If you mean you want to set an array equal to the values in a named range,instead of entering them manually (like your example), then just set the array equal to the range. As long as "kategoria_agronomiczna_gleby" is your named range.
kategoria_a = ActiveWorkbook.Range("kategoria_agronomiczna_gleby")
You can use this loop to check the values in your array
For i = LBound(kategoria_a, 1) To UBound(kategoria_a, 1)
Debug.Print kategoria_a(i, 1)
Next

Using VBA to assign range of cell values to array of variables

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!

VBA Why does Application.Countif return an array or error 424

I would like to count the number of matching items in an array. I tried using
Application.Countif
MyCount = Application.WorksheetFunction.CountIf(Myrange, val)
but this returns an array full of errors rather than a simple count. I have also tried using Application.WorksheetFunction.Countif but this causes a 424 error.
I currently am testing on a worksheet with a short list of names in cells A1:A20, but ultimately I plan to use this code with a very large CSV file and I want to load the information into an array before using CountIf (rather than using the range).
Sub TestCount()
Dim MyCount
Dim Myrange As Variant
Dim val As String
val = "Addison"
Myrange = ActiveSheet.Range("A1").CurrentRegion.Value
MyCount = Application.WorksheetFunction.CountIf(Myrange, val)
MsgBox (MyCount)
End Sub
Can anyone suggest what I did wrong?
You have several problems.
Using CountIf
First, if I understand right, you are intentially trying to use the Application.WorksheetFunction.CountIf statement on an array. That will only cause trouble, since CountIf (as the statment suggests) is a "worksheet function" not a "VBA Array function".
Ditto has created a solution that uses CountIf correctly, by setting a range in the worksheet on which the CountIf statement performs its job. If all you want is a way to count the value within that range, that is the way to go.
Creating an array from a range
Second, if you really need to get the items out of the worksheet and into an array (for example, if you plan to work with those values in ways you don't want to effect the worksheet), you should know that you have only partially solved the question of creating an array of values from a range selection.
You are correct that to establish an array by assigning a range to a variable you need a variant, but you have forgotten the parenthesis, which are an essential part of denoting an array.So, instead of Dim Myrange As Variant you should use Dim Myrange () As Variant
Having established MyRange as an array, you can now assign the array values by saying MyRange = Range("x") where x is the area being captured. You do not need to (or want to) use .Value for this. VBA will automatically do that for you. So, in your case you want to use the CurrentRegion for Range("A1") which is done like this: MyRange = Range("A1").CurrentRegion. You could also use a closely defined range like this: MyRange = Range("A1:A12") or MyRange = Range("C7:F14"). Note: I left off the ActiveSheet because it does not work when assigning ranges to arrays. The assumption is that you are using the active sheet, and the current region is for the cell indicated in the Range("x") statement.
Counting values within the array
Third, once you have succeeded in creating an array, you won't be able to use Countif (as noted above). You'll need to create a method of counting that value within the array. There are several considerations in doing this.
Since an array created from a range will be two dimensional and may have more than one column, you should not assume just one column. You will want to create a variable that holds the number of rows and number of columns, so you can loop through the entire array. Something like this:
Dim Row As Long
Dim Col As Long
You will want to define the limits of your loops using the UBound of the array dimensions. Something like this:
Dim RowNumber As Integer
RowNumber = UBound(MyRange, 1)
Dim ColNumber As Integer
ColNumber = UBound(MyRange, 2)
Code for using an array to find your count
I think the following code will do what you want using an array created in the manner you were attempting:
Sub TestCount()
Dim MyCount As Long
MyCount = 0
Dim MyRange() As Variant
MyRange = Range("A1").CurrentRegion
Dim val As String
val = "Addison"
Dim Row As Long
Dim Col As Long
Dim RowNumber As Long
RowNumber = UBound(MyRange, 1)
Dim ColNumber As Long
ColNumber = UBound(MyRange, 2)
For Col = 1 To ColNumber
For Row = 1 To RowNumber
If MyRange(Row, Col) = val Then MyCount = MyCount + 1
Next Row
Next Col
msgbox MyCount
End Sub
Just because this horse hasn't been beat enough already..here is a 1 liner
Sub Button3_Click()
MsgBox Application.WorksheetFunction.CountIf(Range("A1:a20"), "Addison")
End Sub
Try this:
Sub TestCount()
Dim MyCount
Dim Myrange As Range
Dim val As String
val = "Addison"
Set Myrange = ActiveSheet.Range("A1:a20")
MyCount = Application.WorksheetFunction.CountIf(Myrange, val)
MsgBox (MyCount)
End Sub
1) define "Myrange" as a RANGE, not a variant.
2) use "set" keyword to assign range to Myrange
3) give it the range you want: "a1:a20", not just "a1"
Yes, you didn't declare you range as a range type, so you didn't set the range.
Sub Macro1()
Dim val as String
Dim r As Range
Set r = Range("a1:a20")
val = "Addison"
MsgBox Application.WorksheetFunction.CountIf(r, val)
End Sub
or
Sub CritSrh_Column()
Dim cell As Variant
Dim counter As Integer
For Each cell In Range("A1:A20")
'could use ("A:A") to search the whole column #not recommended#
'for dynamic rows, use end.xl('direction')
If cell.Value = "Addison" Then
counter = counter + 1
End If
Next
MsgBox counter
End Sub

Populate dynamic multi-dimensional, mult-type arrays array Excel VBA

I am trying to use excel 2010 VBA to populate an array containing three arrays. The first is a string type array and the other two are integer type arrays. The relevant portion of the macro is below.
Option Explicit
Option Base 1
Private Type T_small
myStr() As String
y() As Integer
z() As Integer
End Type
Sub ColorByPoint()
On Error GoTo ErrHandler
Dim I As Integer, SCCount As Integer, PCCount As Integer, CLCount As Integer
Dim N As Integer, M As Integer, K As Integer, P As Integer
Dim x() As String, y() As Integer, z() As Integer
Dim pvtItM As Variant
Dim xName As String, str As String
Dim xlRowField As Range
Dim PC As ChartObjects
Dim WS As Sheet3
Dim SC As SeriesCollection
Dim MyObj As Object
Dim PvTbl As Object
Dim CelVal As Integer
Dim rng As Variant, lbl As Variant, vlu As Variant
Dim ItemField1 As PivotItem, ItemField2 As PivotItem
Dim ValueField As PivotField
Dim dField As PivotCell
Dim oPi As PivotItem
Dim acolRng As Range
Dim arowRng As Range
Dim myStr() As String
Dim iData() As T_small
Dim xSSN() As String
Set WS = Application.ActiveWorkbook.ActiveSheet
Set MyObj = Worksheets("Pivot1").ChartObjects("MyChart").Chart
Set PvTbl = Worksheets("Pivot1").PivotTables("PivotTable1")
Set rng = PvTbl.PivotFields("SSN").PivotItems
Set lbl = PvTbl.DataFields
M = 1
SCCount = MyObj.SeriesCollection.Count 'Series count
PCCount = PvTbl.TableRange1.Rows.Count 'Rows Count
CLCount = PvTbl.TableRange1.Columns.Count 'Columns Count
Set acolRng = PvTbl.ColumnRange
Set arowRng = PvTbl.RowRange
Worksheets("Pivot1").Activate
P = PCCount
ReDim Preserve myStr(P)
ReDim Preserve y(P)
ReDim Preserve z(P)
ReDim Preserve iData(P)
For N = 2 To PCCount
ReDim Preserve iData((iData(2).myStr(2)), (iData(N).y(N)),(iData(N).z(N)))
Next N
For I = 2 To PvTbl.TableRange1.Rows.Count Step 1
For K = 2 To PvTbl.TableRange1.Columns.Count Step 1
M = K
N = K
iData(I).myStr(I) = PvTbl.Cells("myStr" & I, "K").Value
iData(I).y(I) = PvTbl.Cells("I", "M").Value
iData(I).z(I) = PvTbl.Cells("I", "N").Value
Next K
Next I
The problem is that the line
ReDim Preserve iData((iData(2).myStr(2)), (iData(N).y(N)), (iData(N).z(N)))
continues to give me a "Run Time error 9 Subscript out of range" error. I've tried everything I can think of to get past this including using "N"'s instead of the "2" indexes throughout, adding and removing parentheses, etc.
What causes the runtime error?
The problem is you are accessing the array indexes of your T_small properties. You never define (or change) the bounds of iData(x).myStr; rather you only define the bounds of myStr, which is not part of your iData array.
In other words, the of bounds error comes from trying to access iData(x).myStr(x) because iData(x).myStr has no bounds defined.
This should work:
' Now that the iData bounds have been defined, update the property bounds.
ReDim Preserve iData(N).myStr(myStr(N))
ReDim Preserve iData(N).y(y(N))
ReDim Preserve iData(N).z(z(N))
Note that I am having a bit of difficulty following exactly what your code is trying to accomplish, so the above only addresses the specific error you are getting.

Resources