matching text EXACTLY within vba IF statement - vba

I looked through the questions here and though there is a lot of stuff about matching similar strings with the instr function etc, there isn't much about exact matching.
I'm looping through a list of names classified by id where each id has its own corresponding benchmark. Unfortunately all the benchmark names are something along the lines of "Barclays" x Index where there are a ton of similar sounding names such as Barclays US Aggregate Index, Barclays Intermediate Us Aggregate Index etc... and just trying to match gives an output.. but the wrong data points. Here is my code for reference.. the issue is in 2nd elseif of the loop.
I was wondering if there is an easy method to resolve this.
For i = 1 To lastrow
Sheets(source).Activate
If source = "Historical" Then
If Range("A" & i).Value = delimit2 Then
benchmark_name = Sheets(source).Range("L" & i).Value
j = j + 10
name = Sheets(source).Range("A" & i + 1).Value
Sheets(output_sht).Range("D" & j - 3) = "Portfolio"
Sheets(output_sht).Range("E" & j - 3) = benchmark_name
ElseIf benchmark_name <> vbNullString _
And Range("A" & i).Value = benchmark_name Then
If IsNumeric(Sheets(source).Range("F" & i).Value) Then
Alt_return3 = Sheets(source).Range("F" & i).Value
If IsEmpty(Sheets(output_sht).Cells(j, col1)) Then
Sheets(output_sht).Cells(j, col1) = Alt_return3 / 100
End If
End If
If IsNumeric(Sheets(source).Range("G" & i).Value) Then
Alt_return5 = Sheets(source).Range("G" & i).Value
If IsEmpty(Sheets(output_sht).Cells(j + 1, col1)) Then
Sheets(output_sht).Cells(j + 1, col1) = Alt_return5 / 100
End If
End If
'
If IsNumeric(Sheets(source).Range("H" & i).Value) Then
Alt_returnINC = Sheets(source).Range("H" & i).Value
If IsEmpty(Sheets(output_sht).Cells(j + 2, col1)) Then
Sheets(output_sht).Cells(j + 2, col1) = Alt_returnINC / 100
End If
Sheets(output_sht).Range("D" & j & ":E" & j + 5).NumberFormat = "0.00%"
End If
Sheets(output_sht).Range("C" & j) = period
Sheets(output_sht).Range("C" & j + 1) = period2
Sheets(output_sht).Range("C" & j + 2) = period3
Else
End If
End If
Next i

Comment as answer because I cannot comment:
Aren't you looking for the Like operator?
And you should add to the top of your code: Option compare text
More info on the like operator

