热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

重新调整2d数组会导致类型不匹配-Redimminga2darraythrowstypemismatch

IwasworkingonasolutiontoanotherquestionofminewhenIstumbleacrossthishelpfulquestion

I was working on a solution to another question of mine when I stumble across this helpful question and answer. However implementing the answer given by Control Freak over there throws me a Type Mismatch error as soon as I exit the function and return to my code on the line: Years = ReDimPreserve(Years, i, 3). I'm not that skilled of a programmer to figure out what is going wrong here, so can anybody shed some light on this.

当我偶然发现这个有用的问题和答案时,我正在研究另一个问题的解决方案。然而,当我退出函数并返回到我的代码时,实现Control Freak在那里给出的答案会抛出类型不匹配错误:Years = ReDimPreserve(Years,i,3)。我不是那么熟练的程序员来弄清楚这里出了什么问题,所以任何人都可以对此有所了解。

Here is my code:

这是我的代码:

Sub DevideData()

    Dim i As Integer

    Dim Years() As String
    ReDim Years(1, 3)

    Years(1, 1) = Cells(2, 1).Value
    Years(1, 2) = 2

    i = 2
    ThisWorkbook.Worksheets("Simple Boundary").Activate
    TotalRows = ThisWorkbook.Worksheets("Simple Boundary").Range("A100000").End(xlUp).row

    For row = 3 To TotalRows
        Years = ReDimPreserve(Years, i, 3)

        If Not Cells(row, 1).Value = Cells(row - 1, 1).Value Then
            Years(i - 1, 3) = row - 1
            Years(i, 1) = Cells(row, 1).Value
            Years(i, 2) = row
            i = i + 1
        End If
    Next row

End Sub

And here is the function as written by Control Freak:

这是Control Freak编写的函数:

Public Function ReDimPreserve(aArrayToPreserve, nNewFirstUBound, nNewLastUBound)
    ReDimPreserve = False
    'check if its in array first
    If IsArray(aArrayToPreserve) Then
        'create new array
        ReDim aPreservedArray(nNewFirstUBound, nNewLastUBound)
        'get old lBound/uBound
        nOldFirstUBound = UBound(aArrayToPreserve, 1)
        nOldLastUBound = UBound(aArrayToPreserve, 2)
        'loop through first
        For nFirst = LBound(aArrayToPreserve, 1) To nNewFirstUBound
            For nLast = LBound(aArrayToPreserve, 2) To nNewLastUBound
                'if its in range, then append to new array the same way
                If nOldFirstUBound >= nFirst And nOldLastUBound >= nLast Then
                    aPreservedArray(nFirst, nLast) = aArrayToPreserve(nFirst, nLast)
                End If
            Next
        Next
        'return the array redimmed
        If IsArray(aPreservedArray) Then ReDimPreserve = aPreservedArray
    End If
End Function

3 个解决方案

#1


2  

I promised a fuller answer. Sorry it is later than I expected:

我答应了更全面的答案。对不起,它比我预期的要晚:

  1. I got tied up with another problem,
  2. 我遇到了另一个问题,

  3. Technique 1, which I was expecting to recommend, did not work as I expected so I added some other techniques which are much more satisfactory.
  4. 技术1,我期望推荐,没有按照我的预期工作,所以我添加了一些更令人满意的技术。

As I said in my first comment:

正如我在第一次评论中所说:

Public Function ReDimPreserve(aArrayToPreserve, nNewFirstUBound, nNewLastUBound)

causes aArrayToPreserve to have the default type of Variant. This does not match:

导致aArrayToPreserve具有默认类型Variant。这与以下内容不符:

Dim Years() As String

As you discovered, redefining Years as a Variant, fixes the problems. An alternative approach would be to amend the declaration of ReDimPreserve so aArrayToPreserve is an array of type String. I would not recommend that approach since you are storing both strings and numbers in the array. A Variant array will handle either strings or numbers while a String array can only handle numbers by converting them to strings for storage and back to numbers for processing.

正如您所发现的那样,将Years重新定义为Variant,可以解决问题。另一种方法是修改ReDimPreserve的声明,以便aArrayToPreserve是String类型的数组。我不推荐这种方法,因为你在数组中存储字符串和数字。 Variant数组将处理字符串或数字,而String数组只能通过将数字转换为字符串进行存储并返回数字进行处理来处理数字。

