Is there a way like in MATLAB to assign many values in an array's row?
Let's say you have a matrix M(5,5) and a Vector V = [1, 2, 3, 4, 5]
In Matlab, If you wanted the first row to be filled up with vector V, you would write:
M(1,:) = V
Is there something similar in Excel VBA instead of looping through each column of the first row and assign the value V(j) every time?
You could try something along these lines:
Sub InitializeMatrixWithVector()
Dim V(), M()
ReDim M(1 To 5, 1 To 5)
V = Array(1, 2, 3, 4, 5)
M = Application.Choose([1+(row(1:5)=1)], M, V)
Range("B1").Resize(UBound(M), UBound(M, 2)) = M
End Sub
The square brackets are equivalent to the Evaluate VBA function.
Application.Choose is a WorksheetFunction method that can take and return variant arrays.
Related
I'm a beginner on VBA. I have been following SO for years but have never really posted. I'm really struggling to understand a concept and have found no answers elsewhere.I want to use a for loop that 's going to loop these three arrays going like the following:
EUR_Buy = (1,2,3,4,5,6)
USD_BUY = (2,4,6,8,10,12)
GBP_BUY = (1,3,5,7,9,11)
curr = (EUR,USD,GBP)
For i = 0 To 2
For j = 0 To 5
If curr(i) & "_BUY" & (j) = 8
MsgBox Yes
End If
Next j
Next i
The only thing I get is the name of the variable (ex: Eur_Buy(0) but not the value of the value which would be "1". Any idea how I could get this? Would be very helpful).
Thanks a lot and please do not hesitate if you have any questions.
You cannot create a string from pieces and then expect the runtime to use this as variable name.
If you have a list of names and associated values, you can use a Collection (or a Dictionary).
The following piece of code gives you the idea how to use them.
' Create collection and fill it with 3 elements, each holding an array of 6 values
Dim myVars As New Collection
' Elements are added to a collection with add <value>, <key>
myVars.Add Array(1, 2, 3, 4, 5, 6), "EUR_Buy"
myVars.Add Array(2, 4, 6, 8, 10, 12), "USD_BUY"
myVars.Add Array(1, 3, 5, 7, 9, 11), "GBP_BUY"
Dim curr as Variant
Dim j As Long
For Each curr In Array("EUR", "USD", "GBP")
Dim key As String
key = curr & "_BUY"
' You can access an element of a collection with it's key (name) or index.
For j = 0 To 5
If myVars(key)(j) = 5 Then Debug.Print curr, j, "Found 8 in " & key
Next
Next
Referencing an Array of arrays via Enum statement
If you have to deal with a greater number of currencies, it can increase readibility to
use an enumeration defined in the head of a code module and to
reference an Array of arrays (aka jagged array) by these placeholder variables in the main code and which
holds the individual currency arrays for its part; you may think it as sort of container.
Option Explicit ' head of code module
Enum C ' Enum statement allows automatic increments (if no special assignments)
[_Start] = -1
EUR
USD
GBP
LastElement = GBP ' (re-)set to last currency (here GBP), if changed
End Enum
Note that you can easily insert or add other currencies without caring in further code for the actual number as Enum automatically increments the start element (if not assigned explicitly).
The following example code
assigns the individual arrays (starting a little bit tricky with the "Name" of the array as string value, e.g. "EUR") to buy() serving as container array and
executes a Match over all enumerated currencies eventually.
Sub ExampleCall()
'1) define zero-based buy arrays referenced by Enum values (~> module top)
Dim buy(C.LastElement) ' declare 0-based Array of arrays
buy(C.EUR) = Array("EUR", 1, 2, 3, 4, 5, 6) ' assign the individual arrays
buy(C.USD) = Array("USD", 2, 4, 6, 8, 10, 12)
buy(C.GBP) = Array("GBP", 1, 3, 5, 7, 9, 11)
'2) define a search value
Dim srch As Variant
srch = 5
'3) find value 5
Dim curr As Long
For curr = 0 To C.LastElement
Dim no As Variant
no = Application.Match(srch, buy(curr), 0) ' << Find ordinal element position
If IsNumeric(no) Then ' check for valid findings only
no = no - 1 ' adjust counter as Match returns 1-based numbers
'4) display result of individual sub-array buy(curr)
Debug.Print _
buy(curr)(0), _
"index " & no, _
"Found " & buy(curr)(no) & " in " & buy(curr)(0) & "_BUY"
End If
Next
End Sub
Note that Application.Match always returns a 1-based position number (adjusted to the 0-based index by a -1 subtraction) within the individual arrays or an Error if there is no finding at all; checking the no result by IsNumeric allows to get only valid findings.
Results in the VB Editor's immediate window would be displayed e.g. as follows:
EUR index 5 Found 5 in EUR_BUY
GBP index 3 Found 5 in GBP_BUY
Im using a Adodb connection
and reading the result of a query
into an array with
array = recordSet.GetRows()
which leads to a transposed array of dimensions
(row,col)
(0, 0)
(0, 1)
(0, 2)
instead of
(row,col)
(0, 0)
(1, 0)
(2, 0)
so it should be a 3 x 1 not 1 x 3 array
Any suggestions ?
It is because it is returning an Array containing (intField, intRecord):
https://msdn.microsoft.com/en-us/library/office/ff194427.aspx
So it's a matter of different interpretation in the way that intField is basically the Column and intRecord the Row (both zero-based).
note that (using ADODB driver):
Range("A1").CopyFromRecordset recordset ;does not transpose a
;recordset
varArray = recordset.GetRows(10) ;transposes a recordset
Range("A1:L10").Value = varArray ;does not transposes array
listbox.list = varArray ;does not transposes array
listbox.column = varArray ;transpose array
listbox.column = recordset.GetRows(10) ;does not transpose a
;recordset
Preface: Messing around with cube math and after looking around at all the various options I seem to have run into an issue with my current structure. Maybe someone would love to point out what I am missing here?
Private Sub cube3()
Dim x(0 To 2, 0 To 2) As Single
Dim y(0 To 2, 0 To 2) As Single
Dim z(0 To 2, 0 To 2) As Single
For a = 0 To 2
For b = 0 To 2
Count = (Count + 3) / 2 * 1.5
x(a, b) = Count
y(a, b) = Count
z(a, b) = Application.WorksheetFunction.MMult(x, y)(a, b) '****** This is where the issue occurs
Debug.Print ; x(a, b)
Debug.Print ; z(a, b)
Next
Next
End Sub
In this instance MMULT() is not the way to go it is not made for single value multiplication.
It systematically does something similar to SUMPRODUCT(). As it multiplies each item in a row in the First array with each item in a column in the second array.
Becomes
See HERE for a much better explanation.
In this code you are multiplying single values so a simple multiplication would work:
= x(a,b)*y(a, b)
As far as my research has found to multiply each value in one array with its sister value in a second array, the only method is to loop.
You are already using the quickest method by which to do this. By using array instead of worksheet ranges even with larger datasets this will still be pretty quick.
If you want to use the MMULT() then fill both arrays first and declare z as a variant:
Private Sub cube3()
Dim x(0 To 2, 0 To 2) As Single
Dim y(0 To 2, 0 To 2) As Single
Dim z
For a = 0 To 2
For b = 0 To 2
Count = (Count + 3) / 2 * 1.5
x(a, b) = Count
y(a, b) = Count
Debug.Print x(a, b)
Debug.Print y(a, b)
Next
Next
z = Application.WorksheetFunction.MMult(x(), y()) '****** This is where the issue occurs
Range("A1").Resize(UBound(z, 1), UBound(z, 2)).Value = z
End Sub
I have a need to run successive passes of built in excel functions on a single matrix of input.
The problem is, the input [range] is what I assume, a pointer constant.
So sure, I can do a WorkSheetFunction calculations on the [range] input and place the output into a variant.
But, I do have a need to run more passes on the variant data. I have a more advanced calculation that is going to run 4 transforms on data that use standard excel functions like average, and median.
Here's my code
Public Function RankECDF(ByRef r_values As Range, Optional ByVal zeroFlag As Boolean = 0) As Variant()
Dim i As Integer, j As Integer, N As Integer, M As Integer
Dim total As Integer
Dim y() As Variant
N = r_values.Rows.Count
M = r_values.Columns.Count
y = r_values.Value 'copy values from sheet into an array
Dim V() As Variant
Dim AltV As Variant
Dim OutV As Variant
Dim OutAltV As Variant
'quite possible to makes the Variant larger to hold the "other arrays"
ReDim V(1 To N, 1 To M)
ReDim AltV(1 To N, 1 To M)
ReDim OutV(1 To N, 1 To M)
ReDim OutAltV(1 To N, 1 To M)
'first pass just checks for zero's. Could speed this process up by implementing the zeroFlag check to skip the double loop
total = WorksheetFunction.Sum(r_values)
For R = 1 To N
For C = 1 To M
If y(R, C) = "" Then
V(R, C) = ""
AltV(R, C) = 0
Else
'would error if cell was ""
'V(R, C) = WorksheetFunction.Average(WorksheetFunction.Rank(y(R, C), r_values, 1), WorksheetFunction.CountIf(r_values, "<=" & y(R, C))) / WorksheetFunction.Count(r_values)
V(R, C) = y(R, C)
AltV(R, C) = y(R, C)
End If
Next C
Next R
'second loop does rankecdf conversions
For RA = 1 To N
For CA = 1 To M
'OutV(RA, CA) = 1
'OutV(RA, CA) = WorksheetFunction.Rank(V(RA, CA), V, 1)
'OutAltV(RA, CA) = 2
'OutAltV(RA, CA) = WorksheetFunction.Average(WorksheetFunction.Rank(y(RA, CA), r_values, 1), WorksheetFunction.CountIf(r_values, "<=" & y(RA, CA))) / WorksheetFunction.Count(r_values)
Next CA
Next RA
If (zeroFlag) Then
RankECDF = AltV
'RankECDF = OutAltV(1 to N, 1 to M)
Else
RankECDF = V
'RankECDF = OutV(N, M)
End If
End Function
The problem can be identified right around here:
OutV(RA, CA) = WorksheetFunction.Rank(V(RA, CA), V, 1)
WorksheetFunction.Rank(y(R, C), r_values, 1)
You cannot put an Array on arg1. Just do:
i = y(R, C)
Then:
WorksheetFunction.Rank(i, r_values, 1)
It worked fine for me
Updated from comments as I see the answer I initially posited misread the problem:
As a general rule, arrays and performing calculations purely in memory are faster than you might think. For one example I used to use the Application.Match function to find the index position of a value in an array, rather than simple brute force iteration. Turns out that iteration was a far faster (up to 10x faster!!!) method. Check out Tim's answer to my question about Matching values in a string array.
I suspect it is the same with rank/sorting. Worksheet functions are expensive. For/Next is not, relatively speaking.
As for the specific needs to rank from an array, there are examples of custom functions which rank and sort arrays, collections, dictionaries, etc. I ultimately end up using a bunch of Chip Pearson's Array helper functions, he has a number of them; which do really cool sh!t like reversing an array, sorting array, determining whether an array is allocated (I use this one a lot) or empty, or all numeric, etc. There are about 30 of them.
here is the code to sort an array.
Note: I did not post his code because there is a lot of it. WHile it appears daunting, because it is a lot of code to re-invent the wheel, it does work and saves a lot of trouble and is very useful. I don't even use these in Excel, since I do most of my dev in PowerPoint now -- I think all of these modules ported over with zero or almost zero debugging on my end. They're really buttoned up quite nicely.
Getting the rank
Once the array is "sorted" then determining the rank of any value within it is trivial and only requires some tweaking since you may want to handle ties appropriately. One common way of dealing with ties is to "skip" the next value, so if there is a two-way tie for 2nd place, the rank would go {1, 2, 2, 4, 5, 6, etc.}
Function GetRank(arr As Variant, val As Variant)
'Assumes arr is already sorted ascending and is a one-dimensional array
Dim rank As Long, i As Long
Dim dictRank As Object
Set dictRank = CreateObject("Scripting.Dictionary")
rank = 0
For i = LBound(arr) To UBound(arr)
rank = rank + 1
If dictRank.Exists(arr(i)) Then
'Do nothing, handles ties
Else
'store the Key as your value, and the Value as the rank position:
dictRank(arr(i)) = rank
End If
If arr(i) = val Then Exit For
Next
GetRank = rank
End Function
I'm trying to teach myself some basic VBA in Excel 2010 and I've come across a problem I can't google myself out of. The objective is to create a button which when pressed, automatically does linest for me and writes the values into an array. So far, this is my code.
Private Sub CommandButton1_Click()
Dim linest As Variant
Dim linestArray(4,1) as Variant
Dim i As Integer, j as Integer
linest = Application.LinEst(Range("U49:U51"), Range("T49:T51"), True, True)
For i = 0 To 4
linestArray(i,0) = accessing values of linest variable fyrst column
Cells(68 + i, 21) = linestArray(i,0)
Next
For j = 0 To 4
linestArray(j,1) = accessing values of linest variable second column
Cells(68 + j, 22) = linestArray(j,0)
Next
End Sub
How do I access the values of variable linest so I can store them to an array and print them? Thank you.
EDIT: I figured it out. Variable linest is already an array! I feel pretty dumb. Sorry, this can be ignored.
New code:
Dim linestArray As Variant
linestArray = Application.LinEst(Range("U49:U51"), Range("T49:T51"), True, True)
For i = 0 To 4
For j = 0 To 1
Cells(68 + i, 21 + j) = linestArray(i + 1, j + 1)
Next
Next
The output of any such formula is a Variant array. So you've got that part right.
For a general approach to these Application. (use WorksheetFunction. instead, it's much faster) type functions is...
Type the function in Excel (as an array formula, Ctrl-Shift-Return, if need be)
The output is an N x M matrix of some sort (N =1 , M =1) for most cases
When you do Var = Application.Linest(xyx), the answer gets stored in Var
Get the size of the Var using Ubound(Var, 1), Ubound(Var, 2) to get number of rows and columns (note that these are base 0 type arrays)
Usually, the size will be one x one. In that case, your variable is stored in Var(0,0) i.e. a zero base multidimensional variant array, the top left element.
Hope this helps.