计算Excel VB阵列列中的非空字段数

时间:2016-05-13 16:28:57

标签: arrays excel excel-vba vba

我目前将二维范围读入Excel VBA数组,如下所示:

Set Ws = Sheet1
Ws.Activate

LastRow = Ws.Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
LastCol = Ws.Cells.Find("*", searchorder:=xlByColumns, searchdirection:=xlPrevious).Column

ReDim elements(0 To LastRow - 2, 0 To LastCol - 2)
elements = Ws.Range(Cells(2, 1), Cells(LastRow, LastCol))

范围是25行乘11列。但是,并非该范围内的所有单元格都具有值,因此数组中的某些值为“空”'。

  • col A有25件
  • col B有16
  • col K有12 ...

我需要遍历这个数组并创建第二个数组,这将是一个"笛卡尔积"来自第一个的值。为了确定我需要循环多少次,我需要弄清楚每个数组列中有多少项("维度"?)。

这是我的循环尝试:

Row = 0

For i = 1 To 25  'numElements in column 1
 For j = 1 To 3    'numElements in column 6           
  For k = 1 To 5    'numElements in column 7
   For l = 1 To 14    'numElements in column 8
    For m = 1 To 6     'numElements in column 10
     For n = 1 To 12     'numElements in column 11

      cartesian(Row, 0) = elements(i, 0)
      cartesian(Row, 1) = elements(i, 1)
      cartesian(Row, 2) = elements(i, 2)
      cartesian(Row, 3) = elements(i, 3)
      cartesian(Row, 4) = elements(i, 4)
      cartesian(Row, 5) = elements(j, 5)
      cartesian(Row, 6) = elements(k, 6)
      cartesian(Row, 7) = elements(l, 7)
      cartesian(Row, 8) = elements(l, 8)
      cartesian(Row, 9) = elements(m, 9)
      cartesian(Row, 10) = elements(n, 10)

       Row = Row + 1
     Next n         
    Next m
   Next l
  Next k
 Next j
Next i

任何帮助表示赞赏:)

编辑1:

这是我读入array1的范围:

Austria  sem  jan
Belgium  gdn  feb
France        mar
US            apr
              may
              jun

我需要能够计算出多少"项目"第1列,第2列和第3列中有它们相乘。这样我就会知道ReDim第二个数组需要多大。

这就是我在数组2中所需要的,并最终写回另一张表:

Austria  sem    jan
Austria  sem    feb
Austria  sem    mar
Austria  sem    apr
Austria  sem    may
Austria  sem    jun
Austria  gdn    jan
Austria  gdn    feb
Austria  gdn    mar
Austria  gdn    apr
Austria  gdn    may
Austria  gdn    jun
Belgium  sem    jan
Belgium  sem    feb
Belgium  sem    mar
Belgium  sem    apr
Belgium  sem    may
Belgium  sem    jun
Belgium  gdn    jan
Belgium  gdn    feb
Belgium  gdn    mar
Belgium  gdn    apr
Belgium  gdn    may
Belgium  gdn    jun

1 个答案:

答案 0 :(得分:1)

这应该像你在相当长的时间内需要它一样......仍然需要一些时间来达到~300k的条目:

Option Explicit

Sub getMyList()

  'set input
  Dim inputVal As Variant

  'get input values
  With ThisWorkbook.Worksheets("Sheet1")
    inputVal = .Range(.Cells(1, 1), .Cells(.Cells.Find("*", , , , 1, 2).Row, .Cells(1, 1).End(xlToRight).Column)).Value
  End With

  'set count array
  Dim xCounts() As Variant
  ReDim xCounts(1 To UBound(inputVal, 2))

  Dim i As Long, j As Long
  For i = 1 To UBound(xCounts)
    j = 1
    While inputVal(j, i) <> "" And j < UBound(inputVal)
      j = j + 1
    Wend
    'xCounts(i) = j - 1 'will skip last value if it is at the last row
    xCounts(i) = j + (inputVal(j, i) = "") 'new one will work as wanted
  Next

  'set output sizes
  Dim outVal() As Variant
  ReDim outVal(1 To Application.Product(xCounts), 1 To UBound(xCounts))

  'runner sets
  Dim colRunner As Long, rowRunner As Long, copyRunner As Long
  Dim itemRep As Long, listRep As Long

  For colRunner = 1 To UBound(xCounts)

    rowRunner = 1
    itemRep = 1
    listRep = 1

    'repeat whole list
    For i = 1 To colRunner - 1
      listRep = listRep * xCounts(i)
    Next

    'repeat each item
    For i = colRunner + 1 To UBound(xCounts)
      itemRep = itemRep * xCounts(i)
    Next

    'run the list for output
    copyRunner = 1
    For i = 1 To listRep
      For copyRunner = 1 To xCounts(colRunner)
        For j = 1 To itemRep

          outVal(rowRunner, colRunner) = inputVal(copyRunner, colRunner)
          rowRunner = rowRunner + 1

        Next
      Next
    Next
  Next

  'output everything
  ThisWorkbook.Worksheets("Sheet2").Cells(1, 1).Resize(UBound(outVal), UBound(outVal, 2)).Value = outVal

End Sub

代码应该易于阅读(内部没有真正的魔法):P

但是,如果还有任何问题,请问:)

修改

xCounter只保留每列所有项目的计数,因为这些数字被多次使用 澄清一下:我们假设您有一个这样的列表:

A B C D E
1 1 1 1 1
2 2 2 2 2
3   3 3 3
4   4 4
5   5 5
6   6 6
7     7
8

(使用的数字便于计数,也适用于任何字符串)
xCounter现在为{8,2,6,7,3}。现在,如果您想要记下C列,那么您需要知道每个项目需要重复多少次。这可以通过将稍后出现的所有列的计数相乘来计算。对于这种情况,它将是7 * 3 = 21次。此外,您需要知道要循环的列表中有多少项6。然后整个列表也需要重复自己,这可以通过乘以它前面的所有行数来计算。那将是8 * 2 = 16次。这样也可以构建3个内部For ... Next循环。 ListRepeat(EachItem(ItemRepeat))。 要知道要写入输出数组中的哪一行,您需要一个简单的向上计数值,即RowCounter。直接在工作表中执行此操作,您将使用一个范围,每次在单元格中写入值时,该范围都会向下偏移一行 通过这个系统,你可以将每一列与其他列完全分开,因为你需要的只是前导列和后续列的项目计数的产物(我们有xCounter)。我们仍然需要为每个列执行此操作,因此外部循环是列(colRunner) 只是为了避免因使用i, j, k, l在彼此内部进行4次循环而感到困惑,我将outVal中的行的“runner”重命名为rowRunner,将colRunner中的行重命名为{{1} }}。将重复的上限和下限直接设置在内环的前面,我留在ij。 (它们也不用于那个循环中的任何东西,它们只是通过多次执行相同的动作来确保重复)

如果我错过了某些内容或其他问题,请执行此操作,因为它总是正确的做法:问。 ;)