I tried your macro with different quantities of data and different amendments and timed the runs:

我尝试使用不同数量的数据和不同的修改宏,并计算运行时间:

Rows of data  Amendment                           Duration of run
       3,500  Years() changed to Variant            4.99 seconds
      35,000  Years() changed to Variant          502    seconds
      35,000  aArrayToPreserve changed to String  656    seconds

As I said in my second comment, ReDim Preserve is slow for both the inbuilt method and the VBA routine you found. For every call it must:

正如我在第二条评论中所说,ReDim Preserve对于内置方法和您找到的VBA例程都很慢。每次通话都必须:

  • find space for the new larger array
  • 找到新的更大阵列的空间

  • copy the data from the old array to the new
  • 将旧数组中的数据复制到新数组

  • release the old array for garbage collection.
  • 释放旧数组以进行垃圾回收。

ReDim Preserve is a very useful method but it must be used with extreme care. Sometimes I find that sizing an array to the maximum at the beginning and using ReDim Preserve to cut the array down to the used size at the end is a better technique. The best techniques shown below determine the number of entries required before sizing the array.

ReDim Preserve是一种非常有用的方法,但必须非常小心使用。有时我发现在开始时将数组的大小调整到最大值并使用ReDim Preserve将数组减少到最后使用的大小是一种更好的技术。下面显示的最佳技术确定了在调整阵列大小之前所需的条目数。

At the bottom of your routine, I added:

在你的日常工作的最底层,我补充说:

For i = LBound(Years, 1) To LBound(Years, 1) + 9
  Debug.Print Years(i, 0) & "|" & Years(i, 1) & "|" & Years(i, 2) & "|" & Years(i, 3)
Next
For i = UBound(Years, 1) - 9 To UBound(Years, 1)
  Debug.Print Years(i, 0) & "|" & Years(i, 1) & "|" & Years(i, 2) & "|" & Years(i, 3)
Next

This resulted in the following being output to the Immediate Window:

这导致以下输出到立即窗口:

|||
|AAAA|2|2
|AAAB|3|4
|AAAC|5|7
|AAAD|8|11
|AAAE|12|16
|AAAF|17|22
|AAAG|23|23
|AAAH|24|25
|AAAI|26|28
|AOUJ|34973|34976
|AOUK|34977|34981
|AOUL|34982|34987
|AOUM|34988|34988
|AOUN|34989|34990
|AOUO|34991|34993
|AOUP|34994|34997
|AOUQ|34998|35002
|AOUR|35003|
|||

Since you have called the array Years, I doubt my string values are anything like yours. This does not matter. What matters, is that I doubt this output was exactly what you wanted.

既然你已经调用了数组,我怀疑我的字符串值与你的一样。这没关系。重要的是,我怀疑这个输出正是你想要的。

If you write:

如果你写:

ReDim Years(1, 3) 

The lower bounds are set to the value specified by the Option Base statement or zero if there is no Option Base statement. You have lower bounds for both dimensions of zero which you do not use. This is the reason for the “|||” at the top. There is another “|||” at the end which means you are creating a final row which you are not using. The final used row does not have an end row which I assume in a mistake.

下限设置为Option Base语句指定的值,如果没有Option Base语句,则设置为零。您有两个不使用的维度的下限。这就是顶部“|||”的原因。最后还有另一个“|||”表示您正在创建一个您没有使用的最后一行。最后使用的行没有一个我认为是错误的结束行。

When I can divide a routine into steps, I always validate the result of one step before advancing to the next. That way, I know any problems are within the current step and not the result of an error in an earlier step. I use Debug.Print to output to the Immediate Window most of the time. Only if I want to output a lot of diagnostic information will I write to a text file. Either way, blocks of code like mine are a significant aid to rapid debugging of a macro.

当我可以将例程分成几个步骤时,我总是在前进到下一步之前验证一步的结果。这样,我知道任何问题都在当前步骤内,而不是前一步骤中的错误结果。我使用Debug.Print在大多数时间输出到立即窗口。只有当我想输出大量诊断信息时,才会写入文本文件。无论哪种方式,像我这样的代码块都是快速调试宏的重要辅助手段。

