Character Comparison QWERTY Key Proximity Typo (Access VBA Equivalent) - vba

This requires a solution & the code to migrate to Access from VB6 is below.
I have a function to compare characters that comes from VB6 and I am a novice user on VB6 and mostly work from VBA platform. I need to setup a class or a better way in MS Access to check character by character for typo mistakes without the use of UDT.
Mytypolist as an array refers to the following dataset:
QWA WESAQ ERDSW RTFDE TYGFR YUHGT UIJHY IOKJU OPLKI PLO AQWSZ SEDXZA DRFCXSE FTGVCDR GYHBVFT HUJNBGY JIKMNHU KOLMJI LPKO ZASX XZSDC CXDFV VCFGB BVGHN NBHJM MNJK
The above data is used to compare if a character was mistyped in a word.. ex. if I use A as in Auebec instead of what I mean to type Quebec, my cluster of interest is QWA; WESAQ; AQWSZ; or any other Q arrangement on a standard English Qwerty keyboard based on proximity. And this is not just for Q, but for entire set of alphabets, regardless of case, so c has its own cluster of possible typo matches etc..
In VB6 setup of UDT (user defined type):
'declare UDT type for typos
Public Type Mytypos
Rightrkey As String * 1
PossibleKey As String * 8
End Type
'declare arrays and variable for master list and typos
Public Masterlist() As String
Public Mytypolist(26) As Mytypos
Public Matchkey As Mytypos
the following function compares two words; and assign similarity by calculating currentpct score:
Public Function CompareCharacters(ByRef MasterWord As String, _
ByRef Checkword As String, ByRef CurrentPCT As Double, _
ByRef WordVal As Long) As Double
'define function variables
Dim ChrCount As Long
Dim ChrValue As Long
Dim loop1 As Long
Dim loop2 As Long
'define the letter values
If Len(MasterWord) > Len(Checkword) Then
ChrCount = Len(MasterWord) * 2
Else
ChrCount = Len(Checkword) * 2
End If
ChrValue = 1 / ChrCount
'say CURRENT PCT has a value of 10%
'check each letter for a match in current word position
For loop1 = 1 To Len(Checkword)
'check for typo errors (key proximity)
For loop2 = 0 To UBound(Mytypolist)
Matchkey = Mytypolist(loop2)
'if indexkey = letter in masterword
If Matchkey.Rightrkey = Mid(MasterWord, loop1, 1) Then
'does the letter in the checkword exist in the proximity keylist
If InStr(1, Matchkey.PossibleKey, Mid(Checkword, loop1, 1), vbTextCompare) > 0 Then
'value for letter found in proximity keylist
CurrentPCT = CurrentPCT + ChrValue
End If
Exit For
End If
Next loop2
Next loop1
CompareCharacters = CurrentPCT
End Function
IF you can post me a array/class solution that may not produce compiler issues (String UDT in VBA are a problem). Please check it out now!

It would probably best, since you have a 1 character to 8 character thing, to just have a full mapping. Something to replace this:
Public Type Mytypos
Rightrkey As String * 1
PossibleKey As String * 8
End Type
To:
PossibleKeys(255) As String * 8
That way, each character (from 0 to 255) would have the 8 character mapping. No UDT required!

Related

Detecting a certain number of consequential numbers in a cell (VBA)

I need help coming up with a function that detects a certain number of consequential numbers in a cell or range of cells.
I have a bunch of data in the form of descriptions with a sequence of an 8 digit number hidden between text. I need to be able to detect this 8 digit number in a cell and extract it to another cell.
The reason I can't just extract all the numbers in a string is because there are also other numbers in the same cell, so I need to be able to detect an 8 digit number specifically.
Thanks in advance!
This will give you the first 8-digits that is contained in any String.
Sub Selvi()
MsgBox GetConsequentialDigits(Cells(1, 1), 8)
End Sub
I made this into a function so that you can also use it directly into Excel like this :
=GetConsequentialDigits(A1,8)
And here is the function :
Public Function GetConsequentialDigits(ByVal InCellString As String, ByVal DigitsNumber As Integer) As String
Dim Cpt As Integer, _
TpStr As String, _
TpRez As String
Cpt = 0
TpRez = vbNullString
For i = 1 To Len(InCellString) - 1
If Cpt < DigitsNumber Then
TpStr = Mid(InCellString, i, 1)
If IsNumeric(TpStr) Then
Cpt = Cpt + 1
Else
Cpt = 0
End If
Else
TpRez = Trim(Mid(InCellString, i - DigitsNumber, DigitsNumber))
Exit For
End If
Next i
GetConsequentialDigits = TpRez
End Function

Get Index of last active columns per Row in Excel using Open XML

