I want to be able to populate an array with rows of data at a time instead of element by element. For example, I'd like to get a final product like the 3x3 array:
1 2 3
4 5 6
7 8 9
by populating it with the row 1 2 3, then with 4 5 6, then with 7 8 9.
In Excel, I have formulas set up in cells F1:Z1 that change based on inputs in cells A1:D1. The macro loads the case in A1:D1, and then I want to be able to select cells F1:Z1 and insert them into an array at once, instead of looping through individual elements in F1, G1, H1, ..., Z1. (I have 10,000 cases, so I know the array would have 10,000 rows and 21 columns.)
Is there any way to do this, or would I have to loop through each element individually?
You can actually populate an array in one shot. Suppose your Range of interest is A1:C200 in Sheet1. You can populate the array using the following
Dim v as Variant
v = ThisWorkbook.Worksheets("Sheet1").Range("A1:C200").Value
This will give you a 2D array with 3 columns and 200 rows.
If any one need help, I was looking for a solution to the same need too, and I found this right now.
Here is a solution using "Index" function :
Sub Test()
Dim varArray As Variant
Dim varTemp As Variant
varArray = ThisWorkbook.Worksheets("Sheet1").Range("A1:E10")
varTemp = Application.Index(varArray, 2, 0)
End Sub
In this example, varTemp contains the values of the row number "2" in the range "A1:E10".
Say we have, in the columns A, B and C, from row 1 to 10, this values :
1 a q
2 b w
3 c e
4 d r
5 e t
6 f y
7 g u
8 h i
9 i o
10 j p
then :
Debug.Print varTemp(1)
Debug.Print varTemp(2)
Debug.Print varTemp(3)
will display :
2
b
w
Have a look here : https://usefulgyaan.wordpress.com/2013/06/12/vba-trick-of-the-week-slicing-an-array-without-loop-application-index/
You can actually store the entire row at once. I was trying to do exactly what you are doing here
Consider you have formulae in "A3:Z3" which you want to loop got 1000 times and store values. below are steps
declare a variant
Dim myarray(1000) As Variant
for i = 1 to 1000
myarray(i) = Range("A3:Z3").value2
next i
use the same loop to paste wherever you want
Related
All -
I'm wondering if there's an efficient way to "shift" elements of a 2-dimensional array. Effectively, what I have is triangular data, saved in a VBA array (n x m, where n <= m):
0 1 2 3 4 5
----------------
0 | A B C D E F
1 | G H I J
2 | K L
I'd like to "restructure" this array to:
0 1 2 3 4 5
----------------
0 | A B C D E F
1 | G H I J
2 | K L
The blank values in the array are actually empty strings (""). I'd imagine there's some looping that I could do to perform this with some compute cost, but I'm wondering if there's an efficient approach for subset "shifting" within VBA...
As #TimWilliams commented correctly, you won't do it without any loops. - A possible approach, however reducing loops would be to
write the initial array (named e.g. v) row wise to an empty target range (applying individual offsets you can calculate before) and eventually
assign them back as so called data field array.
The following example code should give you an idea. - Little draw back: in any case you get the array back as 1-based array.
'~~> assign initial (variant) array v as in OP
'...
'~~> calculate column offsets per array row, e.g. {0,2,4}
'...
'~~> shift array rows and write them to any empty target area
Dim startCell As Range: Set startCell = Sheet1.Range("X1")
Dim i As Long, j As Long, tmp As Variant
For i = 1 To UBound(v)
'~~> get individual column offset per row, e.g. {0,2,4}
j = Array(0, 2, 4)(i - 1)
'~~> write next row to target range
startCell.Offset(i, j).Resize(1, UBound(v, 2)) = Application.Index(v, i, 0)
Next i
'~~> get rearranged 1-based 2-dim datafield array
v = startCell.Offset(1).Resize(UBound(v), UBound(v, 2))
If you shift elements within a greater array, you could write the entire array to the target and overwrite only rows you need rearranged (considering to clear these single row ranges before:-)
I have a 2d array, with flexible dimensions:
arr_emissions(1 to n, 0 to m)
Where n is 22 or larger, and m is 6 or larger.
In the smallest case column m = 6 should contain the sum of columns m = 2 - 5.
I could ofcourse simply add them, but as the dimensions of the array are flexible I would like to implement a more robust method, that preferly doesn't loop over the entire array.
I was hoping to implement the native application.WorksheetFormula.Sum(). I saw an implementation in this answer, but that only works for complete rows or columns.
Example:
I have arr_emissions(0 to 111,1 to 6). It is populated in a loop from 1 to 111.
The data in the array is as follows:
(1,1) #3-4-2020# 'a date value
(1,2) 1,379777
(1,3) 0
(1,4) Empty
(1,5) Empty
Don't know if this helps, but this takes a source array v and then populates a new array w with the sum of columns 2-4 of the corresponding row of v.
Sub x()
Dim v, i As Long, w()
'just to populate source array
v = Range("A1").CurrentRegion.Value
ReDim w(1 To UBound(v, 1))
For i = 1 To UBound(w)
'each element of w is sum of columns 2-4 of corresponding row of v
w(i) = Application.Sum(Application.Index(v, i, Array(2, 3, 4)))
Next i
'write w to sheet
Range("G1").Resize(UBound(w)) = Application.Transpose(w)
End Sub
Thanks to the answer from SJR I found myself a working solution. This is all within a larger piece of code, but for this example I filled some variables with fixed numbers to match my example from my question.
Dim days as Integer
days = 111
Dim emissions_rows as Integer
emissions_cols = 6
ReDim arr_emissions(0 To days, 1 To emissions_cols) As Variant
Dim arr_sum As Variant
Dim sum_str As String
sum_str = "Transpose(row(2:" & emissions_rows - 1 & "))"
arr_sum = Application.Evaluate(sum_str) '= Array(2,3,4,5)
arr_emissions(emissions_index, emissions_cols) = Application.Sum(Application.Index(arr_emissions, emissions_index + 1, arr_sum))
The code writes a string to include the variables, so to take the second column untill the second to last column, which is then evaluated into an array.
That array is then used within the sum function, to only sum over those columns.
The result is then written to the last column of arr_emissions().
emissions_index is an index that is used to loop over the array.
I'm trying to populate a 3d array from data stored in an Excel worksheet. I'm hoping I just don't know the syntax, but it may not be possible to 'batch' assign values to 3D arrays like you can with 2D arrays. Such as...
Dim 2DArray() As Variant
2DArray = Range(A1:C10)
This is so clean and efficient I was hoping for something equivalent, as opposed to using 3 nested for-next loops to populate the data one value at a time.
For background this is to lookup/interpolate a gas compressibility factor Z, which is dependent on specific gravity, pressure and temperature. I have 6 tables (really just named ranges) for 6 different values of specific gravity. Each table has pressure and temperature cross-tabulated with Z factor as the datapoint. Here is the code I have (which doesn't work):
Sub Array3DTest()
Dim ZTables() As Variant
ReDim ZTables(1 to 6, 1 to 15, 1 to 9)
'6 specific gravity tables, '15 pressure indices, 9 temp indices
'Here is my feeble attempt to guess the syntax...
'ZTables = ("tblZFactorSG1", "tblZFactorSG2", "tblZFactorSG3",
' "tblZFactorSG4", "tblZFactorSG5", "tblZFactorSG6")
End Sub
To clarify, tblZFactorSG1...SG2...SG3...etc are the 6 named ranges with the cross-tabbed Z factors. All are 15 rows (pressure) by 9 columns (temperature). The last line is commented out because VBA doesn't like the syntax, but maybe you can see what I'm trying to do here. I'm trying to assign the data to the 3Darray in chunks of 15x9. tblZFactorSG1 contains the values that should go in ZTables(1, 1 to 15, 1 to 9). tblZFactorSG2 contains the values that should go in ZTables(2, 1 to 15, 1 to 9).
Thanks in advance for any help.
Sub ArrayOfArrays()
Dim ZTables(1 to 6) As Variant, x as Long, ranges
ranges = Array("tblZFactorSG1", "tblZFactorSG2", "tblZFactorSG3", _
"tblZFactorSG4", "tblZFactorSG5", "tblZFactorSG6")
For x=1 to 6
ZTables(x) = ActiveSheet.Range(ranges(x-1)).Value
Next x
End Sub
the source data is in the following format
A B
1 0
2 0
4 1
5 0
6 0
8 1
I originally intended to list the missing items in the column A but since that did not quite work out for me, I intend to achieve the same thing via the method I will propose now.
What I want is a list that goes
C
3
7
essentially giving me the missing numbers of the sequence by using the values provided in the column B. But basically any solution that would give me the values listed under column C would be very appreciated.
It should be noted that I am working with a large list so that manual filtering and such are not preferable.
If column B is not needed (it can be computed from A), assuming that you have the data on sheet named worksheet1, the VBA sub that writes all the missing values to column C is the following:
Sub worksheet1c()
'this contains the position in column A
Dim i As Integer
'this contains the position in column C
Dim j As Integer
'the numbers from 1
Dim number As Integer
'initialization
i = 1
j = 1
number = 1
'check all numbers in column A from row 2
Do While Sheets("worksheet1").Cells(i, 1).Value <> ""
'if there is a difference between number and the value in cell write them to column C
Do Until number = Sheets("worksheet1").Cells(i, 1).Value
Sheets("worksheet1").Cells(j, 3).Value = number
j = j + 1
number = number + 1
Loop
i = i + 1
number = number + 1
Loop
End Sub
In cell G4 enter an address you want to look in, for example A2:A5 and then use the array formula below. The only assumption is that the list of numbers should begin from 1. I know it looks crazy, the formula. This formula has to be applied to range, so select a range, then go to address bar paste this formula and press CTRL + SHIFT + ENTER. In this particular case the range has to be in column shape.
I left the formula as not formatted text, so it was easier to copy.
=IF(INT(SQRT(MMULT((IF(TRANSPOSE(MMULT(INDIRECT(G4),1+0*TRANSPOSE(ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))))))=MMULT(ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))),1+0*TRANSPOSE(ROW(INDIRECT("1:"&COUNTA(INDIRECT(G4)))))),0,ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))))^2),1+0*ROW(INDIRECT("1:"&COUNTA(INDIRECT(G4)))))/COUNTA(INDIRECT(G4))))=SQRT(MMULT((IF(TRANSPOSE(MMULT(INDIRECT(G4),1+0*TRANSPOSE(ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))))))=MMULT(ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))),1+0*TRANSPOSE(ROW(INDIRECT("1:"&COUNTA(INDIRECT(G4)))))),0,ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))))^2),1+0*ROW(INDIRECT("1:"&COUNTA(INDIRECT(G4)))))/COUNTA(INDIRECT(G4))),SQRT(MMULT((IF(TRANSPOSE(MMULT(INDIRECT(G4),1+0*TRANSPOSE(ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))))))=MMULT(ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))),1+0*TRANSPOSE(ROW(INDIRECT("1:"&COUNTA(INDIRECT(G4)))))),0,ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))))^2),1+0*ROW(INDIRECT("1:"&COUNTA(INDIRECT(G4)))))/COUNTA(INDIRECT(G4))),"")
The formula could be read as
=IF(INT(A)=A,A, "")
where A is
SQRT(MMULT((IF(TRANSPOSE(MMULT(INDIRECT(G4),1+0*TRANSPOSE(ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))))))=MMULT(ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))),1+0*TRANSPOSE(ROW(INDIRECT("1:"&COUNTA(INDIRECT(G4)))))),0,ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))))^2),1+0*ROW(INDIRECT("1:"&COUNTA(INDIRECT(G4)))))/COUNTA(INDIRECT(G4)))
I'm a whiz at Matlab, but apparently I can't figure out excel for my life today. I have a spreadsheet where I keep track of votes. So I record x number of votes for each score, i.e. on a scale of 1 to 5, 3 people voted 4, 2 people voted 3, and 1 person voted 1. I want to find the median of these votes, but I need to turn them into an array first, otherwise I'm just taking the median of the numbers of votes. I'm having trouble with getting arrays to work in this case. I need to build an array, with the above example, that looks like {4 4 4 3 3 1}, and then I can take the median of that (I assume I can just use the regular median function on an array?).
I realize the problem here is that I don't really know excel very well. So I guess I'm just asking for an answer, which is frowned upon when I can't show much work myself. But can someone give me a hint?
This one intrigued me, I'm sure there is a way to do this with an array formula but they have never been my strong point. For the time being here is a VBA solution:
Function MedianArray(rngScore As Range, rngCount As Range) As Double
Dim arrS() As Variant, arrC() As Variant, arrM() As Variant
Dim i As Integer, j As Integer, k As Integer
Dim d As Double
arrS = rngScore
arrC = rngCount
d = WorksheetFunction.Sum(rngCount)
ReDim arrM(1 To d, 1 To 1)
k = 1
For i = 1 To UBound(arrS, 2)
For j = 0 To arrC(1, i) - 1
arrM(k, 1) = arrS(1, i)
k = k + 1
Next j
Next i
MedianArray = WorksheetFunction.Median(arrM())
End Function
Given you say you don't know much about VBA here's how you do it:
From Excel press Alt + F11 to open the VB Editor
In the VB Editor menus select Insert -> Module
Paste in the code above
In the cell where you need median value type =MedianArray(B1:F1,B2:F2), assuming your scores are in row 1 columns B through F and the counts are directly below.
Hope this helps.
I'll let someone else post a VBA solution, but here's what I did using just formulas:
A B C D E
1 Running Total: 1 1 3 6 6 Median
2 Greater/lesser: < < = > > 3.5
3 Values: 1 2 3 4 5
4 Counts: 1 2 3
Rows 3 and 4 are your original values and counts of values. Row 1 is the running total of the counts, going from left to right. Row 2 represents whether row 1 is greater than, lesser than, or equal to the total sum of the counts row.
If there's no = in row 2, then you just need to get the value from the first column with a >. This is achieved with an HLookup.
If there is an = in row 2, then you need to get the average of the value in the = column and the value of the first > column.
See it in action
I'd like to know if there's a more elegant way!