I would never write ReDim Years(1, 3). I always specify the lower bound so as to be absolutely clear. VBA is the only language I know where you can specify any value for the lower bound (providing it is less than the upper bound) so I will specify non-standard values if is helpful for a particular problem. In this case, I see not advantage to a lower bound other than one so that is what I have used.

我永远不会写ReDim年(1,3)。我总是指定下限,以便绝对清楚。 VBA是我所知道的唯一语言,您可以在其中指定下限的任何值(假设它小于上限),因此如果对特定问题有帮助,我将指定非标准值。在这种情况下,我认为除了一个以外的下限没有优势,所以这就是我所使用的。

With two dimensions arrays it is conventional to have columns as the first dimension and rows as the second. One exception is for arrays read from or to be written to a worksheet for which the dimensions are the other way round. You have rows as the first dimension. If you have used the conventional sequence you could have used the ReDim Preserve method, thereby avoiding the RedimPreserve function and the problem of non-matching types.

对于二维数组,通常将列作为第一维,将行作为第二维。一个例外是读取或写入工作表的数组,其尺寸是相反的。您将行作为第一个维度。如果您使用了传统的序列,则可以使用ReDim Preserve方法,从而避免使用RedimPreserve函数和不匹配类型的问题。

Technique 1

I expected this to be the fastest technique. Experts advise us to avoid “re-inventing the wheel”. That is, if Excel has a routine that will do what you want, don’t code an alternative in VBA. However, I have found a number of examples where this is not true and I discovered this technique was one of them.

我希望这是最快的技术。专家建议我们避免“重新发明轮子”。也就是说,如果Excel有一个可以执行您想要的例程,请不要在VBA中编写替代代码。但是,我发现了一些不成立的例子,我发现这种技术就是其中之一。

The obvious technique here is to use Filter, then create a range of the visible rows using SpecialCells and finally process each row in this range. I have used this technique very successfully to meet other requirements but not here.

这里显而易见的技术是使用Filter,然后使用SpecialCells创建一系列可见行,最后处理此范围内的每一行。我非常成功地使用这种技术来满足其他要求,但不是这里。

I did not know the VBA to select unique rows so started the macro recorder and filtered my test data from the keyboard to get:

我不知道VBA选择了唯一的行,因此启动了宏录制器并从键盘中过滤了我的测试数据以获得:

Range("A1:A35000").AdvancedFilter Action:=xlFilterInPlace, Unique:=True

My past uses of Filter have all converted to AutoFilter which I have found to give acceptable performance. This converted to AdvancedFilter which took 20 seconds both from the keyboard and from VBA. I do not know why it is so slow.

我过去使用的Filter都转换为AutoFilter,我发现它可以提供可接受的性能。这转换为AdvancedFilter,从键盘和VBA都需要20秒。我不知道为什么这么慢。

The second problem was that:

第二个问题是:

  Set RngUnique = .Range(.Cells(1, 1), .Cells(RowLast, 1)) _
                               .SpecialCells(xlCellTypeVisible)

was rejected as “too complicated”.

被拒绝为“太复杂”。

Not being able to get the visible rows as a range means the benefits of Filter are not really available. I have counted the visible rows to simulate having RngUnique.Rows.Count. This shows the technique which has always worked with AutoFilter. If AdvancedFilter had reported the unique rows in an accepted time I might have investigated this problem but under the circumstances it does not seem worth the effort.

无法将可见行作为范围获得意味着Filter的好处并不真正可用。我已经计算了可见行来模拟RngUnique.Rows.Count。这显示了一直使用AutoFilter的技术。如果AdvancedFilter在接受的时间内报告了唯一的行,我可能已经调查了这个问题,但在这种情况下,这似乎不值得努力。

The macro demonstrating this technique is:

展示这种技术的宏是:

