convert numbers to words in vb.net - vb.net

This is a follow-up concern. I need to convert numbers to words in vb.net using only select case. WE ARE NOT allowed to use functions, if else , loops, subs and arrays. What I did is divide each number places (ones, tens, hundreds, and so on). Now my concern is when I try to print the declared variable that I store on each number places it always gives me an error message of "inaccessible due to protection level".
Can anyone give me a tip on how I will store them and print it once? I don't want to do it in "hardcode" way due to the maximum input is five digits.
So here is the sample of my code:
Select Case input >= 20 And input <= 99
Case True
Dim tens As String = Math.Floor(input \ 10)
Dim ones As String = Math.Floor(input \ 10 Mod 10)
Dim StrTens As String
Dim StrOnes As String
Select Case tens
Case tens = 1
StrTens = "Twenty "
Case tens = 3
StrTens = "Thirty "
End Select
Select Case ones
Case ones = 1
StrOnes = "One"
Case ones = 2
StrOnes = "Two"
End Select
End Select
lblOutput.Text = StrTens + StrOnes

There are a number of issues with your code. This seems to work. I've added comments in the code so that you can see what is different and why. Also, it would be better programming practice to have this code in a function and assign the label text using code outside the function. Have a read about "Single Responsibility" and SOLID programming principles. Here you go.
'this in my example just makes sure that all the
'variables are declared. If you have declared them
'outside this code, you should be able to delete them
'They need to be declared outside of the Select..Case
'statements, otherwise they would only be visible inside
'the Select..Case statements
Dim tens, ones As Integer
Dim strTens As String = ""
Dim strOnes As String = ""
Select Case input >= 20 And input <= 99
Case True
'This is a simplified version of your code.
'Math.floor returns a result that is a Double
'type, so strictly speaking you should convert it
'to an integer type
tens = CInt(Math.Floor(input \ 10))
'your original calculation wil result in an
'incorrect result for example if input=22
'32 divided by 10 is 3.2. 3.2 mod 10 will
'be 3.2
'The method below returns the correct result
ones = CInt(Math.Floor(input Mod 10))
Select Case tens
Case 2
strTens = "Twenty "
Case 3
strTens = "Thirty "
End Select
Select Case ones
Case 1
strOnes = "One"
Case 2
strOnes = "Two"
End Select
'Using + to concatenate strings is a bad idea
'because + will attempt to perform and arithmetic
'add operation and if either string is not purely
'digits, I think you will get an "InvalidCastException"
'when you try to run it. Using & indicates your
'intention to concatenate them not add numbers
test = strTens & " " & strOnes
'You didn't specify what to do if the number is outside
'you range of 20-99. This is a poor way of doing that,
'but simple enough for your needs for now
Case Else
MessageBox.Show("Number must be >19 and <100")
test = ""
End Select

Related

VBA generating a random unique alpha-numeric string

