【问题标题】:How to fill in a dynamic combination of numbers into an array如何将数字的动态组合填充到数组中
【发布时间】:2013-06-20 16:49:09
【问题描述】:

在我的 Excel 工作表中,用户可以以最小值、最大值和步长值的形式输入 1 到 5 行数据。我想创建一个包含所有数据组合的多维数组。

有没有办法在 VBA 中对此进行编码以动态调整数组大小并循环遍历单元格值,而无需事先知道有多少数据项?

3行输入的示例数据(可多可少)

     Min, Max, Step

数据 1:1、10、1

数据 2:10、50、10

数据 3:5、25、5

总组合为 250 (10 x 5 x 5)

组合 1:1、10、5

组合 2:1、10、10

组合 3:1、10、15

...

谢谢!

【问题讨论】:

  • 您可以使用此处讨论的“Redim Preserve”:stackoverflow.com/questions/2916009/…
  • 当您说“我想创建一个多维数组”时 - 您的意思是在您的情况下数组是 (250 x 3) 还是 (10 x 5 x 5 x 3)?您希望这些值在 VBA 数组中生成(供其他 VBA 代码使用),还是放在工作表的某个位置?
  • 我的回答能满足你的需要吗?如果不是,为什么不呢?您的个人资料显示您正在访问 Stack Overflow,但您尚未确认我的回答。
  • 是的,我希望创建一个 250 x 3 的数组,或者因为我并不总是知道输入组合...(“组合数”x“输入数”)。
  • 托尼,谢谢!这太棒了。我认为我可以使您的答案适应我正在尝试做的事情。我正在尝试创建一个数组,其中第一个维度是组合(在本例中为 1 到 250),另一个维度是具有该组合值的输入数。

标签: arrays excel multidimensional-array vba


【解决方案1】:

我发现您的问题有点不清楚,但我相信下面的宏可以满足您的要求。

如果你有一个变体 Result,你可以将 Result 设置为一个数组。然后,您可以依次将 Result(1)、Result(1)(1)、Result(1)(1)(1) 等设置为嵌套数组。使用合适的递归例程,我相信您可以在 Excel 限制内创建任何大小的数组。但是,我认为这种方法很难理解。

我不相信有更简单的方法来创建具有可变维数的数组。然而,改变尺寸的大小不是问题。

由于您最多有五个维度,我决定使用固定数量的维度,尾随未使用的维度宽度为 1。对于您的示例(第 1 步 1 到 10、第 10 步 10 到 50、第 5 步 5 到 25),这将需要:

Dim Result(1 To 10, 1 To 5, 1 To 5, 1 To 1, 1 To 1)  

前三个维度有 10、5 和 5 个元素,可以容纳一系列值。最后两个维度只是占位符。

您正在让您的用户输入维度详细信息。我已经从工作表“Dyn Dims”中加载了详细信息。对于与您的示例匹配的测试,我将此工作表设置为:

Min Max Step
  1  10    1
 10  50   10
  5  25    5

我将此信息加载到长数组要求(1 到 3、1 到 5)。列是最小值、最大值和步长。这些行最多允许五个维度。如果第 3 列(步骤)为零,则不使用维度。我不允许负步长值,但如果有必要,请指出需要更改的位置。

您需要根据用户输入的数据初始化此数组。

根据数组需求,宏计算每个维度的元素个数。我已经用值测试了这个计算,例如 1 step 2 to 10,其中 N 没有值,例如 Min + N * Step = Max。

宏然后根据需要维度数组结果。

你没有说你想要数组中的什么值,所以我将它们设置为“N:N:N”形式的值,其中 Ns 是来自 Min-To-Max-Step 计算的值。这个我已经在宏里解释过了,这里不再赘述。

最后,我将数组的内容输出到以日期和时间命名的文件中。在您的示例中,输出为:

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

我相信我已经包含了足够多的 cmets 来解释宏,但如果有必要,我会提出问题。

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

【讨论】:

  • 谢谢托尼。这很棒!!!我将不得不仔细查看以确保我理解它。我认为我可以调整它以适应我的需要。我想将组合保存到一个数组中,该数组是“输入数”的“组合数”。对于这个例子,它将是数组(1 到 250、1 到 3)。
  • 如果一个数组(1 到 250、1 到 3)足以满足您的需求,那么我误解了您的要求。您的问题意味着您需要每个输入一个维度,并且该维度的尺寸适合输入。
猜你喜欢
  • 1970-01-01
  • 2017-12-13
  • 2021-07-18
  • 1970-01-01
  • 2013-11-26
  • 1970-01-01
  • 1970-01-01
  • 2017-05-20
  • 2015-08-15
相关资源
最近更新 更多