0

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!

Community
  • 1
  • 1
Jason
  • 1
  • 1
  • You could use "Redim Preserve" as discussed here: http://stackoverflow.com/questions/2916009/vba-what-does-redim-preserve-do-and-simple-array-question – Axel Kemper Jun 20 '13 at 17:00
  • When you say "I want to create a multidimensional array" - do you mean that in your case the array is (250 x 3), or (10 x 5 x 5 x 3)? And do you want the values to be produced in a VBA array (to be used by other VBA code), or placed somewhere on a worksheet? – Floris Jun 20 '13 at 22:19
  • Does my answer meet your need? If not, why not? Your profile says you are visiting Stack Overflow but you have not acknowledged my answer. – Tony Dallimore Jun 24 '13 at 14:34
  • Yes, I am looking to create an array that is 250 x 3 or since I don't always know the input combinations ... ("number of combination" x "number of inputs"). – Jason Jun 24 '13 at 21:12
  • Tony, thanks! That is great. I think that I can adapt your answer to what I am trying to do. I am trying to create an array where the first dimension is the combination (1 to 250 in this example) and the other dimension is the number of inputs with there value for that combo. – Jason Jun 24 '13 at 21:18

1 Answers1

0

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
Tony Dallimore
  • 12,335
  • 7
  • 32
  • 61
  • Thanks Tony. This is great!!! I'll have to look it over to make sure I understand it. I think that I can adapt it to suit my needs. I would like to save the combination into an array that is "number of combination" by "number of inputs". For this example it would be array(1 to 250, 1 to 3). – Jason Jun 24 '13 at 21:15
  • If an array (1 to 250, 1 to 3) would be sufficient for your needs then I have misunderstood what you seek. Your question implies you need one dimension per input with the dimension sized for the input. – Tony Dallimore Jun 25 '13 at 10:48