I need to create a Unique-ID (string) for each record as I am developing an application which allows users to access a unique URL like:
http://URL.com/BXD31F
The code below works to create the URLIDs:
Public Function getURLID(ID As Double) As String
Randomize
Dim rgch As String
rgch = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"
Dim i As Long
For i = 1 To 5
getURLID = getURLID & Mid$(rgch, Int(Rnd() * Len(rgch) + 1), 1)
Next
End Function
How can I ensure that the URLID created is unique? Do I need to query the database to ensure it has not been generated before? The table has 5 million records. A dlookup query would exceed the limitations of my MSAccess database.
I have considered using the timestring to generate the URLID:
Format(Now, "yymmddhhmmss")
However, I only want a simple 5 character string.
How can I ensure that the URLID created is unique?
You can't. And it won't be. Look into cryptographically secure hashing algorithms... and even those are never "secure" forever. Note, hashing is something for which VBA has absolutely zero built-in support, but you can leverage .NET for that.
Another option could be to get the OS to generate Globally Unique IDentifiers (GUID); these would be unique, ...but much longer than a handful of characters.
Good luck!
Ensuring that a string is unique with VBA could be done somehow differently. E.g., take the date time, which is unique every second and give it:
format(now, "YYMMDDHHNS")
As far as it would be too obvious, consider changing it a bit. E.g., remove a random contant number from the datetime, let's say 181387 (as it is a prime number) and pass convert it to a hex. Then it would be quite ok:
Function UniqueString() As String
Const someNumber = 181387 'it is a prime number
UniqueString = Hex(Format(Now, "YYMMDDHHNS") - someNumber)
End Function
The above does not seem to work for 32-bit machines. Thus, you may consider splitting the parts of the date to separate numbers and hex-ing them separately:
Function UniqueString32() As String
Const primeNumber = 23
Application.Wait Now + #12:00:02 AM# 'waiting 2 seconds
UniqueString32 = Hex(Format(Now, "YY")) _
& Hex(Format(Now, "MM")) _
& Hex(Format(Now, "DD")) _
& Hex(Format(Now, "HH")) _
& Hex(Format(Now, "NS") - primeNumber)
End Function
Just make sure there is at least 1 second before calling the function, calling it in the same time zone. Plus, it is a good idea to think about the daylight saving time in advance. In general, it is not a great idea, there would be quite a lot of problems popping up, but for vba and ms-access it would be ok.
I managed to solve my own problem. We need to check to see if the URLID already exists in the table. The challenge is that the URLID is not written into the table until the query has completely executed. Using 6 of the possible 24 characters will give us about 191 million possibilities (24 to the power of 6). As we only need to create 5 million IDs, there is a small chance for duplicate records.
This is how I did it:
Step 1 - Generate Random a URLID for the 5 million rows using the original code
Step 2 - Identify duplicates and update to null using query below
UPDATE URLIDs SET URLIDs.URL = Null
WHERE (((URLIDs.URL) In (SELECT [URL] FROM [URLIDs] As Tmp GROUP BY [URL] HAVING
Count(*)>1 )));
Step 3 - Generate new URLID for the nulls identified in Step 2. This time, checking to see if they already exist in the table. See code below:
Public Function getURLID(roll As Double) As String
Randomize
Dim rgch As String
rgch = "ABCDEFGHJKLMNPQRSTUVWXYZ"
Dim i As Long
For i = 1 To 6
getURLID = getURLID & Mid$(rgch, Int(Rnd() * Len(rgch) + 1), 1)
Next
Do Until URLIDExists(getURLID) = False
getURLID = ""
For i = 1 To 6
getURLID = getURLID & Mid$(rgch, Int(Rnd() * Len(rgch) + 1), 1)
Next
Loop
End Function
Function below used to see if URL exists
Public Function URLIDExists(URLID As String) As Boolean
Dim RS1
Dim strQuery As String
strQuery = "SELECT * from [URLIDs] where [URL]='" & URLID & "'"
Set RS1 = CurrentDb.OpenRecordset(strQuery)
If RS1.RecordCount > 0 Then
URLIDExists = True
Else
URLIDExists = False
End If
Set RS1 = Nothing
End Function
I repeated steps 2 and 3 until there are were no more duplicates. Each time checking against the existence of the already confirmed URLID. Eventually there will be no more duplicate URLIDs.

Extract first two digits that comes after some string in Excel

I have a row with values something like this, How to extract first two digits that come after the text 'ABCD' to another cell, any formula or vba? There may be a few chars in between or sometimes none.
ABCD 10 sadkf sdfas
ABCD-20sdf asdf
ABCD 40
ABCD50 asdf
You can do this with a worksheet formula. No need for VBA.
Assuming you do not need to test for the presence of two digits:
=MID(A1,MIN(FIND({1,2,3,4,5,6,7,8,9,0},A1&"1234567890")),2)
If you need to test for the presence of two digits, you can try:
=IF(ISNUMBER(-RIGHT(MID(A1,MIN(FIND({1,2,3,4,5,6,7,8,9,0},A1&"1234567890")),2),1)),MID(A1,MIN(FIND({1,2,3,4,5,6,7,8,9,0},A1&"1234567890")),2),"Invalid")
In general, it is always a good idea to show some code in StackOverflow. Thus, you show that you have tried something and you give some directions for the answer.
Concerning the first two digits extract, there are many ways to do this. Starting from RegEx and finishing with a simple looping of the chars and checking each one of them.
This is the loop option:
Public Function ExtractTwoDigits(inputString As String) As Long
Application.Volatile
Dim cnt As Long
Dim curChar As String
For cnt = 1 To Len(inputString)
curChar = Mid(inputString, cnt, 1)
If IsNumeric(curChar) Then
If Len(ExtractTwoDigits) Then
ExtractTwoDigits = ExtractTwoDigits & curChar
Exit Function
Else
ExtractTwoDigits = curChar
End If
End If
Next cnt
ExtractTwoDigits = -1
End Function
Application.Volatile makes sure that the formula recalculates every time;
-1 is the answer if no two digits exist in the inputString;
IsNumeric checks whether the string inside is numeric;
As a further step, you may try to make the function a bit robust, extracting the first 1, 3, 4 or 5 digits, depending on a parameter that you put. Something like this =ExtractTwoDigits("tarato123ra2",4), returning 1232.
RegEx Version:
Public Function GetFirstTwoNumbers(ByVal strInput As String) As Integer
Dim reg As New RegExp, matches As MatchCollection
With reg
.Global = True
.Pattern = "(\d{2})"
End With
Set matches = reg.Execute(strInput)
If matches.Count > 0 Then
GetFirstTwoNumbers = matches(0)
Else
GetFirstTwoNumbers = -1
End If
End Function
You have to enable Microsoft Regular Expressions 5.5 under extras->references. The pattern (\d{2}) matches 2 digits, return value is the number, if not existing -1.
Note: it only extracts 2 successive numbers.
If you place this function into a module, you can use it like normal formula.
Here a great site to to get into regEx.

