Cell by cell diff in excel - vba

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.

Related

Excel vba How to get combination of the numbers output into excel rows?

I need to find out a way to output the result of all combinations of the numbers into rows ( best if could be in a single row)
I have 8 digits {1,2,3,4,5,6,7,8} the typical output for the combination is i;j (i, j are numbers from the set and i< j) if pick up two. To generate result is simple:
Dim Myarray_2 As String
Dim sht as Worksheet
set sht = Sheet1
Myarray_2 = "" ' pick up 2 out of 8
For j = 2 To 8
For i = 1 To j - 1
sht.Cells(i + 1, j + 1) = Str(MyArray(i)) + ";" + Str(MyArray(j))
Myarray_2 = Myarray_2 + Str(MyArray(i)) + ";" + Str(MyArray(j)) + "|"
Next i
Next j
This is an example for pick up 2, I already have it output to rows of a worksheet.
I also have solution for picking up 3, now my questions is for the rest of the cases, how to get the out put?
Here is the solution for picking up 3:
Dim Myarray_3 As String
Myarray_3 = "" ' 3 out of 8
k = 3
Do While k >= 3 And k <= 8
'inner loop through i j
For j = 2 To k - 1
For i = 1 To j - 1
sht.Cells(i + 11, j - 1 + m) = Str(MyArray(i)) + ";" + Str(MyArray(j)) + ";" + Str(MyArray(k))
Myarray_3 = Myarray_3 + Str(MyArray(i)) + ";" + Str(MyArray(j)) + ";" + Str(MyArray(k)) + "|"
Next i
Next j
k = k + 1
m = m + 7
Loop
By the way the MyArray(i) is initialized as Myarray(i) = i
I found some code that I got from another good programmer, I changed the code to fit to your problem. If you have N as the number of members of your set/array, then you will have (2^N)-1 combinations, however you can filter them using your own conditions. Note that in your problem, the order of the members would be important when it comes to filtration using your condition.
The code will first generate ALL OF THE COMBINATIONS and then apply the conditions. Array Result would be the main output so its size will be always (2^N)-1. Array Result-filtered will be what you want.
Note that if you have the numbers sorted from left to right, arrays Result and Result_filtered will be the same.
You can print the out put with any format you like into any sheet.
This method uses bitwise calculation to get the combinations:
if N=2, then number of comnibations would be (2^2)-1=3
we always exclude 0 'in binary of course
{A,B} -> { [00],[01],[10],[11] } ->{ ignore, [B],[A],[AB]}
I hope this helps! if it did, please hit the checkmark to this anwer
Run Sub Test:
Sub Test()
Dim bCondSatisfied As Boolean
Dim InxComb As Integer
Dim InxResult As Integer
Dim count As Integer
Dim i As Integer
Dim j As Integer
Dim arr() As String
Dim TestData() As Variant
Dim Result() As Variant
Dim Result_filtered() As Variant
TestData = Array(1, 3, 2, 4)
Call GenerateCombinations(TestData, Result)
'Now you have all the possible combinations, you can apply custom conditions
'(e.g. any number on the left side of another number should be smaller, practically this is satisfied with the
' given test array, but if the numbers are scrambled it will fix the problem)
ReDim Result_filtered(0 To 0)
Result_filtered(0) = "No Combination Matched the Condition" 'default for the case there is no result matched
count = 0
For i = 0 To UBound(Result)
arr() = Result(i)
bCondSatisfied = True
If UBound(arr) > 0 Then 'if there is more than one number in the array, compare the adjacent numbers
For j = 0 To UBound(arr) - 1
If arr(j) > arr(j + 1) Then
bCondSatisfied = False
Exit For
End If
Next j
End If
'Store the array in the filtered array if it passed the test
If bCondSatisfied = True Then
ReDim Preserve Result_filtered(count)
Result_filtered(count) = arr
count = count + 1
End If
Next i
'Print Result
For InxResult = 0 To UBound(Result)
Debug.Print Right(" " & InxResult + 1, 3) & " ";
For InxComb = 0 To UBound(Result(InxResult))
Debug.Print "[" & Result(InxResult)(InxComb) & "] ";
Next
Debug.Print
Next
Debug.Print "-----------------" 'separate two results
'Print Result_filtered
For InxResult = 0 To UBound(Result_filtered)
Debug.Print Right(" " & InxResult + 1, 3) & " ";
For InxComb = 0 To UBound(Result_filtered(InxResult))
Debug.Print "[" & Result_filtered(InxResult)(InxComb) & "] ";
Next
Debug.Print
Next
End Sub
Sub GenerateCombinations(ByRef AllFields() As Variant, _
ByRef Result() As Variant)
Dim InxResultCrnt As Integer
Dim InxField As Integer
Dim InxResult As Integer
Dim i As Integer
Dim NumFields As Integer
Dim Powers() As Integer
Dim ResultCrnt() As String
NumFields = UBound(AllFields) - LBound(AllFields) + 1
ReDim Result(0 To 2 ^ NumFields - 2) ' one entry per combination
ReDim Powers(0 To NumFields - 1) ' one entry per field name
' Generate powers used for extracting bits from InxResult
For InxField = 0 To NumFields - 1
Powers(InxField) = 2 ^ InxField
Next
For InxResult = 0 To 2 ^ NumFields - 2
' Size ResultCrnt to the max number of fields per combination
' Build this loop's combination in ResultCrnt
ReDim ResultCrnt(0 To NumFields - 1)
InxResultCrnt = -1
For InxField = 0 To NumFields - 1
If ((InxResult + 1) And Powers(InxField)) <> 0 Then
' This field required in this combination
InxResultCrnt = InxResultCrnt + 1
ResultCrnt(InxResultCrnt) = AllFields(InxField)
End If
Next
' Discard unused trailing entries
ReDim Preserve ResultCrnt(0 To InxResultCrnt)
' Store this loop's combination in return array
Result(InxResult) = ResultCrnt
Next
End Sub

