How can I tell the differences between two strings in Excel? - excel

I created an assessment that applicants fill out in Excel. I have a key where I copy their answers in and it tells me if their answers match my key. I'd like to add a formula that will also show me the differences between the applicant's cell (B2) and the key's cell (A2), to make it easier to see what they got wrong.
I tried using =SUBSTITUTE(B2,A2,"") but this only gives me differences at the beginning or end of the string. Usually, the difference is in the middle.
For example, my key (cell A2) might say: Cold War | Bay of Pigs | Fidel Castro
And the applicant (cell B2) might say: Cold War | Cuban Missile Crisis | Fidel Castro
I want this formula to return: "Cuban Missile Crisis"

You may try something like this...
Function CompareStrings(keyRng As Range, ansRng As Range) As String
Dim arr() As String
Dim i As Long
arr() = Split(ansRng.Value, "|")
For i = 0 To UBound(arr)
If InStr(keyRng.Value, arr(i)) = 0 Then
CompareStrings = arr(i)
Exit Function
End If
Next i
End Function
Then you can use this UDF like below...
=CompareStrings(A2,B2)
If you want to compare them in the reverse order also and return the not matched string part from any of them, try this...
Function CompareStrings(ByVal keyRng As Range, ByVal ansRng As Range) As String
Dim arr() As String
Dim i As Long
Dim found As Boolean
arr() = Split(ansRng.Value, "|")
For i = 0 To UBound(arr)
If InStr(keyRng.Value, Trim(arr(i))) = 0 Then
found = True
CompareStrings = arr(i)
Exit Function
End If
Next i
If Not found Then
arr() = Split(keyRng.Value, "|")
For i = 0 To UBound(arr)
If InStr(ansRng.Value, Trim(arr(i))) = 0 Then
CompareStrings = arr(i)
Exit Function
End If
Next i
End If
End Function
Use this as before like below...
=CompareStrings(A2,B2)
So the function will first compare all the string parts of B2 with A2 and if it finds any mismatch, it will return that part of string and if it doesn't find any mismatch, it will then compare all the parts of string in A2 with B2 and will return any mismatch part of string. So it will compare both ways.

The function above displays only the first difference. Here is an update that displays all differences between two strings.enter image description here
Function CompareStrings(ByVal keyRng As Range, ByVal ansRng As Range) As String
Dim arr() As String
Dim i As Long
arr() = Split(ansRng.Value, " ")
CompareStrings = "+["
For i = 0 To UBound(arr)
If InStr(keyRng.Value, Trim(arr(i))) = 0 Then
CompareStrings = CompareStrings & " " & arr(i)
End If
Next i
CompareStrings = CompareStrings & " ] -["
arr() = Split(keyRng.Value, " ")
For i = 0 To UBound(arr)
If InStr(ansRng.Value, Trim(arr(i))) = 0 Then
CompareStrings = CompareStrings & " " & arr(i)
End If
Next i
CompareStrings = CompareStrings & " ]"
End Function

Related

VBA printing a substring from a string

