Check an array for non-numeric characters - excel

I am trying to create a function that will check an array for non-numeric characters. I'm sure there is a way with existing functions, but I was hoping to make a clean function.
This is what I have:
Public Function ContainsAllNumbers(Text() As Variant) As Boolean
For Each element In Text
If Not IsNumeric(element) Then
ContainsAllNumbers = False
End If
Next
End Function
The arguments I want it to take would be something like 11013688 or K03778 or 9005110-4400. I need to know if these strings contain something that is not a number.

Here is a (debugged) function which takes a string input and returns True if and only if all of the characters in the string are digits. This is (perhaps) what you are trying to do:
Function AllDigits(s As String) As Boolean
Dim i As Long
For i = 1 To Len(s)
If Not IsNumeric(Mid(s, i, 1)) Then
AllDigits = False
Exit Function
End If
Next i
AllDigits = True
End Function

I assume you want a function that takes an array of strings and checks them for nun-numeric values. Then your question would be, why your function always returns False.
The default value of a Boolean is false so you need to set ContainsAllNumbers = True at the beginning of the function.
I also recommend using Option Explicit so you don't forget to Dim your variables.

Convert the characters to their ascii code and check each of them. You can Look up an ascii table to find out the specific values for certain characters.
As an example I just copied this from one of my macros. It checks for non-alphanumeric values in the input string str:
invalidCharacter = false
For pos = 1 To Len(str)
Select Case Asc(Mid(str, pos, 1)) 'ascii value of character at str(pos)
Case 48 To 57, 65 To 90, 97 To 122 '0-9,a-z,A-Z
'nothing
Case Else
invalidCharacter = True
End Select
Next pos
If invalidCharacter Then
MsgBox "only numbers or letters may be used"
End If

Related

How do i get part of string after a special character?

I have a column where i pickup increasing numbering values, and their format is xx_yy
so the first is 1_0, second 1_1 and so forth, no we are at 23_31
I want to get the right side of the string, and i am already getting the left side correctly.
using
newActionId = Left(lastActionID, (Application.WorksheetFunction.Find("_", lastActionID, 1) - 1))
i wish to do the following, human writing below
nextSubid = entire stringvalue AFTER special character "_"
I tried just switching left to right, didnt go so well, do you have a suggestion?
You can use Split function to get the relevant text.
Syntax: Split(expression, [ delimiter, [ limit, [ compare ]]])
Option Explicit
Sub Sample()
Dim id As String
Dim beforeSplChr As String
Dim afterSplChr As String
id = "23_31"
beforeSplChr = Split(id, "_")(0)
afterSplChr = Split(id, "_")(1)
Debug.Print beforeSplChr
Debug.Print afterSplChr
End Sub
Another way
Debug.Print Left(id, (InStrRev(id, "_", -1) - 1)) '<~~ Left Part
Debug.Print Right(id, (InStrRev(id, "_", -1) - 1)) '<~~ Right Part
Even though Siddharth Rout has given what can probably be considered a better answer here, I felt that this was worth adding:
To get the second part of the string using your original method, you would want to use the Mid function in place of Left, rather than trying to use Right.
Mid(string, start, [ length ])
Returns length characters from string, starting at the start position
If length is omitted, then will return characters from the start position until the end of the string
newActionId = Mid(lastActionID, Application.WorksheetFunction.Find("_", lastActionID, 1) + 1)
Just for fun (Split is the way to go here), an alternative way using regular expressions:
Sub Test()
Dim str As String: str = "23_31"
With CreateObject("VBScript.RegExp")
.Global = True
.Pattern = "\d+"
Debug.Print .Execute(str)(0) 'Left Part
Debug.Print .Execute(str)(1) 'Right Part
End With
End Sub
Btw, as per my comment, your first value could also be achieved through:
Debug.Print Val(str)
Split function of string is very usefull for this type of query.
Like:
String s = "23_34";
String left = s.split("_")[0];
String right = s.split("_")[1];
Or you can also use combination of indexOf and substring method together.
String left = s.substring(0,s.indexOf('_')+1)
String right = s.substring(s.indexOf('_'));

VBA Greater Than Function Not Working

