VBA - put values from 2D array to 2 or more cells - arrays

I'm trying to figure out how to put values from a [N x 2] matrix to cells on the same row on a different worksheet.
The matrix, which changes, is something like:
1 0
1 2
1 3
2 0
2 1
2 2
... so on.
On a different sheet, using the items in the matrix, I want to create a vector that omits the zero, such as:
A B
1 1 1
2 1 2
3 1 3
4 2 1
5 2 2
... so on.
I already have an array filled with the values from the matrix. I am having problems trying to extract values from the array.
Eventually, there will be certain criteria and some combinations, so # of columns and rows will increase. So, I need to do this in VBA. Can anyone guide me in the right direction or provide some example code that I can reference?
Please let me know if I need to clarify anything.

A sample for your reference
Sub testArr()
'Declare a 4-by-2 matrix
Dim Data(3, 1) As Variant
Dim i As Long, j As Long
For i = 0 To UBound(Data, 1)
For j = 0 To UBound(Data, 2)
Data(i, j) = Int((10 - 1 + 1) * Rnd + 1)
Next j
Next i
'You extract a value from the array like this
MsgBox "data(2,1) = " & Data(2, 1)
End Sub

Related

How to merge three arrays in visual basic?

I would like merge three arrays in one array.
Dim arr() = 1 4 7 10
Dim arr2() = 2 5 8 11
Dim arr3() = 3 6 9 12
Dim arr4()
for i=0 to ubond.(arr1)+2
??????????????
Next
The result should be arr4()= {1 2 3 4 5 6 7 8 9 10 11 12}
Best Regards
I'd just concatenate and sort them with LINQ:
Dim arr4 = arr.Concat(arr2).Concat(arr3).OrderBy(Function(x) x)
Whether or not you call .ToArray() is optional and depends what you do with arr4. If you will enumerate it with a foreach loop you can leave it like above. If you will random access it or pass it round as an array of int then call ToArray on it
Make sure you Imports System.Linq
If you don't want to use LINQ, a simple loop:
Dim arr4() as New Integer(arr.Length * 3 - 1)
For x = 0 to arr.Length - 1 Step 1
arr4(3*x) = arr(x)
arr4(3*x+1) = arr2(x)
arr4(3*x+2) = arr3(x)
Next x
This works completely differently - the LINQ version would have made an array of 1 4 7 2 5 8 3 6 9 and then sorted it, this one puts the bits in the right order naturally, but if your data didn't sort this way the two approaches would produce different outputs!

Looping through column in a multidimensional array

I have a multidimensional array with a layout as set out below:
Banana 10 20 30 40
Coconut 5 10 2 4
Apple 3 4 5 6
I want to loop through a specific column range in a worksheet to check if the values are either 'Banana', 'Coconut' or 'Apple'. When the cell value equate to a value in the first column of my array, I want to then output the array values next to that specific identifier. So for instance I want the output to be as below:
Shark
Banana 10 20 30 40
Pear
Apple 3 4 5 6
I understand that I need to loop through each cell in my range and then evaluate if the cell is equal to the values in the first column of the array. However, I am not sure how to do this. Typically I just use the setup below but I would like to understand how I can create a better solution in this case where I only want to loop through the first column in the array.
For Each cell In ws.Range("OUTPUT")
For y = LBound(arr, 2) To UBound(arr, 2)
If cell.Value = y Then
For m = 1 To x
ws.Cells(cell.Row, n + 1) = arr(n, m)
Next m
n = n + 1
End If
Next y
Next cell

Generate Permutation lists in VBA

How should I go about generating lists of combinations of n parents and m children in VBA without having n number of nested loops? I will end up with a total of m^n combinations.
Here's an example. Let's say I have n=3 parents (1 to 3) and m=2 children (1 to 2). I would like to generate the following arrays:
1 1 1 2 1 1 1 2 1 1 and so forth...(total of 8)
2 1 2 1 2 2 2 2 2 1
3 1 3 1 3 1 3 1 3 2
These arrays would in turn be used to index another array of data, so I would end up selecting a child of every parent branch and have all the combinations of those. I figured out how I can do this if the number of parents is constant with nested loops, however that is a problem as number of parents, n, is variable.
Fun Puzzle.
Sub KJK()
Dim parent As Long, child As Long
parent = 3
child = 2
Dim oarr As Variant
ReDim oarr(1 To parent, 1 To child ^ parent)
Dim i As Long, j As Long
For i = 1 To parent
For j = 1 To child ^ parent
oarr(i, j) = i & " " & Int(((j - 1) Mod (child ^ i)) / (child ^ i) * child) + 1
Next j
Next i
'Output the array
Worksheets("Sheet1").Range("A1").Resize(parent, child ^ parent).Value = oarr
End Sub
produces:
One note: Child^Parent cannot exceed the column count of 16384 if pasting to the sheet.

How do i plot pair of points from an array