I would like to print each substrings in between the "..." from this string: "...covid...is...very...scary" in consecutive cells in a column in excel.
this is my code in VBA.
Sub copyd()
findandcopy("...covid...is...very...scary") 'not sure how to print in consecutive cells of a column
End Sub
Function findandcopy(brokenstr As String) As String
Dim first, second As Integer
Dim strtarget as string
strtarget = "..."
Do until second =0. 'second=0 so that loop ends when there is no more "..." found
first = InStr(brokenstr, strtarget)
second = InStr(first + 3, brokenstr, strtarget)
findandcopy = Mid(purpose, first +3, second - first -3) 'referred to https://stackoverflow.com/questions/2543225/how-to-get-a-particular-part-of-a-string#_=_
first = second 'so that loop can find next "..."
Loop
End Function
can anyone please advise? thank you for your help :)
Try this code:
Option Explicit
Sub copyd()
Dim arr As Variant
' get splitted text into horizontal array arr()
arr = Split("...covid...is...very...scary", "...")
If UBound(arr) > 0 Then ' if there is something in the array, display it on the sheet
' put onto sheet values from transposed array arr()
ThisWorkbook.Worksheets(1).Range("A1"). _
Resize(UBound(arr) + 1, 1).Value = _
WorksheetFunction.Transpose(arr)
End If
End Sub
Ahh, why not just split the string by "..."?
Like:
Function findandcopy(brokenstr As String, targetStr as string)
dim substr()
if instr(1, brokenstr, targetStr, vbTextCompare) > 0 then
'brokenstr has at least one instance of targetStr in it
brokenstr2 = split(brokenstr,targetStr)
if brokenstr2(0) = "" then
redim substr(ubound(brokenstr2)-1)
iStart = 1
else
redim substr(ubound(brokenstr2))
iStart = 0
end if
for i = iStart to ubound(brokenstr2)
substr(i-iStart) = brokenstr2(i)
next i
else
'No instances of targetStr in brokenstr
redim substr(0)
substr(0) = brokenstr
end if
findandcopy = substr
end function
Which will return an array of strings which are the bits between targetStr. Then you can do with it as you please within the parent sub.
If you start doing comparisons with the results and find issues - you can remove whitespace by modifying above as:
substr(i) = trim(brokenstr2(i))
and your calling code:
Sub main()
Dim covid as string
Dim remove as string
covid = "...covid...is....very...scary"
'covid = "really...covid...is...very...scary" 'For testing
remove = "..."
rtn = findandcopy(covid, remove)
end sub

Index and match between cells that has lines breaks

Cell A2 is the data given which has different lines breaks. I separated the data before and after "-" as shown in B2 & C3. Then I sorted the data of C2 from lowest to largest in D2. The desire result is cell E2. I would like to have a user define function to get index B2 by matching D2 from C2. Please note A2 has four values in 4 lines breaks in one cell not in every cell there is a value, please find attached.
The UDF below will do what you want. Call it from the worksheet like =InexMatch(A2). Make sure that the cell you place it in has its WrapText property set to True.
Function InexMatch(Cell As Range) As String
' 003
Dim Arr As Variant
Dim Sp() As String
Dim Tmp As String
Dim Done As Boolean
Dim i As Integer
Arr = Split(Cell.Value, Chr(10))
For i = 0 To UBound(Arr)
Sp = Split(Arr(i), "-")
Arr(i) = Sp(1) & "-" & Sp(0)
Next i
Do
Done = True
For i = 0 To UBound(Arr) - 1
If Val(Arr(i + 1)) < Val(Arr(i)) Then
Tmp = Arr(i)
Arr(i) = Arr(i + 1)
Arr(i + 1) = Tmp
Done = False
End If
Next i
Loop While Not Done
On Error Resume Next
ReDim Sp(UBound(Arr))
For i = 0 To UBound(Arr)
Sp(i) = Split(Arr(i), "-")(1)
Next i
InexMatch = Join(Sp, Chr(10))
End Function
The function will return a null string if the referenced cell is blank. It can deal with cells that have fewer than 4 lines. It will fail if the CR isn't ANSII Chr(10) or the dash isn't a ANSII Chr(45) - a minus sign. It has no provision for incomplete lines within cells, meaning lines which don't have characters on both sides of a dash.
In order to restore the original format in the sorted string please delete all the lines below the end of the Do Loop in the code above, starting with On Error Resume Next, and replace them with the following.
For i = 0 To UBound(Arr)
Sp = Split(Arr(i), "-")
Arr(i) = Sp(1) & "-" & Sp(0)
Next i
InexMatchV2 = Join(Arr, Chr(10))

How to target and remove multiple sections out of a cell