Option Explicit
Sub Technique1()

  ' * Avoid using meaningless names like i.  Giving every variable a meaningful
  '   name is helpful during development and even more helpful when you return
  '   to the macro in six months for maintenence.
  ' * My naming convention is use a sequence of keywords.  The first keyword
  '   identifies what type of data the variable holds.  So "Row" means it holds
  '   a row number.  Each subsequent keyword narrows the scope.  "RowSb" is a
  '   row of the worksheet "Simple Boundary" and "RowYears" is a row of the Years
  '   array. "RowSbCrnt"is the current row of the worksheet "Simple Boundary".
  ' * I can look at macros I wrote years ago and know what all the variables are.
  '   You may not like my convention.  Fine, development your own but do not
  '   try programming with random names.
  ' * Avoid data type Integer which specifies a 16-bit whole number and requires
  '   special processing on 32 and 64-bit computers.  Long is now the recommended
  '   data type for whole numbers.
  Dim NumRowsVisible As Long
  Dim RowSbCrnt As Long
  Dim RowSbLast As Long
  Dim RowYearsCrnt As Long
  Dim TimeStart As Double
  Dim Years() As Variant

  TimeStart = Timer     ' Get the time as seconds since midnight to nearest .001
                        ' of a second

  ' This can save significant amounts of time if the macro amends the
  ' screen or switches between workbooks.
  Application.ScreenUpdating = False

  With Worksheets("Simple Boundary")

    ' Rows.Count avoiding having to guess how many rows will be used
    RowSbLast = .Cells(Rows.Count, "A").End(xlUp).Row

    ' Hide non-unique rows
    With .Range(.Cells(1, 1), .Cells(RowSbLast, 1))
      .AdvancedFilter Action:=xlFilterInPlace, Unique:=True
    End With

    ' Count number of unique rows.
    ' It is difficult to time small pieces of code because OS routines
    ' can execute at any time. However, this count takes less than .5
    ' of a second with 35,000 rows.
    NumRowsVisible = 0
    For RowSbCrnt = 2 To RowSbLast
      If Not .Rows(RowSbCrnt).Hidden Then
        NumRowsVisible = NumRowsVisible + 1
      End If
    Next

    ' Use count to ReDim array to final size.
    ReDim Years(1 To 3, 1 To NumRowsVisible)

    RowYearsCrnt = 1
    Years(1, RowYearsCrnt) = .Cells(2, 1).Value
    Years(2, RowYearsCrnt) = 2

    For RowSbCrnt = 3 To RowSbLast
      If Not .Rows(RowSbCrnt).Hidden Then
        Years(3, RowYearsCrnt) = RowSbCrnt - 1
        RowYearsCrnt = RowYearsCrnt + 1
        Years(1, RowYearsCrnt) = .Cells(RowSbCrnt, 1).Value
        Years(2, RowYearsCrnt) = RowSbCrnt
      End If
    Next

    ' Record final row for final string
    Years(3, RowYearsCrnt) = RowSbLast

    .ShowAllData        ' Clear AdvancedFilter

  End With

  Application.ScreenUpdating = True

  Debug.Print "Duration: " & Format(Timer - TimeStart, "#,##0.000")

  ' Output diagnostics
  For RowYearsCrnt = 1 To 9
    Debug.Print Years(1, RowYearsCrnt) & "|" & _
                Years(2, RowYearsCrnt) & "|" & _
                Years(3, RowYearsCrnt) & "|"
  Next
  ' Note that rows are now in the second dimension hence the 2 in UBound(Years, 2)
  For RowYearsCrnt = UBound(Years, 2) - 9 To UBound(Years, 2)
    Debug.Print Years(1, RowYearsCrnt) & "|" & _
                Years(2, RowYearsCrnt) & "|" & _
                Years(3, RowYearsCrnt) & "|"
  Next

End Sub

The output to the Immediate Window is:

立即窗口的输出是:

Duration: 20.570
AAAA|2|2|
AAAB|3|4|
AAAC|5|7|
AAAD|8|11|
AAAE|12|16|
AAAF|17|22|
AAAG|23|23|
AAAH|24|25|
AAAI|26|28|
AOUI|34970|34972|
AOUJ|34973|34976|
AOUK|34977|34981|
AOUL|34982|34987|
AOUM|34988|34988|
AOUN|34989|34990|
AOUO|34991|34993|
AOUP|34994|34997|
AOUQ|34998|35002|
AOUR|35003|35008|

As you can see the last row is correct. A duration of 20 seconds is better than the 8 minutes of your technique but I am sure we can do better.

如您所见,最后一行是正确的。持续时间为20秒优于技术的8分钟,但我相信我们可以做得更好。

Technique 2

The next macro is similar to the last one but it counts the unique rows rather than use AdvancedFilter to hide the non-unique rows. This macro has a duration of 1.5 seconds with 35,000 rows. This demonstrates that counting how many rows are required for an array in a first pass of the data is a viable approach. The diagnostic output from this macro is the same as above.