I have an issue where I am trying to compare a values that can be alphanumeric, only numeric, or only alphabetic.
The code originally worked fine for comparing anything within the same 100s group (IE 1-99 with alphabetic components). However when I included 100+ into it, it malfunctioned.
The current part of the code reads:
For j = 1 To thislength
If lennew < j Then
enteredval = Left("100A", lennew)
ElseIf lennew >= j Then
enteredval = Left("100A", j)
End If
If lenold < j Then
cellval = Left("67", lenold)
ElseIf lenold >= j Then
cellval = Left("67", j)
End If
'issue occurs here
If enteredval >= cellval Then
newrow = newrow+1
End If
Next j
The issue occurs in the last if statement.
When cycling through the 100 is greater than the 67 but still skips over. I tried to declare them both as strings (above this part of code) to see if that would help but it didn't.
What I am trying to accomplish is to sort through a bunch of rows and find where it should go. IE the 100A should go between 100 and 100B.
Sorry lennew=len("100A") and lennold=len("67"). And thislength=4or whatever is larger of the two lengths.
The problem is that you're trying to solve the comparison problem by attacking specific values, and that's going to be a problem to maintain. I'd make the problem more generic by creating a function that supplies takes two values returns -1 if the first operand is "before" the second, 0 if they are the same, and 1 if the first operand is "after" the second per your rules.
You could then restructure your code to eliminate the specific hardcoded prefix testing and then just call the comparison function directly, eg (and this is COMPLETELY untested, off-the-cuff, and my VBA is VERRRRRY stale :) but the idea is there: (it also assumes the existence of a simple string function called StripPrefix that just takes a string and strips off any leading digits, which I suspect you can spin up fairly readily yourself)
Function CompareCell(Cell1 as String, Cell2 as String) as Integer
Dim result as integer
Dim suffix1 as string
Dim suffix2 as string
if val(cell1)< val(cell2) Then
result = -1
else if val(cell1)>val(cell2) then
result = 1
else if val(cell1)=val(cell2) then
if len(cell1)=len(cell2) then
result =0
else
' write code to strip leading numeric prefixes
' You must supply StripPrefix, but it's pretty simple
' I just omitted it here for clarity
suffix1=StripPrefix(cell1) ' eg returns "ABC" for "1000ABC"
suffix2=StripPrefix(cell2)
if suffix1 < suffix2 then
result = -1
else if suffix1 > suffix2 then
result = 1
else
result = 0
end if
end if
return result
end function
A function like this then allows you to take any two cell references and compare them directly to make whatever decision you need:
if CompareCell(enteredval,newval)>=0 then
newrow=newrow+1
end if

Excel formula: If cell contains substring "this" AND does not contain substring "that"