The Fastest Permutation Code Permutating Numbers in VB NET

There is a famous and the fastest permutation code without any "function" for VB .Net to permutate numbers just in several rows, that I can't remember unfortunately.
Is there anyone know this code? Or know like this?
Some part of the code is here:
UPDATE: I FOUND IT. ALL THE WORKING CODES HERE:
Dim L(4) As Byte
Dim I As Byte
Dim K As Byte
Dim J As Byte
Dim RESULTS As String
Dim UB, UBm1 As Integer
L = {1, 2, 3, 4, 5}
UB = L.GetUpperBound(0)
UBm1 = UB - 1
Do
I = UBm1
Do While I > 0 And L(I) >= L(I + 1)
I -= 1
Loop
K = L(I)
J = UB
Do While J > 0 And L(J) <= K
J -= 1
Loop
RESULTS = L(0) & "," & L(1) & "," & L(2) & "," & L(3) & "," & L(4)
L(I) = L(J)
L(J) = K
Array.Reverse(L, I + 1, UB - I)
Loop While J
It seems like you're looking for this ...
http://www.cut-the-knot.org/do_you_know/AllPerm.shtml
(2. Lexicographic order and finding the next permutation)
... ?
In case you are, the starting values for I and J are incorrect (they should be 4 and 5 instead of 3 and 4 respectively).
(I know the example uses swap, but that can be replaced with a single colon-delimited line.)
Dim L(4) As Byte
L = {1, 2, 3, 4, 5}
Dim K as Byte
For N as integer = 1 to 120 'No. of permutations: 5!
Dim I As Byte = 4, J as Byte = 5
While L(I - 1) >= L(I)
I -= 1
End While
While L(J - 1) <= L(I - 1)
J -= 1
End While
K = L(I - 1) : L(I - 1) = L(J - 1) : L(J - 1) = K
I += 1 : J = 5
While I < J
K = L(I - 1) : L(I - 1) = L(J - 1) : L(J - 1) = K
I += 1 : J -= 1
End While
Dim RESULT as String = L(0) & "," & L(1) & "," & L(2) & "," & L(3) & "," & L(4)
'Export / print RESULT as you like, e.g. Console.WriteLine or something
Next
For obtaining a set of permutations of a natural number (recommended less than 10 though it can be bigger than that), this VBA Excel sub-routine is very fast and short. I believe it can be easy to convert it to VB.NET. Have a look.
Const P = 5 'Recommended Max P = 9
Dim m As Long, PArray(1 To 1000, 1 To P) 'Recommended Max 9! = 362880
Public Sub PermutationNaturalNumber()
Dim Q(1 To P) As Long
For m = 1 To P: Q(m) = m: Next: m = 0
PermutationGenerator P, Q
Range("A1").Resize(UBound(PArray), P) = PArray: End
End Sub
Sub PermutationGenerator(n As Long, Q() As Long)
Dim i As Long, j As Long, k As Long
If n > 1 Then
For i = 1 To n - 1
PermutationGenerator n - 1, Q
If n Mod 2 = 1 Then j = 1 Else j = i
k = Q(j): Q(j) = Q(n): Q(n) = k
Next
PermutationGenerator n - 1, Q
Else
m = m + 1: For i = 1 To P: PArray(m, i) = Q(i): Next
End If
End Sub
P can be any natural number, in this case {1, 2, 3, 4, 5}. Make sure you change the upper bound of PArray with P! meaning (P*(P-1)*(P-2)*...*1).

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