下一个宏类似于最后一个宏但它计算唯一行而不是使用AdvancedFilter来隐藏非唯一行。此宏的持续时间为1.5秒,包含35,000行。这表明在第一次传递数据时计算数组所需的行数是可行的方法。此宏的诊断输出与上述相同。

Sub Technique2()

  Dim NumRowsUnique As Long
  Dim RowSbCrnt As Long
  Dim RowSbLast As Long
  Dim RowYearsCrnt As Long
  Dim TimeStart As Double
  Dim Years() As Variant

  TimeStart = Timer     ' Get the time as seconds since midnight to nearest .001
                        ' of a second

  With Worksheets("Simple Boundary")

    RowSbLast = .Cells(Rows.Count, "A").End(xlUp).Row

    ' Count number of unique rows.
    ' Assume all data rows are unique until find otherwise
    NumRowsUnique = RowSbLast - 1
    For RowSbCrnt = 3 To RowSbLast
      If .Cells(RowSbCrnt, 1).Value = .Cells(RowSbCrnt - 1, 1).Value Then
        NumRowsUnique = NumRowsUnique - 1
      End If
    Next

    ' * Use count to ReDim array to final size.
    ' * Note that I have defined the columns as the first dimension and rows
    '   as the second dimension to match convention. Had I wished, this would
    '   have allowed me to use the standard ReDim Preserve which can only
    '   adjust the last dimension. However, this does not match the
    '   syntax of Cells which has the row first. It may have been better to
    '   maintain your sequence so the two sequences were the same.
    ReDim Years(1 To 3, 1 To NumRowsUnique)

    RowYearsCrnt = 1
    Years(1, RowYearsCrnt) = .Cells(2, 1).Value
    Years(2, RowYearsCrnt) = 2

    For RowSbCrnt = 3 To RowSbLast
      If .Cells(RowSbCrnt, 1).Value <> .Cells(RowSbCrnt - 1, 1).Value Then
        Years(3, RowYearsCrnt) = RowSbCrnt - 1
        RowYearsCrnt = RowYearsCrnt + 1
        Years(1, RowYearsCrnt) = .Cells(RowSbCrnt, 1).Value
        Years(2, RowYearsCrnt) = RowSbCrnt
      End If
    Next

    ' Record final row for final string
    Years(3, RowYearsCrnt) = RowSbLast

  End With

  Debug.Print "Duration: " & Format(Timer - TimeStart, "#,##0.000")

  ' Output diagnostics
  For RowYearsCrnt = 1 To 9
    Debug.Print Years(1, RowYearsCrnt) & "|" & _
                Years(2, RowYearsCrnt) & "|" & _
                Years(3, RowYearsCrnt) & "|"
  Next
  ' Note that rows are now in the second dimension hence the 2 in UBound(Years, 2)
  For RowYearsCrnt = UBound(Years, 2) - 9 To UBound(Years, 2)
    Debug.Print Years(1, RowYearsCrnt) & "|" & _
                Years(2, RowYearsCrnt) & "|" & _
                Years(3, RowYearsCrnt) & "|"
  Next

End Sub

Technique 3

The next macro is only slightly changed from the last.

下一个宏只是从最后一个稍微改变了。

Firstly, I have replaced the literals used to identify the column numbers in worksheets and arrays with constants such as:

首先,我用常量替换了用于识别工作表和数组中列数的文字,例如:

  Const ColYrEnd As LOng= 3

Under my naming convention ColYrEnd = Column of Year array holding range End hence:

在我的命名约定下,ColYrEnd = Year of Year数组保持范围因此结束:

           Years(ColYrEnd, RowYearsCrnt) = RowCvCrnt - 1
instead of Years(3, RowYearsCrnt) = RowCvCrnt - 1

This makes no difference to the compiled code but makes the source code easier to understand because you do not have to remember what columns 1, 2 and 3 hold. More importantly, if you ever have to rearrange the columns, updating the constants is the only change required. If you ever have to search through a long macro replacing every use of 2 as a column number (while ignoring any other use of 2) by 5, you will know why this is important.

这对编译的代码没有任何影响,但是使源代码更容易理解,因为您不必记住第1,2和3列的含义。更重要的是,如果您必须重新排列列,则更新常量是唯一需要的更改。如果你需要搜索一个长宏来代替每次使用2作为列号(而忽略任何其他2的使用),那么你就会知道为什么这很重要。