I know you are looking for an exact match. However you may want to consider trying a FuzzyMatch.
http://code.google.com/p/fast-vba-fuzzy-scoring-algorithm/source/browse/trunk/Fuzzy1
You can download/import this function to your workbook and then call it with the 2 strings/names you are comparing and it will return a score.
If I were you I'd loop through all the possible names and take the highest score.
Which in your case would be 100% if you are looking for an exact match.
This will add time to your procedure but it might help you.
===EDITED
========= Here is the code. Add this to your Module.
Option Explicit
Public Declare Function GetTickCount Lib "kernel32.dll" () As Long
'To be placed in the Declarations area
'_____________________________________
Sub TestFuzzy()
Dim t As Long, a As Long, i As Long
t = GetTickCount
For i = 1 To 100000
a = Fuzzy("Sorin Sion", "Open Source")
Next
Debug.Print "Similarity score: " & a & "; " & i - 1 & " iterations took " & _
GetTickCount - t & " milliseconds"
End Sub
'TestFuzzy's result should look like:
'Similarity score: 0.3; 100000 iterations took 2094 milliseconds
'The test was done on an Intel processor at 3.2GHz
'_____________________________________
Public Function Fuzzy(ByVal s1 As String, ByVal s2 As String) As Single
Dim i As Integer, j As Integer, k As Integer, d1 As Integer, d2 As Integer, p As Integer
Dim c As String, a1 As String, a2 As String, f As Single, o As Single, w As Single
'
' ******* INPUT STRINGS CLEANSING *******
'
s1 = UCase(s1) 'input strings are converted to uppercase
d1 = Len(s1)
j = 1
For i = 1 To d1
c = Mid(s1, i, 1)
Select Case c
Case "0" To "9", "A" To "Z" 'filter the allowable characters
a1 = a1 & c 'a1 is what remains from s1 after filtering
j = j + 1
End Select
Next
If j = 1 Then Exit Function 'if s1 is empty after filtering
d1 = j - 1
s2 = UCase(s2)
d2 = Len(s2)
j = 1
For i = 1 To d2
c = Mid(s2, i, 1)
Select Case c
Case "0" To "9", "A" To "Z"
a2 = a2 & c
j = j + 1
End Select
Next
If j = 1 Then Exit Function
d2 = j - 1
k = d1
If d2 < d1 Then
'to prevent doubling the code below s1 must be made the shortest string,
'so we swap the variables
k = d2
d2 = d1
d1 = k
s1 = a2
s2 = a1
a1 = s1
a2 = s2
Else
s1 = a1
s2 = a2
End If
If k = 1 Then 'degenerate case, where the shortest string is just one character
If InStr(1, s2, s1, vbBinaryCompare) > 0 Then
Fuzzy = 1 / d2
Else
Fuzzy = 0
End If
Else '******* MAIN LOGIC HERE *******
i = 1
f = 0
o = 0
Do 'count the identical characters in s1 and s2 ("frequency analysis")
p = InStr(1, s2, Mid(s1, i, 1), vbBinaryCompare)
'search the character at position i from s1 in s2
If p > 0 Then 'found a matching character, at position p in s2
f = f + 1 'increment the frequency counter
s2 = Left(s2, p - 1) & "~" & Mid(s2, p + 1)
'replace the found character with one outside the allowable list
'(I used tilde here), to prevent re-finding
Do 'check the order of characters
If i >= k Then Exit Do 'no more characters to search
If Mid(s2, p + 1, 1) = Mid(s1, i + 1, 1) Then
'test if the next character is the same in the two strings
f = f + 1 'increment the frequency counter
o = o + 1 'increment the order counter
i = i + 1
p = p + 1
Else
Exit Do
End If
Loop
End If
If i >= k Then Exit Do
i = i + 1
Loop
If o > 0 Then o = o + 1 'if we got at least one match, adjust the order counter
'because two characters are required to define "order"
finish:
w = 2 'Weight of characters order match against characters frequency match;
'feel free to experiment, to get best matching results with your data.
'If only frequency is important, you can get rid of the second Do...Loop
'to significantly accelerate the code.
'By altering a bit the code above and the equation below you may get rid
'of the frequency parameter, since the order counter increments only for
'identical characters which are in the same order.
'However, I usually keep both parameters, since they offer maximum flexibility
'with a variety of data, and both should be maintained for this project
Fuzzy = (w * o + f) / (w + 1) / d2
End If
End Function
==================
So once you have it then just add something like this.
Dim strA, strB, hiScore(1 to 3), tempScore
With Thisworkbook.ActiveSheet
For a = 1 to .Usedrange.Rows.Count ' Scans Column 1
strA = .cells(a,1) ' Barclays Aggregate Index
For b = 1 to .usedrange.rows.count ' Compares Col 1 to Col 2
strB = .cells(b,2) ' Barclays Aggregate Other Index
tempScore = Fuzzy(strA, strB)
If tempScore > hiScore then
hiScore(1) = tempScore
hiScore(2) = a
hiScore(3) = b
End If
Next b
' Do your Action with the Best Match Here
If hiScore(1) = 1 then ' (100% - perfect match)
' Copies col 3 from the row that the best strB match was on
' to col 4 from the row strA was on
.Cells(a,4) = .Cells(hiScore(3),3)
End If
' ====
' Reset Variables
hiScore = ""
tempScore = ""
Next a
End with

Related

Cell by cell diff in excel