I'm trying to write a function in which a column contains one substring and does not contain another substring.
In the example bellow I would like my function to return 1 if my row contains "some project" AND DOES NOT CONTAIN "overhead".
row| example strings | desired return value
0 |some project,other project | 1
1 |some project | 1
2 |overhead | 0
3 |some project, overhead | 0
4 |some project, other, boo | 1
I was trying to formulate it first with exact strings such as:
=IF(AND((E3="some project"),NOT(E3="overhead")),1,0)
But this only gives correct results for row 1 and 2 because it only does exact mach for the string instead of matching on the substring.
What you need is some kind of Substring function. I think FIND might work. Check out this page: https://exceljet.net/excel-functions/excel-find-function
Your function would be like:
=IF(AND(ISERROR(FIND("some project", E3))=FALSE,ISERROR(FIND("overhead",E3))),1,0)
EDIT: Above function works after testing
Tricky part here is that FIND returns the starting position of the string, and if it fails it returns #VALUE, which I believe you can catch with the ISERROR() function. This is in no way a beautiful solution. I would try to utilize the code behind and write this is VBA, as I am certain there is a proper substring function in VBA.
If you can insert a little VBA code, then you can use a custom function like so:
=StrContains(E3, "some project", "overhead")
And this will return True if the value in E3 contains both of those substrings. This function relies mainly on VBA's Instr function, which
Function code:
Public Function StrContains(ByRef cl As Excel.Range, ParamArray strings() As Variant)
'Function returns TRUE if the range contains ALL of the passed substring items
' uses ParamArray to allow varying number of substring items
' Ex:
' =StrContains(A1, "something", "else", "foo")
'
Dim val$
Dim s
Dim i As Integer
Dim ret As Boolean
Dim length As Integer
length = UBound(strings) + 1
val = cl.Value2
For Each s In strings
If InStr(1,val, s) <> 0 Then
i = i + 1
End If
Next
ret = (i = length)
StrContains = ret
End Function
You could modify this relatively easily to be case-insensitive, or to accept partial matches optionally, etc. Here is what it looks like extended for both of those concepts:
=StrContains(E3, False, True, "some project", "overhead")
Function Code:
Public Function StrContains(ByRef cl As Excel.Range, MatchCase As Boolean, MatchAll as Boolean, ParamArray strings() As Variant)
'MatchAll is matching switch, use True to require ALL matching items, or False to allow for fewer.
'MatchCase is the Case-sensitive switch, use False to ignore case.
' uses ParamArray to allow varying number of substring items
' Ex:
' =StrContains(A1, "something", "else", "foo")
'
Dim val$
Dim s
Dim i As Integer
Dim ret As Boolean
Dim length As Integer
length = UBound(strings) + 1
val = cl.Value2
If Not MatchCase Then
val = LCase(val)
For i = lBound(strings) to UBound(strings)
strings(i) = lcase(strings(i))
Next
Next
For Each s In strings
If InStr(val, s) <> 0 Then
i = i + 1
End If
Next
ret = (i = IIF(MatchAll, length, 1))
StrContains = ret
End Function
You can actually use the SEARCH function, like this:
=IF(ISNUMBER(SEARCH("some project",B2)),NOT(ISNUMBER(SEARCH("overhead",B2)))*1,0)
Considerations:
SEARCH function: returns either a number or error (that's why I used ISNUMBER, you could actually have used ISERROR too). This function is case insensitive. For case sensitiveness you might just use FIND, in the previous formula woud be =IF(ISNUMBER(FIND("some project",B2)),NOT(ISNUMBER(FIND("overhead",B2)))*1,0)
ISNUMBER function: returns either FALSE or TRUE. I converted the result to 1 or 0 simply by multiplying TRUE or FALSE by 1.
Hope helps

Excel if function

I've made this large excel sheet and at the time i didn't know i'd need to sort this table through categories.
I have in a column (J here ) the description of the line and the category joint. (example: "Shipment of tires for usin'ss")
The only way i was able to sort the table the way i wanted was to build a category column using this :
=IF(COUNTIF(J3;"*usi*");"Usins";IF(COUNTIF(J3;"*remis*");"Remise";IF(COUNTIF(J3;"*oe*");"Oenols";IF(COUNTIF(J3;"*KDB*");"KDB";IF(COUNTIF(J3;"*vis*");"cvis";IF(COUNTIF(J3;"*amc*");"AMC";0))))))
usi for instance is a segment of a category name, that i sometimes wrote as
usin'ss
usin
usin's
usins
'cause you know smart.
Anyway, how do i translate =If(If(If...))) into something readable in VBA like:
If...then
If... then
Example of "IF ... ELSE" in EVBA
IF condition_1 THEN
'Instructions inside First IF Block
ELSEIF condition_2 Then
'Instructions inside ELSEIF Block
...
ELSEIF condition_n Then
'Instructions inside nth ELSEIF Block
ELSE
'Instructions inside Else Block
END IF
Example of Case Switch in EVBA
Select Case score
Case Is >= 90
result = "A"
Case Is >= 80
result = "B"
Case Is >= 70
result = "C"
Case Else
result = "Fail"
End Select
Both cases work off a waterfall type logic where if the first condition is met, then it does not continue, but if condition 1 is not met then it checks the next, etc.
Example usage:
Function makeASelectAction(vI_Score As Integer) As String
Select Case vI_Score
Case Is >= 90
makeASelectAction = "A, fantastic!"
Case Is >= 80
makeASelectAction = "B, not to shabby."
Case Is >= 70
makeASelectAction = "C... least your average"
Case Else
makeASelectAction = "Fail, nuff said."
End Select
End Function
Function makeAnIfAction(vS_Destination As String, vS_WhatToSay As String, Optional ovR_WhereToStick As Range, Optional ovI_TheScore As Integer)
If vS_Destination = "popup" Then
MsgBox (vS_WhatToSay)
ElseIf vS_Destination = "cell" Then
ovR_WhereToStick.value = vS_WhatToSay
ElseIf vS_Destination = "select" Then
MsgBox makeASelectAction(ovI_TheScore)
End If
End Function
Sub PopMeUp()
Call makeAnIfAction("popup", "Heyo!")
End Sub
Sub PopMeIn()
Call makeAnIfAction("cell", "Heyo!", Range("A4"))
End Sub
Sub MakeADescision()
Call makeAnIfAction(vS_Destination:="select" _
, vS_WhatToSay:="Heyo!" _
, ovI_TheScore:=80 _
)
End Sub
It will show you how to send variables to functions and how to call said function, it will show you how use optional parameters, how a function and interact with another function or sub, how do write a value to a sheet or spit out a messagebox. The possabilities are endless. Let me know if you need anything else cleared up or coded out.
You seem to be using CountIf just to see if the contents of the cell matches a certain pattern and, if so, give a replacement string. In VBA you can use the Like operator for pattern matching. In any event -- here is a function I wrote which, when passed a string and a series of pattern/substitution strings, loops through the patterns until it finds a match and then returns the corresponding substitution. If no match is found, it returns an optional default value (the last argument supplied). If no default is supplied, it returns #N/A.
The code illustrates that sometimes complicated nested ifs can be replaced by a loop which iterates through the various cases. This is helpful when you don't know the number of cases before hand.
Function ReplacePattern(s As String, ParamArray patterns()) As Variant
Dim i As Long, n As Long
n = UBound(patterns)
If n Mod 2 = 0 Then n = n - 1
For i = 0 To n Step 2
If s Like patterns(i) Then
ReplacePattern = patterns(i + 1)
Exit Function
End If
Next i
If UBound(patterns) Mod 2 = 0 Then
ReplacePattern = patterns(n + 1)
Else
ReplacePattern = CVErr(xlErrNA)
End If
End Function
Your spreadsheet formula is equivalent to
=ReplacePattern(J3,"*usi*","Usins","*remis*","Remise","*oe*","Oenols","*KDB*","KDB","*vis*","cvis","*amc*","AMC",0)