Secondly, I have used:

其次,我用过:

ColValues = .Range(.Cells(1, ColSbYear), _
                   .Cells(RowSbLast, ColSbYear)).Value

to import column 1 to an array. The code that read the values from the worksheet now reads them from this array. Array access is much faster than worksheet access so this reduces the runtime from 1.5 seconds to .07 seconds.

将列1导入数组。从工作表中读取值的代码现在从此数组中读取它们。数组访问比工作表访问快得多,因此这将运行时间从1.5秒减少到.07秒。

The revised code is:

修订后的代码是:

Sub Technique3()

  Const ColCvYear As LOng= 1
  Const ColSbYear As LOng= 1
  Const ColYrYear As LOng= 1
  Const ColYrStart As LOng= 2
  Const ColYrEnd As LOng= 3
  Const RowSbDataFirst As LOng= 2
  Const RowCvDataFirst As LOng= 2

  Dim ColValues As Variant
  Dim NumRowsUnique As Long
  Dim RowCvCrnt As Long
  Dim RowSbCrnt As Long
  Dim RowSbLast As Long
  Dim RowYearsCrnt As Long
  Dim TimeStart As Double
  Dim Years() As Variant

  TimeStart = Timer     ' Get the time as seconds since midnight to nearest .001
                        ' of a second

  With Worksheets("Simple Boundary")

    RowSbLast = .Cells(Rows.Count, ColSbYear).End(xlUp).Row

    ColValues = .Range(.Cells(1, ColSbYear), _
                       .Cells(RowSbLast, ColSbYear)).Value
    ' * The above statement imports all the data from column 1 as a two dimensional
    '   array into a Variant.  The Variant is then accessed as though it is an array.
    ' * The first dimension has one entry per row, the second dimension has on entry
    '   per column which is one in this case.  Both dimensions will have a lower bound
    '   of one even if the first row or column loaded is not one.

  End With

  ' Count number of unique rows.
  ' Assume all data rows are unique until find otherwise
  NumRowsUnique = UBound(ColValues, 1) - 1
  For RowCvCrnt = RowCvDataFirst + 1 To UBound(ColValues, 1)
    If ColValues(RowCvCrnt, ColCvYear) = ColValues(RowCvCrnt - 1, ColCvYear) Then
      NumRowsUnique = NumRowsUnique - 1
    End If
  Next

  ' I mentioned earlier that I was unsure if having rows and columns in the
  ' convention sequence was correct. I am even less sure here where array
  ' ColValues has been loaded from a worksheet and the rows and columns are
  ' not in the conventional sequence.  ReDim Years(1 To 3, 1 To NumRowsUnique)

  RowYearsCrnt = 1
  Years(ColYrYear, RowYearsCrnt) = ColValues(RowCvDataFirst, ColCvYear)
  Years(ColYrStart, RowYearsCrnt) = RowCvDataFirst

  For RowCvCrnt = RowCvDataFirst + 1 To UBound(ColValues, 1)
    If ColValues(RowCvCrnt, ColCvYear) <> ColValues(RowCvCrnt - 1, ColCvYear) Then
      Years(ColYrEnd, RowYearsCrnt) = RowCvCrnt - 1
      RowYearsCrnt = RowYearsCrnt + 1
      Years(ColYrYear, RowYearsCrnt) = ColValues(RowCvCrnt, ColCvYear)
      Years(ColYrStart, RowYearsCrnt) = RowCvCrnt
    End If
  Next

  ' Record final row for final string
  Years(ColYrEnd, RowYearsCrnt) = UBound(ColValues, 1)

  Debug.Print "Duration: " & Format(Timer - TimeStart, "#,##0.000")

  ' Output diagnostics
  For RowYearsCrnt = 1 To 9
    Debug.Print Years(ColYrYear, RowYearsCrnt) & "|" & _
                Years(ColYrStart, RowYearsCrnt) & "|" & _
                Years(ColYrEnd, RowYearsCrnt) & "|"
  Next
  ' Note that rows are now in the second dimension hence the 2 in UBound(Years, 2)
  For RowYearsCrnt = UBound(Years, 2) - 9 To UBound(Years, 2)
    Debug.Print Years(ColYrYear, RowYearsCrnt) & "|" & _
                Years(ColYrStart, RowYearsCrnt) & "|" & _
                Years(ColYrEnd, RowYearsCrnt) & "|"
  Next

