2016-05-03 9 views
0

Ich habe einen Algorithmus geschrieben, der ganzzahlige Werte in aufsteigender Reihenfolge sortiert, aber entsprechende Informationen in benachbarten Zellen in einer Zeile sortiert hält. Er verwendet Arr als Momentaufnahme der zu sortierenden Zelle, erstellt ein Array der Indizes der Werte (TagIndex), wenn sie in aufsteigender Reihenfolge sortiert wurden, und wendet TagIndex auf diese und die angrenzenden Zellen an.Ungleichheitsoperatoren scheitern beim Vergleichen von Varianten

Zum Beispiel sollte es dies nehmen ...

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

... und sortiert nach der zweiten Spalte ganz links es in diese einzuschalten:

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

Hier ist der Code:

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 

Dieser Code arbeitete zuerst Dandy, aber ich hatte zwei getrennte Versionen - eine für das Sortieren von ganzen Zahlen und eine andere für das Sortieren Dates - und wollte einen, der beides angeht. Um das zu tun, versuchte ich BoundVal als eine Variante in der neuen zu deklarieren. Als die Ergebnisse wonky, enthüllte scharfer Gebrauch von MsgBox'es, dass es logische Tests am Operator < versagte und versuchte, mir für 96 < 201 und Ja für 107 < 75 Nein zu sagen (aber nicht für 117/107, wie es sollte).

Wenn ich BoundVal als Integer deklariere, funktioniert es gut für Integer, aber gibt mir einen Type Mismatch Fehler, wenn ich es an Daten versuche.

Gibt es ein grundlegendes Problem beim Vergleich von Arr (j) < BoundVal? Beide sind Varianten, beide stammen von Strings ab. Irgendwelche Ideen?

+0

Nur weil Sie eine Variable als Variante deklarieren, heißt das nicht, dass sie zur Laufzeit nur so bleibt. Teste es einfach selbst: 'Dim varTMP As Variant' setze es dann auf 20 in der nächsten Zeile' varTMP = 20' und frage nach dem Typ der Variablen 'Debug.Print TypeName (varTMP)'. Sie erhalten die Antwort "Integer". Also, vielleicht möchten Sie ein paar mehr TextBoxen hinzufügen, um zu überprüfen, was Sie miteinander vergleichen ... – Ralph

+0

Danke, das ist das Problem. Aber BoundVal = CVar (Arr (i)) gelingt es nicht, BoundVal zu einer Variante zu ändern. Wie sonst könnte es gemacht werden? –

Antwort

0

Sie müssen weg von all diesen Variant Variablen. Dieser Typ (ähnlich wie eine Variable als Object deklarieren) sollte Ihr aller letzter Ausweg sein. Variant kann alles sein und könnte sich sogar im Verlauf des Codes ändern (wie oben gezeigt). Also, wenn Sie sie verwenden, um sie mit etwas anderem zu vergleichen, dann könnten Sie am Ende einen Boolean (TRUE oder FALSE) mit einem Date oder einem String oder etwas anderem vergleichen. VBA ist da, um Sie zu aktivieren und die Kontrolle zu übernehmen! Also, ich würde empfehlen, dass Sie genau das tun.

Nachdem wir das gesagt haben und aufgrund der Tatsache, dass uns einige Code fehlen (offensichtlich ist der obige Code unvollständig, da er einige Variablen verwendet, die weder deklariert noch initialisiert werden), hier einige Hinweise, wie Sie Ihren Code verbessern können und möglicherweise zu einem Arbeitsergebnis selbst:

(1) Wenn Sie mehrere Variablen in einer Zeile deklarieren möchten, müssen Sie den Variablentyp für jede Variable wiederholen. So müssen Sie

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

(2) schreiben Versuchen Variant zu vermeiden, wann immer möglich, und verwenden Sie die entsprechende Art statt. Zum Beispiel scheint BoundVal ganze Zahlen oder Daten zu speichern und daher könnte der Datentyp am besten sein.

(3) Arrays sind normalerweise (VBA-Standard) von 0 bis ArrayCount - 1.So wird die Basis auf 0 gesetzt: Option Base 0https://msdn.microsoft.com/en-us/library/aa266179(v=vs.60).aspx Dennoch, um Ihren Code zu machen länger (und transparent) Ich würde diese Zeile im Code enthalten, und ändern Sie Ihre Loops zu

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

statt

For j = 0 To UBound(Arr) 

(4) Zwingen Sie sich, alle Variablen zu erklären, um sicherzustellen, dass Sie wissen, was los ist: Option Explicithttps://msdn.microsoft.com/en-us/library/office/gg278855.aspx

(5) Verwenden Sie .Value2, wann immer möglich, anstelle von .Value für eine kleine Geschwindigkeitsbonus. Darüber hinaus erhalten Sie im Wesentlichen Daten nur in Form einer Zahl und nicht als Datum².

Nachdem alle oben ändert Ihr Code wie folgt aussehen sollte:

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 

Haftungsausschluss: Ich weiß, dass diese Antwort nicht abgeschlossen ist und dass der obige Code muss wahrscheinlich einige Optimierungen, bevor es wirklich das, was man tut, will es tun. Dennoch funktioniert der Code ohne Fehler (getestet auf meinem System) und führt einige Best Practices ein, um Sie in die richtige Richtung zu führen und (möglicherweise) alle Hindernisse zu beseitigen.

² Alle Daten und Uhrzeiten in VBA sind Zahlen! Daten werden als die Tage zwischen dem 31. Dezember 1899 und dem Tag, den Sie speichern möchten, gespeichert, während Zeiten immer als Dezimalzahlen gespeichert werden, die einen Bruchteil des Tages darstellen. Also, 0,5 = ein halber Tag = 12 Uhr. 0,75 = drei Viertel eines Tages = 6 Uhr. Das heutige Datum ist der 4. Mai 2016. Das sind 42.495 Tage nach dem 31. Dezember 1899. Daher ist die .Value2 für das heutige Datum 42.495.

+0

Danke, aber 'BoundVal = CLng (IIf (IsNumerisch (Arr (i)), Arr (i), 0))' funktioniert nicht für Daten, löst einen Type Mismatch Fehler noch aus. –

+0

Wie in meiner Antwort geschrieben "Alle Daten und Zeiten in VBA sind Zahlen". Und die Zeile'Arr = Split (cell.Value2, Chr (10)) 'sollte sicherstellen, dass das Array nur mit Zahlen gefüllt ist (mit der Eigenschaft '.Value2' und ** not ** der Eigenschaft' .Value'). Aber wenn diese Antwort Ihnen nicht hilft, kann vielleicht jemand anderes helfen ... – Ralph

+0

.Value2 hat keinen Unterschied gemacht. Die MsgBox TypeName() Traps, die ich in meinem Code habe, sagen mir, dass Arr (j) ein String ist, was bedeutet, dass IsNumeric (Arr (i)) jedes Mal falsch zurückkehrt und BoundVal hartnäckig bei 0. –

Verwandte Themen