Phone number format

I have a table field 'Phone number', the data in table is imported through a excel file link in database. The data type of the field is text since we are not sure how user enters his phone number(sometime with country code and sometime without country code).
I want to format the 'phone number' field once the table is updated everytime or data is imported into table. Format [countrycode]-[localcode]-[phone num].
I am not sure how to go about it, whether to create an update query or VBA code to update this field. Will appriciate any help in this regards.
It is generally recommended that in database fields, the phone number is maintained in numerical format only (meaning no parenthesis, dashes, or whatnot) because it provides more stability for the data and allows for better/easier future formatting when outputting. The most recommended method is to take the number given to you and strip it of all non-numeric characters and then store that value.
If you are working with an excel sheet containing this information before it is put into the database, then you can simply format the column that contains the phone numbers to convert everything into a single numerical value so 800-555-1212 or (888) 222-1515 would just become 8005551212 and 8882221515. This can be done using the existing cell formatting option built into Excel or if you want it done on the fly a simple VBA code that triggers when the field has a value would do the trick too.
EDIT #1 (super simple function)
Public Function numOnly(sToClean As String) As String
Const NUM_CHARS = "0123456789"
Dim lChar As Long
Dim sResult As String
For lChar = 1 To Len(sToClean)
If InStr(1, NUM_CHARS, Mid$(sToClean, lChar, 1)) > 0 Then
'Found a numeric character
sResult = sResult + Mid$(sToClean, lChar, 1)
End If
Next
'Return the result
numOnly = sResult
End Function
EDIT #2 (more feature advanced version)
Option Explicit
Public Function stripChars(CheckStr As String, Optional KillNums As Boolean = False, Optional AllowedChars As String, Optional NotAllowed As String)
' CheckStr = the string being evaluated
' KillNums [True/False] = remove numbers/remove non-numeric
' AllowedChars = what to allow even if you are removing non-numeric (aka KillNums=False) [ex. "$,."] or removing numbers (aka KillNums=True) [ex. "6,9"]
' NotAllowed = override characters never allowed and processed before any other option (meaning if you have it in both allow/not allow - not allow takes precedence
' NOTE: AllowedChars and NotAllowed arguments are *not* case-sensitive
Dim Counter As Long
Dim TestChar As String
Dim TestAsc As Long
' Loop through characters
For Counter = 1 To Len(CheckStr)
' Get current character and its ANSI number
TestChar = Mid(CheckStr, Counter, 1)
TestAsc = Asc(TestChar)
' Test first to see if current character is never allowed
If InStr(1, NotAllowed, TestChar, vbTextCompare) > 0 Then
' do nothing
' If current character is in AllowedChars, keep it
ElseIf InStr(1, AllowedChars, TestChar, vbTextCompare) > 0 Then
stripChars = stripChars & TestChar
' If KillNums=True, test for not being in numeric range for ANSI
ElseIf KillNums Then 'only allow non-numbers
If TestAsc < 48 Or TestAsc > 57 Then
stripChars = stripChars & TestChar
End If
' If KillNums=False, test for being in numeric ANSI range
Else 'only allow numbers
If TestAsc >= 48 And TestAsc <= 57 Then
stripChars = stripChars & TestChar
End If
End If
Next
End Function
You can drop either of these in your Excel's module (Alt+F11) or in your Access forms or what not, good luck.

Resources