I've seen a couple similar questions, but nothing I've found quite hits the mark with me.
I have two tables on two different sheets. The tables themselves are structured identically, but hold different content. Each data cell within the table can have MULTIPLE lines of information (e.g. E5 is 5 lines (actual lines, not wrapped) of data).
I'd like to create a third table that is also structurally identical (row 1 and column A are the same) to the first two, but have each data cell contain in some meaningful syntax the difference between the same two cells in the first two tables. It could be as straightforward as the output of a diff between the contents of the first two cells.
Is there a way to essentially take the diff of sheet1!E5 and sheet2!E5 and store it in sheet3!E5 ?
EDIT:
Some clarification. All the data cells contain information on MULTIPLE lines. Some lines are present in one sheet but not in another. For instance, sheet1!E5 may contain "string1", "string2", and "string3" on THREE lines whereas sheet2!E5 contains "string2", "string3", "string7", and "string8" on FOUR lines. The output on sheet3!E5 could be anything that clearly marks the difference between the two, for instance (as was mentioned before), exactly what the output would be if the two cells were text files and you ran a diff (or diff -c) on them like this:
*** 1,3 ****
- string1
string2
! string3
\ No newline at end of file
--- 1,4 ----
string2
! string3
! string7
! string8
\ No newline at end of file
or even just something much simpler that lists the strings that are/are not in each cell like:
< string1
<> string2
<> string3
> string7
> string8
Using the code at https://en.wikipedia.org/wiki/Longest_common_subsequence_problem I made the following VBA code. It isn't verbatim because VBA doesn't allow short circuit operators and I needed PrintDiff to return the string instead of displaying it.
Function LCSLength(C() As Integer, X() As String, Y() As String, M As Integer, N As Integer) As Integer
Dim I As Integer
For I = 0 To M
C(I, LBound(Y)) = 0
Next
Dim J As Integer
For J = 0 To N
C(LBound(X), J) = 0
Next
For I = 1 To M
For J = 1 To N
If X(I) = Y(J) Then
C(I, J) = C(I - 1, J - 1) + 1
ElseIf C(I, J - 1) < C(I - 1, J) Then
C(I, J) = C(I - 1, J)
Else
C(I, J) = C(I, J - 1)
End If
Next
Next
LCSLength = C(M, N)
End Function
Function PrintDiff(C() As Integer, X() As String, Y() As String, I As Integer, J As Integer) As String
Continue = 1
If Continue = 1 And I > 0 And J > 0 Then
If X(I) = Y(J) Then
PrintDiff = PrintDiff(C, X, Y, I - 1, J - 1) & Chr(10) & "<> " & X(I)
Continue = 0
End If
End If
If Continue = 1 And J > 0 Then
If I = 0 Then
PrintDiff = PrintDiff(C, X, Y, I, J - 1) & Chr(10) & "> " & Y(J)
Continue = 0
ElseIf C(I, J - 1) >= C(I - 1, J) Then
PrintDiff = PrintDiff(C, X, Y, I, J - 1) & Chr(10) & "> " & Y(J)
Continue = 0
End If
End If
If Continue = 1 And I > 0 Then
If J = 0 Then
PrintDiff = PrintDiff(C, X, Y, I - 1, J) & Chr(10) & "< " & X(I)
Continue = 0
ElseIf C(I, J - 1) < C(I - 1, J) Then
PrintDiff = PrintDiff(C, X, Y, I - 1, J) & Chr(10) & "< " & X(I)
Continue = 0
End If
End If
If Continue = 1 Then
PrintDiff = ""
End If
End Function
Function Diff(A As String, B As String) As String
Dim X() As String
X = Split(Chr(10) & A, Chr(10))
Dim M As Integer
If (A = "") Then
M = 0
Else
M = UBound(X)
End If
Dim Y() As String
Y = Split(Chr(10) & B, Chr(10))
Dim N As Integer
If (B = "") Then
N = 0
Else
N = UBound(Y)
End If
Dim C() As Integer
ReDim C(M, N) As Integer
Call LCSLength(C, X, Y, M, N)
Diff = Mid(PrintDiff(C, X, Y, M, N), 2)
End Function
If A1 contains:
string1
string2
string3
And B1 contains:
string2
string3
string7
string8
And if C1 contains =Diff(A1,B1) then C1 will display:
< string1
<> string2
<> string3
> string7
> string8
According to the "Code optimization" section on that page several optimizations can be made to the code - I haven't done those in this code.
Yes. duplicate your structure in Sheet 3, and simply make sure all your references are accurate. For example, in cell A1 on sheet 3, you would just type:
= [navigate to sheet1!A1 and click it] - [navigate to sheet2!A1 and click it]
pretty easy, actually.