Excel: Split ; separated cell values into columns and then shift in consecutive rows

I'm in the situation described by fig.1 where I have a cell with the reference name and a cell with one or more semicolon separated emails associated to the same reference. I'd like to split the cells contaning more than one email stacking them consecutively and copying the refence name. Is it possible to do this with a VBA Macro in Excel 2007? I know the existence of the "Split in columns" command, but I don't know how to automatically shift the columns in rows and copying the reference name. Thanks in advance.
Here you go:
Sub SplitColumnB()
Dim r As Range
Set r = [B2]
Do While r.Value <> ""
res = Split(r.Value, " ; ")
i = 0
For Each resStr In res
If i > 0 Then r.Offset(1).EntireRow.Insert xlDown
r.Offset(IIf(i > 0, 1, 0)).Value = resStr
r.Offset(IIf(i > 0, 1, 0), -1).Value = Right(resStr, Len(resStr) - InStr(resStr, "#"))
i = i + 1
Next
Set r = r.Offset(IIf(i > 0, i, 1))
Loop
End Sub
Try with the below code. Replace all instances of Sheet1 with the name of your worksheet.
Sub test()
Dim Ref As String
Dim Eid As String
Dim RefR()
Dim EidR()
Rcnt = Sheets("Sheet1").Range("A65000").End(xlUp).Row
K = 0
L = 0
For i = 2 To Rcnt
Ref = Sheets("Sheet1").Range("A" & i).Value
Temp = Split(Sheets("Sheet1").Range("B" & i).Value, ";")
K = K + 1
ReDim Preserve RefR(1 To K)
RefR(K) = Ref
For j = LBound(Temp) To UBound(Temp)
If L <= UBound(Temp) Then
ReDim Preserve EidR(Rcnt, L)
L = UBound(Temp)
End If
EidR(K, j) = Temp(j)
Next j
Next i
RowValue = 2
For i = 1 To UBound(RefR)
For j = 0 To L
Sheets("Sheet1").Range("A" & RowValue).Value = RefR(i)
Sheets("Sheet1").Range("B" & RowValue).Value = Trim(EidR(i, j))
RowValue = RowValue + 1
Next j
Next i
End Sub

matching text EXACTLY within vba IF statement

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