End Sub

Other techniques

I considered introducing other techniques but I decided they were not useful for this requirement. Also, this answer is already long enough. I have provided much for you to think about and more would just be overload. As stated above I have reduced the run time for 35,000 rows from 8 minutes to 20 seconds to 1.5 seconds to .07 seconds.

我考虑引入其他技术,但我认为它们对此要求没用。此外,这个答案已经足够长了。我为你提供了很多思考,而且更多的只是过载。如上所述,我将35,000行的运行时间从8分钟缩短为20秒至1.5秒至.07秒。

Work slowly through my macros. I have hope I have provided adequate explanation of what each is doing. Once you know a statement exists, it is generally easy to look it up so there is not too much explanation of the statements. Come back with questions as necessary.

通过我的宏慢慢地工作。我希望我已经提供了足够的解释,说明每个人在做什么。一旦你知道一个语句存在,通常很容易查找它,所以没有太多的语句解释。如有必要,请回答问题。

#2


1  

As stated earlier in comments, ReDim Preserve is an expensive call when working with large datasets and is generally avoided. Here is some commented code that should perform as desired. Tested on a dataset with 200,000 rows, it took less than 5 seconds to complete. Tested on a dataset with 1000 rows, it took less that 0.1 seconds to complete.

正如前面评论中所述,ReDim Preserve在处理大型数据集时是一项昂贵的调用,通常可以避免。以下是一些应根据需要执行的注释代码。在具有200,000行的数据集上进行测试,完成时间不到5秒。测试了1000行的数据集,完成时间不到0.1秒。

The code uses a Collection to get the unique values out of column A, and then builds the array based on those unique values and outputs the results to another sheet. In your original code, there was nowhere that the resulting array was output, so I just made something up and you'll need to adjust the output section as needed.

代码使用Collection从列A中获取唯一值,然后根据这些唯一值构建数组,并将结果输出到另一个工作表。在你的原始代码中,没有任何地方输出结果数组,所以我只是做了一些东西,你需要根据需要调整输出部分。

Sub tgr()

    Dim ws As Worksheet
    Dim rngYears As Range
    Dim collUnqYears As Collection
    Dim varYear As Variant
    Dim arrAllYears() As Variant
    Dim arrYearsData() As Variant
    Dim YearsDataIndex As Long

    Set ws = ActiveWorkbook.Sheets("Simple Boundary")
    Set rngYears = ws.Range("A1", ws.Cells(Rows.Count, "A").End(xlUp))
    If rngYears.Cells.Count <2 Then Exit Sub   'No data
    Set collUnqYears = New Collection

    With rngYears
        .CurrentRegion.Sort rngYears, xlAscending, Header:=xlYes    'Sort data by year in column A
        arrAllYears = .Offset(1).Resize(.Rows.Count - 1).Value      'Put list of years in array for faster calculation

        'Get count of unique years by entering them into a collection (forces uniqueness)
        For Each varYear In arrAllYears
            On Error Resume Next
            collUnqYears.Add CStr(varYear), CStr(varYear)
            On Error GoTo 0
        Next varYear

        'Ssize the arrYearsData array appropriately
        ReDim arrYearsData(1 To collUnqYears.Count, 1 To 3)
            'arrYearsData column 1 = Unique Year value
            'arrYearsData column 2 = Start row for the year
            'arrYearsData column 3 = End row for the year

        'Loop through unique values and populate the arrYearsData array with desired information
        For Each varYear In collUnqYears
            YearsDataIndex = YearsDataIndex + 1
            arrYearsData(YearsDataIndex, 1) = varYear                                           'Unique year
            arrYearsData(YearsDataIndex, 2) = .Find(varYear, .Cells(1), , , , xlNext).Row       'Start Row
            arrYearsData(YearsDataIndex, 3) = .Find(varYear, .Cells(1), , , , xlPrevious).Row   'End Row
        Next varYear
    End With

    'Here is where you would output your results
    'Your original code did not output results anywhere, so adjust sheet and start cell as necessary
    With Sheets("Sheet2")
        .UsedRange.Offset(1).ClearContents  'Clear previous result data
        .Range("A2").Resize(UBound(arrYearsData, 1), UBound(arrYearsData, 2)).Value = arrYearsData
        .Select 'This will show the output sheet so you can see the results
    End With