Print a sequence using VBA Macro

I want to print letter "A" in the cells corresponding to this formula:
1+3*1 = 4
4+3*2 = 10
10+3*3 = 19
I want to enter "A" in the cells A4, A10, A19. I want to do this for 25 cells.
How to do that
You'll have to test if you are not going to far though :
Sub test_matangraj()
Dim i As Integer
Dim k As Integer
Dim Col As Long
Col = 1
For i = 1 To 25
Col = Col + i * 3
If Col < Columns.Count Then
Cells(1, Col) = "A"
For k = 1 To i - 1
If Col + k < Columns.Count Then Cells(1, Col + k) = "A"
Next k
Else
MsgBox "Number of columns excedeed!" & vbcrlfr & _
"Col: " & Col, vbCritical + vbOKOnly
Exit For
End If
Next i
End Sub

Recursive Function in Excel VBA using GoTo, Characters Combinations from Array

I want to create a recursive function in Excel VBA without using nested Loops. I used GoTo to do it as I think it is very fast as compared to For loop etc. PROBLEM: problem is that the first Label i.e. 'a' does not perform all iterations and the required combinations are not returned so. From the given array 'arr' there should be 39 combinations but just 14 are returned. I i try to change some lines of code the total iterations 'iNum' returns 39, but not 39 combinations (combinations starting from 'a' are always missing). Please help, Thanks.
Function rec_n()
Dim a As Integer, b As Integer, c As Integer
Dim aSize As Integer, iNum As Integer
Dim myStr As String
'Dim arr As Variant
Dim arr(5) As String
'arr = Array("a", "b", "c", "d")
arr(0) = "a"
arr(1) = "b"
arr(2) = "c"
'arr(3) = "d"
aSize = 3 - 1
'a = 0: b = 0: c = 0
a: If a < aSize Then
myStr = myStr & arr(a) & ", "
a = a + 1: iNum = iNum + 1
b: If b < aSize Then
myStr = myStr & arr(a) & arr(b) & ", "
b = b + 1: iNum = iNum + 1
c: If c < aSize Then
'On Error Resume Next
myStr = myStr & arr(a) & arr(b) & arr(c) & ", "
c = c + 1: iNum = iNum + 1
GoTo c
Else
c = 0
'MsgBox c
End If
GoTo b
Else
b = 0
'MsgBox b
End If
GoTo a
End If
EndFunc:
MsgBox iNum & vbLf & myStr
Range("a2").Value = myStr
End Function
EDITED:
The code is resulting in just these combinations:
a, ba, bba, bbb, bb, bca, bcb, b, ca, cba, cbb, cb, cca, ccb,
where as these 39 are expected:
a, b, c, aa, ab, ac, ba, bb, bc, ca, cb, cc, aaa, aab, aac, aba, abb,
abc, aca, acb, acc, baa, bab, bac, bba, bbb, bbc, bca, bcb, bcc, caa,
cab, cac, cba, cbb, cbc, cca, ccb, ccc,
Here is a goto-free recursive approach:
Function StringsFrom(A As Variant, Optional maxlen As Variant) As Variant
'returns a 0-based array of all strings of length <= maxlen
'with elements drawn from A
'A is assumed to be 0-based array
'If maxlen is missing then it is taken to be the number of elements in A
Dim strings As Variant
Dim newstrings As Variant
Dim i As Long, j As Long, k As Long, m As Long, n As Long
If IsMissing(maxlen) Then maxlen = 1 + UBound(A)
m = UBound(A)
If maxlen < 1 Then Exit Function
If maxlen = 1 Then
'basis case -- return a copy of A - coerced to be strings if needed
ReDim newstrings(0 To m)
For i = 0 To m
newstrings(i) = CStr(A(i))
Next i
Else
strings = StringsFrom(A, maxlen - 1)
n = UBound(strings)
ReDim newstrings(0 To n + (m + 1) ^ maxlen)
'first copy strings to newstrings:
For i = 0 To n
newstrings(i) = strings(i)
Next i
k = n + 1 'points to current index in newstrings
'now -- load up the rest using a nested loop:
For i = 0 To m
For j = n + 1 - (m + 1) ^ (maxlen - 1) To n
newstrings(k) = A(i) & strings(j)
k = k + 1
Next j
Next i
End If
StringsFrom = newstrings
End Function
For e.g. maxlen = 4 and A has 5 strings it will first find all strings of length <= maxlen - 1 = 3 and then tack on the characters to those strings of length exactly 3. I had to do a bit of arithmetic to get the indices just right.
Here is some testing code:
Sub test()
Dim start As Double, elapsed As Double, A As Variant, B As Variant
A = Array("a", "b", "c")
B = StringsFrom(A)
MsgBox Join(B, " ") & vbCrLf & 1 + UBound(B) & " strings"
A = Array("a", "b", "c", "d", "e", "f", "g")
start = Timer
B = StringsFrom(A)
elapsed = Timer - start
MsgBox Round(elapsed, 2) & " seconds to process " & 1 + UBound(B) & " strings"
End Sub
The first test correctly gives 3 + 9 + 27 = 39 strings, and the second test gives (on my machine) the message: "0.68 seconds to process 960799 strings". When I increase A more I run out of memory before time is an issue.
ON EDIT: Here is a non-recursive approach. It is slower than the recursive approach but isn't subject to out of memory problems. It is based on the idea that if e.g. your letters are "abc" then you can view e.g. strings of length 4 from those letters as being numbers in base 3 ( = Len("abc")) so to enumerate them just count from 0 to 3^4 -1 = 80, translate each number to base 3, then use the correspondence `0 <=> "a", 1 <=> "b" etc.):
Sub Enumerate(letters As String, maxlen As Long, Optional display As Boolean = True)
'letters is assumed to have no repeated characters
Dim i As Long, j As Long, n As Long, q As Long, r As Long
Dim counter As Long
Dim s As String
Dim A As Variant
n = Len(letters)
ReDim A(0 To n - 1)
For i = 1 To n
A(i - 1) = Mid(letters, i, 1)
Next i
For i = 1 To maxlen
For j = 0 To n ^ i - 1
s = ""
q = j
If q = 0 Then
s = A(0)
Else
Do While q > 0
r = q Mod n
q = Int(q / n)
s = A(r) & s
Loop
End If
s = String(i - Len(s), A(0)) & s
counter = counter + 1
If display Then Debug.Print s
Next j
Next i
Debug.Print counter
End Sub
Tested like:
Sub test2()
Dim start As Double, elapsed As Double
Enumerate "abc", 3
start = Timer
Enumerate "abcdefghijklmnopqrstuvwxyz", 5, False
elapsed = Timer - start
Debug.Print Round(elapsed, 2)
End Sub
output of the time part of the test: shows that it takes (on my machine) about 18 seconds to loop through all of the (over 12.3 million) strings from the lower case standard alphabet of length <= 5. Some improvements are possible, but you aren't going to be able to get the sorts of speed you would need to brute force long strings drawn from a large alphabet.
VBA is an interpreted language. I think of it as a good tool for putzing around the solar system. If you want to explore the galaxy -- use C. If you want to explore other galaxies -- hope that quantum computers can be made to work.
On Further Edit: For fun I wrote a different version of Enumerate. It is about 33% faster than the last version and can loop generate nearly a million strings per second (at least on my somewhat average laptop). It is still based on thinking of strings as numbers in base n = length(letters) but simulates adding 1 to go from 1 number to the next, with an array used to look up which character results from "adding one" to a letter:
Sub Enumerate2(letters As String, maxlen As Long, Optional display As Boolean = True)
'letters is assumed to have no repeated characters
'prints all letter combos of length <= maxlen
'this one simulates the process of adding one to a string
Dim i As Long, j As Long, k As Long, n As Long, p As Long
Dim carry As Boolean
Dim counter As Long
Dim s As String
Dim num As Variant
Dim Successor(127) As String, Z As String, digit As String
n = Len(letters)
For i = 1 To n - 1
Successor(Asc(Mid(letters, i, 1))) = Mid(letters, i + 1, 1)
Next i
Z = Mid(letters, 1, 1) 'the "zero" of the base-n system
Successor(Asc(Mid(letters, n, 1))) = Z
For i = 1 To maxlen
ReDim num(1 To i) 'used to count from 0 to n^i - 1 in base n
For k = 1 To i
num(k) = Z
Next k
For j = 0 To n ^ i - 1
'get current s
s = Join(num, "")
counter = counter + 1
'now add 1 to num
carry = True
p = i 'points to rightmost "digit"
Do While p > 0 And carry
digit = Successor(Asc(num(p)))
If digit <> Z Then carry = False
num(p) = digit
p = p - 1
Loop
'the real code would go here:
If display Then Debug.Print s
Next j
Next i
Debug.Print counter
End Sub