How do i get the Index of the last active column in a row using Open Xml
i have this for row 1.
Dim activeCells As IEnumerable(Of DocumentFormat.OpenXml.Spreadsheet.Cell) = row.Descendants(Of DocumentFormat.OpenXml.Spreadsheet.Cell)().Where(Function(c) Not String.IsNullOrEmpty(c.InnerText))
Dim cell As DocumentFormat.OpenXml.Spreadsheet.Cell = activeCells.LastOrDefault()
Dim CellRef As String = cell.CellReference
This gives D1", but what i want is the index in this case "4". how do i go about this?
To convert the cell reference to a column index you could use something like the following (I've converted the code from the answer here which you've inspired me to write :)).
Private Shared Function GetColumnIndex(cellReference As String) As System.Nullable(Of Integer)
If String.IsNullOrEmpty(cellReference) Then
Return Nothing
End If
'remove digits
Dim columnReference As String = Regex.Replace(cellReference.ToUpper(), "[\d]", String.Empty)
Dim columnNumber As Integer = -1
Dim mulitplier As Integer = 1
'working from the end of the letters take the ASCII code less 64 (so A = 1, B =2...etc)
'then multiply that number by our multiplier (which starts at 1)
'multiply our multiplier by 26 as there are 26 letters
For Each c As Char In columnReference.ToCharArray().Reverse()
columnNumber += mulitplier * (CInt(c) - 64)
mulitplier = mulitplier * 26
Next
'the result is zero based so return columnnumber + 1 for a 1 based answer
'this will match Excel's COLUMN function
Return columnNumber + 1
End Function
Note: the VB might not be idiomatic as I used the Telerik Converter to convert it from C# to VB.

Read binary values (such 1000, 1001, 1010, 1011...) as an array of digits

I am in search of a solution for the following task:
General environment: I am working on a toolbox of VBA functions to process data in an Access database of educational offers. This includes various string operations and operations with database fields mostly via SQL.
In one of my functions, I have to deal with different states of four database fields in a row, their values being NULL or not NULL. As each field can be either NULL or not NULL, we have 16 possible situations:
0-0-0-0, 0-0-0-1, 0-0-1-0, 0-0-1-1 and so on,
which is obviously quite the same as:
0000, 0001, 0010, 0011, 0100, ..., 1111, i.e. binary representation of decimal 0-15.
In order to not hard code every single case, I want make use of the binary representations of numbers 0-15 by counting up from 0 to 15.
In pseudocode:
i = 0
For i = 0 To 15
arrX(): 0000
StateOfField01 = arrX(0) [which is 0, in this case]
StateofField02 = arrX(1) [dito]
StateOfField03 = arrX(2) [dito]
StateofField04 = arrX(3) [dito]
Do something with the fields, depending on their state
i = i + 1
Next i
So far I am happy with my idea, but there is one thing I have no idea how to solve:
How can I get from the binary representations of i an array containing four digits each?
Here is a function which will convert a Decimal number to Binary String.
Public Function GetBinary(Number As Long) As String
'********************
'Code Courtesy of
' Paul Eugin
'********************
Dim resultStr As String, nLen As Integer
Do While Number > 0
resultStr = resultStr & (Number Mod 2)
Number = Int(Number / 2)
Loop
GetBinary = Format(StrReverse(resultStr), "0000")
End Function
The function will take in a "Number" as argument for which you want to find the Binary equivalent. The Format function at the end of the code will make sure the return would be a minimum of 4 literal representation. So if you pass the example,
? GetBinary(5)
0101
Based on PaulFrancis' input, I found the following solution answering my proper needs.
Important additions are 1) "ByVal" for the argument in the call so that the function can be called from inside another procedure whose for-next counter should be preserved; 2) a test for "Number" being 0 which was not covered previously.
Public Function GetBinary(ByVal Number As Integer) As String
Dim resultStr As String
If Number = 0 Then
resultStr = "0"
Else
Do While Number > 0
resultStr = resultStr & (Number Mod 2)
Number = Int(Number / 2)
Loop
End If
GetBinary = Format(StrReverse(resultStr), "0000")
End Function
'-----------------------------------
Public Sub TestBinaryToString()
'Purpose: Testing the GetBinary() function:
'output will be displayed in the Immediate Window
Dim i As Integer
Dim strBin As String
For i = 0 To 15
strBin = GetBinary(i)
Debug.Print i; " --> "; strBin
Next i
End Sub

Excel formula calculating once then deleting