End Sub

#3


0  

As you mentioned in the comments, if you are going to continue this way you definitely need to move that redim inside the if statement:

正如您在评论中提到的,如果您要继续这种方式,您肯定需要在if语句中移动该redim:

If Not Cells(row, 1).Value = Cells(row - 1, 1).Value Then
    Years = ReDimPreserve(Years, i, 3)
    Years(i - 1, 3) = row - 1
    Years(i, 1) = Cells(row, 1).Value
    Years(i, 2) = row
    i = i + 1
End If

I think this redimming multi-dimensional arrays is overkill for you. I have a few recommendations:

我认为这种重新调整的多维数组对你来说太过分了。我有一些建议:

Ranges

I notice that you are using 2 values to represent the start of a range and end of a range (years(i,2) is the start and years(i,3) is the end). Instead why not just use an actual range?

我注意到你使用2个值来表示范围的开始和范围的结束(年(i,2)是开始,年(i,3)是结束)。相反,为什么不使用实际范围?

Create a range variable called startNode and when you find the end of the range create a Range object like with Range(startNode,endNode).

创建一个名为startNode的范围变量,当您找到范围的结尾时,创建一个Range对象,如Range(startNode,endNode)。

Your code will look something like this:

您的代码将如下所示:

Sub DevideData()
    Dim firstCell As Range
    Dim nextRange As Range
    Set firstCell = Cells(2,1)

    ThisWorkbook.Worksheets("Simple Boundary").Activate
    TotalRows = ThisWorkbook.Worksheets("Simple Boundary").Range("A100000").End(xlUp).row

    For row = 3 To TotalRows    
        If Not Cells(row, 1).Value = Cells(row - 1, 1).Value Then
            Set nextRange = Range(firstCell, Cells(row-1,1))
            Set firstCell = Cells(row,1)
        End If
    Next row

End Sub
1D Array

Now you do not need to store 3 values! Just an array of ranges Which you can redim like this:

现在您不需要存储3个值!只是一系列范围,您可以像这样重新划分:

Dim years() As Range
'Do Stuff'
ReDim Preserve years(1 to i)
set years(i) = nextRange
i = i + 1

Note that the only reason that ReDimPreserve was created was so that you can redim both dimensions of a 2D array (normally you can only change the second dimension). With a 1D array you can freely redim without any troubles! :)

请注意,创建ReDimPreserve的唯一原因是您可以重新绘制2D数组的两个维度(通常只能更改第二个维度)。使用一维阵列,您可以自由地重新安装,没有任何麻烦! :)

For Each Loop

Lastly I recommend that you use a for each loop instead of a regular for loop. It makes your intentions for the loop more explicit which makes your code more readable.

最后,我建议您为每个循环使用a而不是常规for循环。它使您对循环的意图更加明确,这使您的代码更具可读性。

Dim firstCell as Range
Dim lastUniqueValue as Variant
Dim lastCell as Range
Dim iCell as Range

Set firstCell = Cells(3,1)
lastUniqueValue = firstCell.Value
Set lastCell = ThisWorkbook.Worksheets("Simple Boundary").Range("A100000").End(xlUp)
For Each iCell in Range(firstCell, lastCell)
    If iCell.Value <> lastUniqueValue Then
        lastUniqueValue = iCell.Value
        'Do Stuff
    End If
Next

Hope this helps! :)

希望这可以帮助! :)


推荐阅读
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • C语言注释工具及快捷键,删除C语言注释工具的实现思路
    本文介绍了C语言中注释的两种方式以及注释的作用,提供了删除C语言注释的工具实现思路,并分享了C语言中注释的快捷键操作方法。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • 如何提高PHP编程技能及推荐高级教程
    本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 怎么在PHP项目中实现一个HTTP断点续传功能发布时间:2021-01-1916:26:06来源:亿速云阅读:96作者:Le ... [详细]
  • IjustinheritedsomewebpageswhichusesMooTools.IneverusedMooTools.NowIneedtoaddsomef ... [详细]
author-avatar
上帝认我做干爹
这个家伙很懒,什么也没留下!
Tags | 热门标签
RankList | 热门文章
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有