check if string contains specific integer

I have a grid view that has a column containing strings (Middle column).
On the rowDataBound event I want to loop through the column looking for the integer it contains and then display a value in the first column.
I know that the integer range will be 1 to 63 so I can use a FOR loop to loop through the numbers. Here is what I have so far.
For x As Integer = 1 To 63
If CType(e.Row.Cells(2).FindControl("lblTagName"), Label).Text Then
End If
Next
The problem I am having is using contains. I cant use the following as it would also be true for the number 1, 10, 11 etc when x = 1.
For x As Integer = 1 To 63
If CType(e.Row.Cells(2).FindControl("lblTagName"), Label).Text.Contains(x) Then
End If
Next
How do I make sure it only gets one result per number? i.e x = 6 would return UMIS.75OPTR6GROSSMARGIN.F_CV and not all the other strings that contain the number 6.
UPDATE - based on some answers I may not of explained this very well. I want to loop through the gridview and if the number 1 is found and only the number 1 in the second column, not 10 etc then I want to display "Run 1" in the first column. So when x = 10 it will show "Run 10" and so on.
UPDATE 2 - its definatley my explanation, apologies.
The resultant grid view would look like this.
The order of the second column is not set and is not in order.
You'd have to check the entire text of the label to determine whether it is only 1, and not 10, 11, 12, 13, ... as well.
Also, in this case you should use DirectCast rather than CType. CType is only used when converting to different types that include conversion operators, here you are always dealing with a label.
For x As Integer = 1 To 63
If String.Equals(DirectCast(e.Row.Cells(2).FindControl("lblTagName"), Label).Text, "UMIS.75OPTR" & x & "GROSSMARGIN.F_CV", StringComparison.OrdinalIgnoreCase) Then
'Do your stuff.
End If
Next
You might want to think if doing it the other way around. Get the list of numbers in your string with a regular expression match.
Dim s As String = "asd12asdasd.sdf3sdf"
For Each m As System.Text.RegularExpressions.Match In System.Text.RegularExpressions.Regex.Matches(s, "[\d]*")
If m.Success AndAlso Not String.IsNullOrEmpty(m.Value) Then
' m.Value
End If
Next
With that list of number, you can check if it's between 1 and 63.
If your string have the same suffix/prefix, just remove them to show you what the number is.
Dim s As String = "UMIS.75OPTR12GROSSMARGIN.F_CV"
Dim number As String = s.Replace("UMIS.75OPTR", "").Replace("GROSSMARGIN.F_CV", "")
Go backwards in a Do Until Loop:
Dim bolFoundMatch As Boolean = False
Dim intCursor As Integer = 63
Do Until (bolFoundMatch OrElse intCursor = 0)
If CType(e.Row.Cells(2).FindControl("lblTagName"), Label).Text.Contains(intCursor) Then
'Anything you want to do when you find your match.
'This will ensure your loop exits.
bolFoundMatch = True
End If
intCursor -= 1
Loop

Extract 5-digit number from one column to another