I have a matrix:
img = [1 1 2 2
1 1 2 2
3 2 2 2
3 2 2 2
3 3 3 2];
from which I obtained the array of points:
A = [3 2; 5 4];
I need to plot each pair of points (y,x) row-by-row (i.e (3,2), (5,4), etc) and i have tried the code:
for i = 1: size(A, 2)
plot(A(i, 1), A(i, 2), '*')
end
This however does not give the expected positions of the points. Please, what could be wrong with my code and what can I do to make this work?
Since your pairs of points are in row/column order, you'll need to switch the order for plot as the order of inputs to plot are x/y. Also, you'll want to use the number of rows size(A, 1) rather than size(A, 2)
for k = 1:size(A, 1)
plot(A(k,2), A(k,1), '*')
hold on
end
You can also just plot everything at once without the loop
plot(A(:,2), A(:,1), '*');

How to fill in a dynamic combination of numbers into an array

In my Excel worksheet users can enter 1 to 5 rows of data in the form of minimum, maximum and step size values. I want to create an multidimensional array that has all the combinations of the data.
Is there a way to code this in VBA to dynamically size the array and loop through the cell values without knowing how many data items beforehand?
Example data of 3 rows of inputs (can be more or less)
Min, Max, Step
Data 1: 1, 10, 1
Data 2: 10, 50, 10
Data 3: 5, 25, 5
Total combinations is 250 (10 x 5 x 5)
Combo 1: 1, 10, 5
Combo 2: 1, 10, 10
Combo 3: 1, 10, 15
...
Thanks!
I found your question a little unclear but I believe the macro below does what you want.
If you have a variant Result, you can set Result to an array. You can then, in turn, set Result(1), Result(1)(1), Result(1)(1)(1) and so on to nested arrays. With suitable recursive routines I believe you could create the sort of array you seek of any size within the limits Excel. However, I think this approach would be very difficult to understand.
I do not believe there is a simpler way of creating an array with a variable number of dimensions. Changing the size of the dimensions is, however, not a problem.
Since you have a maximum of five dimensions, I have decided to go for a fixed number of dimensions with trailing, unused dimensions having a width of one. With your example (1 to 10 step 1, 10 to 50 step 10, 5 to 25 step 5), this would require:
Dim Result(1 To 10, 1 To 5, 1 To 5, 1 To 1, 1 To 1)
The first three dimensions have 10, 5 and 5 elements, ready to hold a range of values. The final two dimensions are just place holders.
You are getting your users to enter dimension details. I have loaded details from worksheet "Dyn Dims". For the test that matches your example, I set this worksheet to:
Min Max Step
1 10 1
10 50 10
5 25 5
I load this information to long array Requirements(1 To 3, 1 To 5). The columns are minimum, maximum and step. The rows allow for a maximum of five dimensions. If column 3 (step) is zero, the dimension is not used. I do not allow for negative step values but indicate where changes would be required if this was necessary.
You will need to initialise this array from the data entered by your users.
From array Requirements, the macro calculates the number of elements in each dimension. I have tested this calculation with values, such as 1 step 2 to 10, where there is no value for N such that Min + N * Step = Max.
The macro then dimensions array Result as necessary.
You do not say what values you want within the array so I have set them to values of the form "N:N:N" where the Ns are the values from the Min-To-Max-Step calculation. I have explained this in the macro and will not repeat myself here.
Finally, I output the contents of the array to a file named for the date and time. With your example the output is:
Dimensions
1 2 3 Value
1 1 1 1:10:5
2 1 1 2:10:5
3 1 1 3:10:5
4 1 1 4:10:5
5 1 1 5:10:5
6 1 1 6:10:5
7 1 1 7:10:5
8 1 1 8:10:5
9 1 1 9:10:5
10 1 1 10:10:5
1 2 1 1:20:5
: : : :
5 5 5 5:50:25
6 5 5 6:50:25
7 5 5 7:50:25
8 5 5 8:50:25
9 5 5 9:50:25
10 5 5 10:50:25
I believe I have included enough comments to explain the macro but come back with questions if necessary.
Option Explicit
Sub DD()
Const ColReqMin As Long = 1
Const ColReqMax As Long = 2
Const ColReqStep As Long = 3
Dim DimCrnt As Long
Dim Entry(1 To 5) As Long
Dim EntryStepped As Boolean
Dim FileOutNum As Long
Dim Index(1 To 5) As Long
Dim IndexStepped As Boolean
Dim NumEntries(1 To 5) As Long
Dim Requirements(1 To 3, 1 To 5) As Long
Dim Result() As String
Dim RowDDCrnt As Long
Dim Stg As String
Dim Value As String
' Load Requirements with the required ranges
With Worksheets("Dyn Dims")
RowDDCrnt = 2 ' First data row of worksheet Dyn Dims
' Note this macro does not check for blank lines in the middle
' of the table.
For DimCrnt = 1 To 5
If IsEmpty(.Cells(RowDDCrnt, ColReqStep)) Then
' No step value so this dimension not required for this run
Requirements(ColReqStep, DimCrnt) = 0
Else
Requirements(ColReqMin, DimCrnt) = .Cells(RowDDCrnt, ColReqMin)
Requirements(ColReqMax, DimCrnt) = .Cells(RowDDCrnt, ColReqMax)
Requirements(ColReqStep, DimCrnt) = .Cells(RowDDCrnt, ColReqStep)
End If
RowDDCrnt = RowDDCrnt + 1
Next
End With
' Calculate number of entries for each dimension
For DimCrnt = 1 To 5
If Requirements(ColReqStep, DimCrnt) = 0 Then
' Dummy dimension
NumEntries(DimCrnt) = 1
Else
NumEntries(DimCrnt) = (Requirements(ColReqMax, DimCrnt) - _
Requirements(ColReqMin, DimCrnt) + _
Requirements(ColReqStep, DimCrnt)) \ _
Requirements(ColReqStep, DimCrnt)
End If
Next
' Size array
ReDim Result(1 To NumEntries(1), _
1 To NumEntries(2), _
1 To NumEntries(3), _
1 To NumEntries(4), _
1 To NumEntries(5))
' Initialise entry for each dimension to minimum value, if any,
' and index for each dimension to 1
For DimCrnt = 1 To 5
Index(DimCrnt) = 1
If Requirements(ColReqStep, DimCrnt) <> 0 Then
Entry(DimCrnt) = Requirements(ColReqMin, DimCrnt)
End If
Next
' Starting with Entry(1), this loop steps the entry if the dimension is used.
' If the stepped entry is not greater than the maximum, then this repeat of
' the loop has finished. If the stepped entry is greater than the maximum,
' it is reset to its minimum and the next entry stepped and checked in the
' same way. If no entry is found that can be stepped, the loop is finished.
' If the dimensions after all 1 to 3 step 1, the values created by this loop
' are:
' 1 1 1 1 1
' 2 1 1 1 1
' 3 1 1 1 1
' 1 2 1 1 1
' 2 2 1 1 1
' 3 2 1 1 1
' 1 3 1 1 1
' 2 3 1 1 1
' 3 3 1 1 1
' 1 1 2 1 1
' 2 1 2 1 1
' 3 1 2 1 1
' : : : : :
' 3 3 3 3 3
Do While True
' Concatenate entries to create value for initial element
' or for element identified by last loop
Value = Entry(1)
For DimCrnt = 2 To 5
If Requirements(ColReqStep, DimCrnt) = 0 Then
Exit For
End If
Value = Value & ":" & Entry(DimCrnt)
Next
Result(Index(1), Index(2), Index(3), Index(4), Index(5)) = Value
' Find an entry to step
EntryStepped = False
For DimCrnt = 1 To 5
If Requirements(ColReqStep, DimCrnt) = 0 Then
Exit For
End If
Index(DimCrnt) = Index(DimCrnt) + 1
Entry(DimCrnt) = Entry(DimCrnt) + _
Requirements(ColReqStep, DimCrnt)
' ### Changes required her if a negative step value is allow
If Entry(DimCrnt) <= Requirements(ColReqMax, DimCrnt) Then
' This stepped entry is within permitted range
EntryStepped = True
Exit For
End If
' This entry past its maximum so reset to minimum
' and let for loop step entry for next dimension
Index(DimCrnt) = 1
Entry(DimCrnt) = Requirements(ColReqMin, DimCrnt)
Next
If Not EntryStepped Then
' All elements of Result initialised
Exit Do
End If
Loop
' All elements of Result initialised
' Output values as test.
FileOutNum = FreeFile
Open ActiveWorkbook.Path & "\" & Format(Now(), "yymmdd hhmmss") & ".txt" _
For Output As #FileOutNum
' Initialise Index
For DimCrnt = 1 To 5
Index(DimCrnt) = 1
Next
' Create header line for table
Print #FileOutNum, "Dimensions"
Stg = ""
For DimCrnt = 1 To 5
If Requirements(ColReqStep, DimCrnt) = 0 Then
Exit For
End If
Stg = Stg & Right(" " & DimCrnt, 4)
Next
Stg = Stg & " Value"
Print #FileOutNum, Stg
' Similar logic to loop that intialised Result but using Index and UBound.
Do While True
' Output initial element or element identified by previous loop
Stg = ""
For DimCrnt = 1 To 5
If Requirements(ColReqStep, DimCrnt) = 0 Then
Exit For
End If
Stg = Stg & Right(" " & Index(DimCrnt), 4)
Next
Stg = Stg & " " & Result(Index(1), Index(2), Index(3), Index(4), Index(5))
Print #FileOutNum, Stg
' Identify next element, if any
IndexStepped = False
For DimCrnt = 1 To 5
If Requirements(ColReqStep, DimCrnt) = 0 Then
Exit For
End If
Index(DimCrnt) = Index(DimCrnt) + 1
If Index(DimCrnt) <= UBound(Result, DimCrnt) Then
IndexStepped = True
Exit For
Else
Index(DimCrnt) = 1
End If
Next
If Not IndexStepped Then
' All entries output
Exit Do
End If
Loop
Close #FileOutNum
End Sub

Resources