2016-05-03 45 views
0

我已經編寫了一個算法,按升序對整數值進行排序,但將相應的信息保留在與其相符的相鄰單元格中。它使用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聲明爲新變量中的變體。當結果變得不可靠時,敏銳地使用MsgBox'es顯示它在<運營商處失敗邏輯測試,試圖告訴我否,對於107 < 201是肯定的(但是對於117/107不適用,就像它應該)。

如果我回去聲明BoundVal是一個整數,它開始工作罰款整數,但給我一個類型不匹配錯誤,當我嘗試它的日期。

比較Arr(j)< BoundVal有一些基本問題嗎?兩者都是變體,都是從字符串開始的。有任何想法嗎?

+0

僅僅因爲你聲明一個變量是變體並不意味着它只是在運行時保持不變。只需自己測試一下:'Dim varTMP As Variant'然後在下一行'varTMP = 20'中將其設置爲20,最後詢問變量的類型是「Debug.Print TypeName(varTMP)'。你會得到「整數」的答案。所以,也許你想包括更多的文本框來檢查你在比較... – Ralph

+0

謝謝,這就是問題所在。但BoundVal = CVar(Arr(i))在將BoundVal更改爲變體時沒有成功。該怎麼辦呢? –

回答

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 0https://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 Explicithttps://msdn.microsoft.com/en-us/library/office/gg278855.aspx

(5)使用.Value2只要有可能不是.Value一個小速度獎金。此外,這基本上只會給你一個數字的日期,而不是日期2。

所有的經過上述改變你的代碼應該是這個樣子:

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 =一天中的四分之三=下午六點。今天的日期是2016年5月4日。這是1899年12月31日之後的42,995天。因此,今天日期的.Value2是42,495。

+0

謝謝,但是'BoundVal = CLng(IIf(IsNumeric(Arr(i)),Arr(i),0))'對日期不起作用,仍會引發Type Mismatch錯誤。 –

+0

正如我的回答「VBA中的所有日期和時間都是數字」所寫。而'Arr = Split(cell.Value2,Chr(10))'這行應該確保數組只填充數字(使用'.Value2'屬性和**而不是'.Value'屬性)。但如果這個答案不能幫助你,也許別人可以幫助... – Ralph

+0

.Value2沒有什麼區別。我的代碼中的MsgBox TypeName()陷阱告訴我,Arr(j)是一個字符串,這意味着'IsNumeric(Arr(i))'每次都返回false,並且將BoundVal固定地保持爲0. –