Using VBA or A Standard formula, I need to edit the following from cells.
I need to remove everything up to and including "Path:",
Then I need it to find | and start over until it reaches the end of the Cell
Example:
Category Name: Ladies, Category Path: Ladies|Category Name: Sale, Category Path: Sale|Category Name: New, Category Path: New|
Goal:
Ladies|Sale|New
It can include NO "|" or it can include up to 20 "|"
Edit: Realized I needed to show my work AFTER the tour. :)
I have spent a day or two on this and so far this is only I can come up with...
Dim s As String
s = Range("Z7").Value
Dim indexOfPath As Integer
Dim indexOfPipe As Integer
Dim indexOfCat As Integer
indexOfPath = InStr(1, s, "Path:")
indexOfPipe = InStr(1, s, "|")
Dim finalString As String
Dim pipeString As String
finalString = Right(s, Len(s) - indexOfPath - 5)
indexOfCat = InStr(1, finalString, "Path:")
pipeString = Right(finalString, Len(finalString) - indexOfCat - 5)
Range("A47").Value = finalString
Range("A48").Value = pipeString
How ever I have got to the point where I am not confusing myself...
Split the cell value on "|", then split each value in the resulting array on "Path:" and take the second element from the result of that.
Like this:
Sub Tester()
Dim s As String, arr, v, arr2
s = "Category Name: Ladies, Category Path: Ladies|Category Name:" & _
" Sale, Category Path: Sale|Category Name: New, Category Path: New|"
arr = Split(s, "|")
For Each v In arr
v = Trim(v)
If Len(v) > 0 Then
arr2 = Split(v, "Path:")
If UBound(arr2) > 0 Then Debug.Print arr2(1)
End If
Next v
End Sub
Try this Function:
Function splitonbar(rng As Range) As String
Dim tempArr() As String
Dim temp As String
Dim i As Integer
tempArr = Split(rng.Value, "|")
For i = LBound(tempArr) To UBound(tempArr)
If Len(tempArr(i)) > 0 Then
temp = temp & "|" & Trim(Mid(tempArr(i), InStr(tempArr(i), "Path:") + 5))
End If
Next i
splitonbar = Mid(temp, 2)
End Function
It can be used as Formula on the sheet, or be called from another sub. To use as a UDF put in a module in the workbook then simply call it with a formula:
=splitonbar(Z7)
Or you can call it with a sub like this:
Sub splitstring()
Dim t as string
t = splitonbar(range("Z7"))
debug.print t
end sub
To directly fit your needs:
Public Function test(ByVal arg As Variant) As String
Dim i As Long
arg = Split(arg, "Category Name: ")
For i = 1 To UBound(arg)
arg(i) = Left(arg(i), InStr(arg(i), ",") - 1)
Next
test = Mid(Join(arg, "|"), 2)
End Function
The Split itself cuts everything in front of the keyword. The Left cuts everything after the comma (including the comma itself)
If you still have questons left, just ask :)

get array position in string array when value = 1

I have a column that contain a binary string as this
11110010
i need to return position in another cell if found 1
like this
12347
i try to use index and match function but it's doesn't work problaly
Put this in a module on your worksheet:
Function GetInstances(MyString As String, FindChar As String)
Dim X As Long, MyResult As String
MyResult = ""
For X = 1 To Len(MyString)
If Mid(MyString, X, 1) = FindChar Then MyResult = MyResult & X
Next
GetInstances = MyResult
End Function
In Cell A1: 11110010
In Cell B1 I used the new formula like so: =GetInstances(A1,1)
The result it gave me was 12347
A1 contains the string to evaluate and the 1 in there is the number to find.
InStr method can shown the position of a character but index start from 1.
So, in 1234, if we find 1, it will return 1. One thing is that, it will shown for the first matches.
I tested about it as:
MsgBox InStr("1234", "1")
I give me 1 in message box. But, when I tried as follow:
MsgBox InStr("12341", "1")
It don't give two message box for position 1 and 5. It just show message box for position 1. If it is OK, try with this.
An alternative function that uses array for speed below:
Function StrOut(strIn As String)
Dim buff() As String
Dim lngCnt As Long
buff = Split(StrConv(strIn, vbUnicode), Chr$(0))
For lngCnt = 0 To UBound(buff) - 1
StrOut = StrOut & (lngCnt + 1) * buff(lngCnt)
Next
StrOut = Replace(StrOut, "0", vbNullString)
End Function
test code
Sub Test()
MsgBox StrOut("11110010")
End Sub
Tinkered with a formula approach that I intended to try with Evaluate, got as far as
=IF(MID(A1,ROW(INDIRECT("1:" & LEN(A1))),1)="1",ROW(INDIRECT("1:" & LEN(A1))),"X")
which gives
={1;2;3;4;"X";"X";7;"X"}
but not progressed to completion yet.