How do I make my program stop at a certain cell?

Hey guys I am new to VBA for excel and I am stuck with a problem.
I am trying to do some calculations for data input and I have to make my program stop displaying values on the worksheet before "Discrepancy" reaches any less than 5. This then should make both columns "Money" and "Discrepancy" stop together. After, the program will then start in another column (column "I1" for "Money2" and J1" for "Discrepancy2") when t=10 is inputted into the formula and the values are displayed in Columns I2 and J2 until till the end.
I'm not sure how to stop it before it reaches and also how to stop the other column simultaneously. I'm also not sure if it will continue for another t=10.
Any advice
Sub solver2()
Dim t As Double, v As Double, i As Integer
Dim rowG As Integer, rowH As Integer
i = 0: v = 0 'related to formuala
'Range("A3").Select
'Range("D3").Select
Range("G1").Value = "Money"
Range("H1").Value = "Discrepancy"
Range("G2").Select
For t = 0 To tf Step delta
ActiveCell.Offset(i, 0) = t
ActiveCell.Offset(i, 1) = v
v = v + delta * accel(t, v)
i = i + 1
Next t
rowG = ActiveSheet.Range("G2").End(xlDown).row
rowH = ActiveSheet.Range("H2").End(xlDown).row
For i = rowG To 1 Step -1
Dim val1 As Long
val1 = ActiveSheet.Range("G" & i).Value
If (val1 > 5) Then
ActiveSheet.Range("G" & i).EntireRow.Delete
End If
Next i
For i = rowH To 1 Step -1
Dim val2 As Long
val2 = ActiveSheet.Range("G" & i).Value
If (val2 > 5) Then
ActiveSheet.Range("G" & i).EntireRow.Delete
End If
Next i
For t = 0 To 10 Step delta 'This steps it per delta input
Range("I1").Value = "Money2"
Range("J1").Value = "Discrepancy2"
Range("I2").Select
ActiveCell.Offset(i, 0) = t
ActiveCell.Offset(i, 1) = v
v = v + delta * accel(t, v)
i = i + 1
Next t
End Sub
If you just need the cells to appear empty, you could use conditional formatting to set the text and background colors the same.
You might try a do while loop instead of a for loop to set the values in the first set:
Do While t <= tf And v < 5
ActiveCell.Offset(i, 0) = t
ActiveCell.Offset(i, 1) = v
v = v + Delta * accel(t, v)
i = i + 1
t = t + Delta
Loop
I'm not sure what you intended for the other columns, but this loop would leave t at the value you would use if you mean to continue where the first column left off

Naturally Sort a Directory in VBA

I am running a VBA script through a directory but I need to go through the files as if they were sorted in Windows Explorer. For example I have a directory like so:
32 Read.log
64 Write.log
256 Read.log
512 Write.log
1024 Write.log
4 Read.log
When I sort it with VBA, it only looks at the first character to sort it like so:
1024 Write.log
256 Read.log
32 Read.log
4 Read.log
512 Write.log
64 Write.log
Any ideas on how to sort from smallest to largest number before I go through the directory?
I actually went to the trouble of building an algorithm for it:
Dim a As Variant
Dim c As String
Dim d As String
Dim x As Long
Dim y As Long
Dim s As Boolean
Dim p As Long
Dim q As Long
Dim e As Long
Dim n1 As String
Dim n2 As String
'Create a dummy array to test
a = Array("1024 Write.log", "256 Read.log", "32 Read.log", "4 Read.log", "512 Write.log", "64 Write.log")
'Loop through the array and look for values that need to change position
For x = LBound(a) To UBound(a) - 1
For y = x + 1 To UBound(a)
'Check if the values at x and y must be swapped
s = False
'Loop through each character in both strings to do a compare
If Len(a(x)) > Len(a(y)) Then e = Len(a(x)) Else e = Len(a(y))
For p = 1 To e
If Len(a(x)) < p Then
'y is longer, so it should come last
Exit For
ElseIf Len(a(y)) < p Then
'y is shorter, so it should come first
s = True
Exit For
ElseIf InStr("0123456789", Mid(a(x), p, 1)) = 0 Or InStr("0123456789", Mid(a(y), p, 1)) = 0 Then
'The char at p in x or y is not a number, so do a text compare
If Mid(a(x), p, 1) < Mid(a(y), p, 1) Then
Exit For
ElseIf Mid(a(x), p, 1) > Mid(a(y), p, 1) Then
s = True
Exit For
End If
Else
'The char at p for both x and y are numbers, so get the whole numbers and compare
'Get the number for x
n1 = ""
q = p
Do While q <= Len(a(x)) And InStr("0123456789", Mid(a(x), q, 1)) <> 0
n1 = n1 & Mid(a(x), q, 1)
q = q + 1
Loop
'Get the number for y
n2 = ""
q = p
Do While q <= Len(a(y)) And InStr("0123456789", Mid(a(y), q, 1)) <> 0
n2 = n2 & Mid(a(y), q, 1)
q = q + 1
Loop
If Len(n1) > Len(n2) Then
'n1 is a bigger number, so it should be last
s = True
Exit For
ElseIf Len(n1) < Len(n2) Then
'n1 is smaller, so it should remain first
Exit For
ElseIf n1 > n2 Then
'n1 is a bigger number, so it should be last
s = True
Exit For
ElseIf n1 < n2 Then
'n1 is smaller, so it should remain first
Exit For
End If
End If
Next
'Do the swap
If s Then
c = a(y)
a(y) = a(x)
a(x) = c
End If
Next
Next
'Verify that it worked
c = ""
For p = LBound(a) To UBound(a)
c = c & a(p) & vbCrLf
Next
MsgBox c
Once the data has been imported into Excel, parse the data so the size is in column A and the name is in column B. Then insure (or convert) the data in column A to be values rather than Text. Then sort columns A & B by A Ascending.
Read the directory into a dictionary object as in CreateObject("Scripting.Dictionary") and write a function that will sort the dictionary in the exact way you want.
An example can be found at this question:
Sort dictionary
EDIT: If you already have it in an array, you can adjust the code to just sort the array
EDIT: Simple example of using a dictionary:
Dim vArray As Variant
Dim vDict As Object
Dim i As Variant
vArray = Array("F1", "F2", "F3")
Set vDict = CreateObject("Scripting.Dictionary")
For i = LBound(vArray) To UBound(vArray)
vDict.Add i, vArray(i)
Next
For Each i In vDict
MsgBox "Key: " & i & ", Value: " & vDict(i)
Next