I have an excel formula:
=SplitKey(GetSysCd(INDEX([ReportValue],MATCH("mtr_make_model",[FieldName],0)),INDEX([ListName],MATCH("mtr_make_model",[FieldName],0))), 0)
which is running a few subroutines in VBA, but mainly matching values and inserting those values into a cell. When it finds a value for "mtr_make_model" it runs and matches the values inside a sys codes table. The issue I am having is that it is calculating once and then it removes the formula and now has solely the value... In the event that I go to the mtr_make_model field and change the value, the formula does not recalculate. Has anyone heard of this happening? Is this due to something in the VBA code? How do I make that formula stay put and if certain values change, the formula recalculates?
Thanks in advance.
Here are the two functions:
Public Function GetSysCd(ByVal name As String, sysCdTableName As String) As String
Dim r As Integer
Dim sysCdTable As Range
Dim nameList As Variant
Dim sysCd As String
On Error GoTo GetSysCd_Error
Set sysCdTable = Worksheets("sys_cd").Range(sysCdTableName)
nameList = WorksheetFunction.Index(sysCdTable, 0, 2)
r = WorksheetFunction.Match(name, nameList, 0)
sysCd = WorksheetFunction.Index(sysCdTable, r, 1)
GetOutOfHere:
On Error GoTo 0
GetSysCd = sysCd
Exit Function
GetSysCd_Error:
sysCd = ""
GoTo GetOutOfHere
End Function
Public Function SplitKey(s As String, v As Integer)
Dim aString As Variant
Dim r As Integer
If Len(s) > 2 Then
aString = Split(s, "_")
If v = 0 Or v = 1 Then
SplitKey = aString(v)
Else
SplitKey = aString(0)
End If
Else
SplitKey = ""
End If
End Function
I don't think the functions are relevant at this point, but rather just a matter of the function not recalculating when a variable in the formula changes...
The problem could be that Excel only recalculates functions when one of their arguments changes, and your GetSysCd function is referring to a range that is not in its argument list
Set sysCdTable = Worksheets("sys_cd").Range(sysCdTableName)
where sysCdTableName is just a string rather than a reference.
You can make the functions recalculate in real time by adding Application.Volatile True to the top of each function.

How do I split up a year range into individual years in a given field?

I was working in Access to make a query of a few tables, and realized that a column of a table does not meet a specific requirement.
I have a field that consists of thousands of records, and it contains "years" in the following format (an example) : 1915-1918.
What I want to accomplish is to make that value individual in the records. So
the end result would be : 1915,1916,1917,1918.
Basically, I want to convert 1915-1918 to 1915,1916,1917,1918.
I thought a simple concatenation would suffice, but could not wrap my head around how to make it so that it can do it for all thousands of records. I did some research and reached the conclusion that a user defined function might be the way to go. How would I go about this?
When your field value consists of 4 digits followed by a dash followed by 4 more digits, this function returns a comma-separated list of years.
In any other cases (Null, a single year such as "1915" instead of a year range, or anything else), the function returns the starting value.
Public Function YearList(ByVal pInput As Variant) As Variant
Dim astrPieces() As String
Dim i As Long
Dim lngFirst As Long
Dim lngLast As Long
Dim varReturn As Variant
If pInput Like "####-####" Then
astrPieces = Split(pInput, "-")
lngFirst = CLng(astrPieces(0))
lngLast = CLng(astrPieces(1))
For i = lngFirst To lngLast
varReturn = varReturn & "," & CStr(i)
Next
If Len(varReturn) > 0 Then
varReturn = Mid(varReturn, 2)
End If
Else
varReturn = pInput
End If
YearList = varReturn
End Function
However, this approach assumes the start year in each range will be less than the end year. In other words, you would need to invest more effort to make YearList("1915-1912") return a list of years instead of an empty string.
If that function returns what you want, you could use it in a SELECT query.
SELECT years_field, YearList(years_field)
FROM YourTable;
Or if you want to replace the stored values in your years field, you can use the function in an UPDATE query.
UPDATE YourTable
SET years_field = YearList(years_field);
You can use the Split function to return an array from the "years" field that contains the upper and lower year. Then loop from the lower year to the upper year and build the concatenated string. For example:
Public Function SplitYears(Years As String) As String
Dim v As Variant
Dim i As Long
Dim s As String
v = Split(Years, "-", 2)
If UBound(v) = 1 Then
For i = v(0) To v(1)
s = s & "," & CStr(i)
Next i
s = Right(s, Len(s) - 1)
Else
s = v(0)
End If
SplitYears = s
End Function
In Excel, make a sequential reference table of Years, for the range of years that you expect to encounter.
Next, use left and right functions to get the start and end of the range.
Create an update query and update the target field with a concatenation of target field to itself and then also the reference year values between that also fit between the start and end of the range.
Or I guess you could make a user function.
Add the code below to a module in Visual Basic
Public Function CommaDates(Start_End) As String
Dim strt As String
Dim endd As String
Dim x As Long
strt = Left(Start_End, 4)
endd = Right(Start_End, 4)
CommaDates = strt
For x = strt + 1 To endd
CommaDates = CommaDates & "," & x
Next x
End Function
Call this in a query like NEW_DATE: CommaDates([OLD_DATE_FIELD_NAME])