Excel formula: For each instance of value in column, get value of another column in same row

I am looking to solve the following problem in Excel:
ID Key Value
1 10 20
2 5 30
3 10 20
4 10 20
If key == 10 and Value == 20, get the ID.
So, I need this to produce the following list: "1,3,4"
Essentially, I'm looking to see if one value is in a given range, and another value is in another range, give me the corresponding value (same row) in another range.
I cannot assume that the ID column will always be the left most column.
You can use the attached User Defined Function for that purpose. Call it from your worksheet as follows:
=concatPlusIfs(A1:A4,",",1,10,2,20)
where
A1:A4 is the ID list
"," is the separator
1 is the offset between your id column and your key column (-1 for 1 column to the left)
10 is the criteria for your Key
2 is the offset between your id column and your Value column
20 is the criteria for your Value
Public Function concatPlusIfs(rng As Range, sep As String, lgCritOffset1 As Long, varCrit1 As Variant, lgCritOffset2 As Long, varCrit2 As Variant, Optional noDup As Boolean = False, Optional skipEmpty As Boolean = False) As String
Dim cl As Range, strTemp As String
If noDup Then 'remove duplicates, use collection to avoid them
Dim newCol As New Collection
On Error Resume Next
For Each cl In rng.Cells
If skipEmpty = False Or Len(Trim(cl.Text)) > 0 Then
If cl.Offset(, lgCritOffset1) = varCrit1 And cl.Offset(, lgCritOffset2) = varCrit2 Then newCol.Add cl.Text, cl.Text
End If
Next
For i = 0 To newCol.Count
strTemp = strTemp & newCol(i) & sep
Next
Else
For Each cl In rng.Cells
If skipEmpty = False Or Len(Trim(cl.Text)) > 0 Then
If cl.Offset(, lgCritOffset1) = varCrit1 And cl.Offset(, lgCritOffset2) = varCrit2 Then strTemp = strTemp & cl.Text & sep
End If
Next
End If
concatPlusIfs = Left(strTemp, Len(strTemp) - Len(sep))
End Function
I would say this is the most basic function of excel but since your assuming the artifical limitation that you can't decide how your columns are going to be ordered - then it requires you to use something like HLOOKUP (assuming you can at least determine your headers):
=IF(AND(HLOOKUP("Key",$A$1:$C$5,ROW(),FALSE)=10,HLOOKUP("VALUE",$A$1:$C$5,ROW(),FALSE)=20),HLOOKUP("ID",$A$1:$C$5,ROW(),FALSE),"")
Good Luck.
EDIT/ADDITION:
Use the multicat function from: http://www.mcgimpsey.com/excel/udfs/multicat.html
e.g.
Sort your table to get rid of the spaces and then:
=multicat(C2:C5,",")
Or if sorting is too much work for you - you can use this modified version of the mcgimpsey function to get rid of blank cells:
Public Function MultiCat( _
ByRef rRng As Excel.Range, _
Optional ByVal sDelim As String = "") _
As String
Dim rCell As Range
For Each rCell In rRng
If rCell.Value <> "" Then
MultiCat = MultiCat & sDelim & rCell.Text
End If
Next rCell
MultiCat = Mid(MultiCat, Len(sDelim) + 1)
End Function

Resources