比较变量时,不等式运算符会失败

时间:2016-05-03 21:05:31

标签: excel-vba vba excel

我编写了一个算法,按升序对整数值进行排序,但在相邻的单元格中保留相应的信息。它使用Arr作为正在排序的单元格的快照,如果它们按升序排序,则构建值的索引(TagIndex)的数组,然后将TagIndex应用于该单元和相邻单元格。

例如,它应该采取这个......

----------------------------------
| 5/14/12  |  87  |  91  |  102  |
| 12/8/11  |  96  |  81  |  93   |
| 9/30/10  |  75  |  101 |  74   |
| 4/26/08  |  107 |  95  |  64   |
----------------------------------

...按第二个最左边的列排序,将其转换为:

----------------------------------
| 9/30/10  |  75  |  101 |  74   |
| 5/4/12   |  87  |  91  |  102  |
| 12/8/11  |  96  |  81  |  93   |
| 4/26/08  |  107 |  95  |  64   |
----------------------------------

以下是代码:

Dim cell as Range
Dim Arr, TempArr, BoundVal As Variant

For Each cell In ActiveSheet.ListObjects("Table2").ListColumns(targetColumn).DataBodyRange

    Arr = Split(cell.Value, Chr(10))
    ReDim TagIndex(0 To UBound(Arr)) As Variant
    For i = 0 To UBound(Arr)
        BoundVal = Arr(i) 'starts with first value and index
        TagIndex(i) = i   'as defaults
        For j = 0 To UBound(Arr)
            If Arr(j) < BoundVal Then  'if sorter finds a smaller value,
                BoundVal = Arr(j)      'flags it...
                TagIndex(i) = j        '...and its index as smaller,
            End If                     'keeps looking,
        Next j                         'leaves For loop with the smallest,
        Arr(TagIndex(i)) = 201         'and moves it up out of reach so sorter won't 
    Next i                             'flag it anymore (none of the values go above 200)

    For j = leftBoundColumn To rightBoundColumn 
        TempArr = Split(Cells(cell.Row, j).Value, Chr(10))
        For i = 0 To UBound(TempArr)
            Arr(i) = TempArr(TagIndex(i))
        Next i
        Cells(cell.Row, j).Value = Join(Arr, Chr(10))
    Next j

Next cell

这段代码起初很有用,但我有两个独立的版本 - 一个用于排序整数,另一个用于排序日期 - 并且想要一个可以处理两者的版本。为此,我尝试将BoundVal声明为新版本中的Variant。当结果变得不稳定时,敏锐地使用MsgBox'es显示它在&lt;操作员,试图告诉我不是96&lt; 201而是和107&lt; 75(但不适用于117/107,就像它应该的那样)。

如果我回去将BoundVal声明为整数,它会对整数开始正常工作,但是当我在日期尝试它时会给我一个类型不匹配错误。

比较Arr(j)&lt;是否存在一些基本问题? BoundVal?两者都是变体,都是字符串的后代。有什么想法吗?

1 个答案:

答案 0 :(得分:0)

您需要远离所有这些Variant变量。这种类型(类似于将变量声明为Object)应该是您最后的选择。 Variant可以是任何内容,甚至可能在代码过程中发生变化(如上所示)。因此,如果您使用它们将它们与其他内容进行比较,那么您最终可能会将Boolean(TRUE或FALSE)与DateString或其他内容进行比较。 VBA可以让你掌控!所以,我建议你这样做。

话虽如此,由于我们缺少一些代码(显然,上面的代码不完整,因为使用了一些既未声明也未初始化的变量),这里有一些提示,说明如何改进代码和可能自己得到一个工作结果:

(1)如果要在一行中声明多个变量,则仍需要为每个变量重复变量类型。所以,你需要写

Dim Arr as Variant, TempArr as Variant, BoundVal As Long

(2)尽可能避免使用Variant,而是使用适当的类型。例如,BoundVal似乎存储了整数或日期,因此数据类型Long可能是最佳的。

(3)数组通常是(VBA默认值),范围从0到ArrayCount - 1.因此,基数设置为0:Option Base 0 https://msdn.microsoft.com/en-us/library/aa266179(v=vs.60).aspx仍然,使代码持续更长时间(和更透明)我在你的代码中包含这一行并将你的循环更改为

For j = LBound(Arr) To UBound(Arr)

而不是

For j = 0 To UBound(Arr)

(4)强迫自己声明所有变量以确保您知道发生了什么:Option Explicit https://msdn.microsoft.com/en-us/library/office/gg278855.aspx

(5)尽可能使用.Value2代替.Value获得小额奖励。此外,这基本上只给你一个数字形式的日期而不是日期²。

完成上述所有更改后,您的代码应如下所示:

Option Base 0
Option Explicit

Sub tmpSO(Optional targetColumn As Long, Optional leftBoundColumn As Long, Optional rightBoundColumn As Long)

Dim cell As Range
Dim Arr As Variant, TempArr As Variant
Dim BoundVal As Long, TagIndex() As Long, i As Long, j As Long

'set the defaults for optional parameters
If targetColumn = 0 Then targetColumn = 2
If leftBoundColumn = 0 Then leftBoundColumn = 1
If rightBoundColumn = 0 Then rightBoundColumn = ActiveSheet.ListObjects("Table2").ListColumns.Count

For Each cell In ActiveSheet.ListObjects("Table2").ListColumns(targetColumn).DataBodyRange

    Arr = Split(cell.Value2, Chr(10))
    ReDim TagIndex(LBound(Arr) To UBound(Arr))
    For i = LBound(Arr) To UBound(Arr)
        'starts with first value and index, use default 0 if necessary
        If IsDate(Arr(i)) Then             'if the item can be interpreted as a date then
            BoundVal = CLng(CDate(Arr(i))) 'convert the string to a date and convert the date to a number
        Else                               'otherwise treat it as a number only
            BoundVal = CLng(IIf(IsNumeric(Arr(i)), Arr(i), 0))
        End If
        TagIndex(i) = i                    'as default
        For j = LBound(Arr) To UBound(Arr)
            If CLng(IIf(IsDate(Arr(j)), CDate(Arr(j)), Arr(j))) < BoundVal Then      'if sorter finds a smaller value,
                BoundVal = CLng(IIf(IsDate(Arr(j)), CDate(Arr(j)), Arr(j)))          'flags it...
                TagIndex(i) = j            '...and its index as smaller,
            End If                         'keeps looking,
        Next j                             'leaves For loop with the smallest,
        Arr(TagIndex(i)) = 201             'and moves it up out of reach so sorter won't
    Next i                                 'flag it anymore (none of the values go above 200)

    For j = leftBoundColumn To rightBoundColumn
        TempArr = Split(Cells(cell.Row, j).Value, Chr(10))
        For i = 0 To UBound(TempArr)
            Arr(i) = TempArr(TagIndex(i))
        Next i
        Cells(cell.Row, j).Value2 = Join(Arr, Chr(10))
    Next j

Next cell

End Sub

免责声明:我知道这个答案并不完整,上面的代码可能需要进行一些调整才能真正做到你想做的事情。然而,代码工作没有错误(在我的系统上测试),并介绍了一些最佳实践,以帮助您朝着正确的方向前进,并(可能)解决所有障碍。

²VBA中的所有日期和时间都是数字!日期存储为1899年12月31日和您希望存储的日期之间的日期,而时间总是存储为小数,代表一天的一小部分。所以,0.5 =半天=中午12点。 0.75 =一天中的四分之三=下午6点。今天的日期是2016年5月4日。也就是1899年12月31日之后的42,495天。因此,今天.Value2的日期是42,495。