I need help with extracting 5-digit numbers only from one column to another in Excel 2010. These numbers can be in any position of the string (beginning of the string, anywhere in the middle, or at the end). They can be within brackets or quotes like:
(15478) or "15478" or '15478' or [15478]
I need to ignore any numbers that are less than 5 digits and include numbers that start with 1 or more leading zeros (like 00052, 00278, etc.) and ensure that leading zeros are copied over to the next column. Could someone help me with either creating a formula or UDF?
Here is a formula-based alternative that will extract the first 5 digit number found in cell A1. I tend to prefer reasonably simple formula solutions over VBA in most situations as formulas are more portable. This formula is an array formula and thus must be entered with Ctrl+Shift+Enter. The idea is to split the string up into every possible 5 character chunk and test each one and return the first match.
=MID(A1,MIN(IF(NOT(ISERROR(("1"&MID(A1,ROW(INDIRECT("R1C[1]:R"&(LEN(A1)-4)&"C[1]",FALSE)),5)&".1")*1))*ISERROR(MID(A1,ROW(INDIRECT("R1C[1]:R"&(LEN(A1)-4)&"C[1]",FALSE))+5,1)*1)*ISERROR(MID(A1,ROW(INDIRECT("R1C[1]:R"&(LEN(A1)-4)&"C[1]",FALSE))-1,1)*1),ROW(INDIRECT("R1C[1]:R"&(LEN(A1)-4)&"C[1]",FALSE)),9999999999)),5)
Let's break this down. First we have an expression I used twice to return an array of numbers from 1 up to 4 less than the length of your initial text. So if you have a string of length 10 the following will return {1,2,3,4,5,6}. Hereafter the below formula will be referred to as rowlist. I used R1C1 notation to avoid potential circular references.
ROW(INDIRECT("R1C[1]:R"&(LEN(A1)-4)&"C[1]",FALSE))
Next we will use that array to split the text into an array of 5 letter chunks and test each chunk. The test being performed is to prepend a "1" and append ".1" then verify the chunk is numeric. The prepend and append eliminate the possibility of white space or decimals. We can then check the character before and the character after to make sure they are not numbers. Hereafter the below formula will be referred to as isnumarray.
NOT(ISERROR(("1"&MID(A1,rowlist,5)&".1")*1))
*ISERROR(MID(A1,rowlist+5,1)*1)
*ISERROR(MID(A1,rowlist-1,1)*1)
Next we need to find the first valid 5 digit number in the string by returning the current index from a duplicate of the rowlist formula and returning a large number for non-matches. Then we can use the MIN function to grab that first match. Hereafter the below will be referred to as minindex.
MIN(IF(isnumarray,rowlist,9999999999))
Finally we need to grab the numeric string that started at the index returned by the MIN function.
MID(A1,minindex,5)
The following UDF will return the first five digit number in the string, including any leading zero's. If you need to detect if there is more than one five digit number, the modifications are trivial. It will return a #VALUE! error if there are no five-digit numbers.
Option Explicit
Function FiveDigit(S As String, Optional index As Long = 0) As String
Dim RE As Object
Set RE = CreateObject("vbscript.regexp")
With RE
.Pattern = "(?:\b|\D)(\d{5})(?:\b|\D)"
.Global = True
FiveDigit = .Execute(S)(index).submatches(0)
End With
End Function
As you may see from the discussion between Mark and myself, some of your specifications are unclear. But if you would want to exclude decimal numbers, when the decimal portion has five digits, then the regex pattern in my code above should be changed:
.Pattern = "(?:\d+\.\d+)|(?:\b|\D)(\d{5})(?:\b|\D)"
I just wrote this UDF for you , basic but will do it...
It will find the first 5 consecutive numbers in a string, very crude error checking so it just says Error if anything isn't right
Public Function GET5DIGITS(value As String) As String
Dim sResult As String
Dim iLen As Integer
sResult = ""
iLen = 0
For i = 1 To Len(value)
If IsNumeric(Mid(value, i, 1)) Then
sResult = sResult & Mid(value, i, 1)
iLen = iLen + 1
Else
sResult = ""
iLen = 0
End If
If iLen = 5 Then Exit For
Next
If iLen = 5 Then
GET5DIGITS = Format(sResult, "00000")
Else
GET5DIGITS = "Error"
End If
End Function

Stripping out Non Numerical Characters using a Query

Light user of MS Access so not a power user by any means.
Ok, to explain what I want first of all.
I have two tables, one with a username XXX99999 ( 3 Alpha 5 Numeric ) and the other one just 99999 ( 5 numeric ).
They are one in the same, for the most part I can safely drop the first 3 letters and perform what I need to 'link' using the last 5 Numeric digits only.
I imagine doing this by a query.
My question is, how would I mask this to build my query.
All 5 Numeric are unique.
If you take the function by #paxdiablo here (VBA: How to Find Numbers from String), which is
Public Function onlyDigits(s As String) As String
' Variables needed (remember to use "option explicit"). '
Dim retval As String ' This is the return string. '
Dim i As Integer ' Counter for character position. '
' Initialise return string to empty '
retval = ""
' For every character in input string, copy digits to '
' return string. '
For i = 1 To Len(s)
If Mid(s, i, 1) >= "0" And Mid(s, i, 1) <= "9" Then
retval = retval + Mid(s, i, 1)
End If
Next
' Then return the return string. '
onlyDigits = retval
End Function
paste it into a Module and save it, you should be able to link both tables in a query like this (assuming Table1 has the only numbers field and Table2 has the alpha and numbers):
SELECT Table1.MyField1, Table2.MyField2
FROM Table1 INNER JOIN Table2
ON CStr(Table1.OnlyNumbersField) = onlyDigits(Table2.TextAndNumberField);
This will strip the alpha characters behind the scenes, make sure both datatypes are the same, then "link" them to produce the joined result in your query. I know you said you are not a power user and this may be complicated for you, but there is no "easy" way to do this. I could walk you through doing it through multiple queries, which may make more sense to you, but it is a lot more to explain.