Find value in a vba dynamic table - vba

I have a dynamic table of strings that I defined in vba (I precise that it is not an XL table) and I want to check if a specific value is present in this table. Here is a portion of my function:
Dim tableOfSizes() As String
sizeTable(0)=size1
sizeTable(1)=size2
sizeTable(2)=size3
'size2 and size3 are optional parameters of the function
If instr(tableOfSizes, "Medium") <> 0 Then
' Action if "Medium" is found in the table
End If
but it seems that instr do not work for tables, or at least, for dynamic tables. Is that the problem?

For 1D-array you can use following approaches.
Way №1 Filter function
If UBound(Filter(tableOfSizes, "Medium")) <> -1 Then
' Action if "Medium" is found in the table
End If
Way №2 (for Excel-VBA) Application.Match
If Not IsError(Application.Match("Medium", tableOfSizes, 0)) Then
' Action if "Medium" is found in the table
End If
For multi-dimmension arrays you can use following function:
Function contains(arr, elem) As Boolean
Dim a
contains = False
For Each a In arr
If a = elem Then
contains = True
Exit Function
End If
Next
End Function
and then:
If contains(tableOfSizes, "Medium") Then
' Action if "Medium" is found in the table
End If

Related

VBA - applying function to a sub

im a VBA newbie. I have to create function, that transform state address to state full name eg. CA -> California and apply it to the whole column with addresses.
Given task:
*Open VBE and add module named modFunction. In this module create function (named this function StateFullName) which transform state address into state full name. Function should take one parameter (state address) and return one string value (state full name)
In column 7 each rows with data should have full state name (Tip: use loop and function prepared in modFunction).*
I have created a function, but now I don't know how to apply it in sub with loop.
My function:
Function StateFullName(state_address As String) As String
Select Case state_address
Case "CA": StateFullName = "California"
Case "AZ": StateFullName = "Arizona"
Case "MT": StateFullName = "Montana"
Case "NM": StateFullName = "New Mexico"
End Select
End Function
When setting up your Function as a public function, you can use it in two ways, as a formula and as a Function on other Macros/Subscripts.
Here is a sample of your code:
Public Function statestring(twoletter As String) As String
Select Case twoletter
Case "CA": statestring = "California"
Case "AZ": statestring = "Arizona"
Case Else: statestring = "Unknown State"
End Select
End Function
And here I am using it as a Formula on Column B, top right you can see the formula:
And here I am using it on another Sub as a Function to fill Column C:
(It could be a lot better coded but I just wrote it as it came to mind)
Edit: After a quick thought an offset would be make it much better
Sub getFullState()
Set wk = ThisWorkbook
With wk.Sheets("Sheet1")
'startRow = 2 ' Removed
For Each Rng In .Range("A2:A4")
'Next line Replaced with Offset
'.Range("C" & startRow).Value = statestring(Rng.Value)
Rng.Offset(0, 2).Value = statestring(Rng.Value)
'startRow = startRow + 1 'Removed
Next
End With
End Sub

MS project : how to check in VBA if a column is visible in tasks view?

I wrote a VBA macro in MSP to insert a column (TaskColumn Flag20) to display specific indicator. It works well except that it creates the column each time I open the project file. Hence I am looking for a way to check if the column exists and is visible when opening file. I couldn't find any information on such possibility.
Many thanks beforehand.
Here's a way to get all of the visible columns programmatically
'The function returns all of the visible column names as a delimiter separated string.
' Call with a string as the first parameter to represent a custom delimiter, or leave
' blank to use the default of a comma ,
Function GetColumns(Optional customDelimeter As String) As String
If customDelimeter = "" Then customDelimeter = "," 'handle custom delimeter
Dim viewableColumns As String 'create return value
SelectRow Row:=1, RowRelative:=False 'select the 1st row then parse all columns composing that row
For Each lngFieldID In MSProject.ActiveSelection.FieldIDList
Dim columnName As String
If lngFieldID > 0 Then
'convert the column ID to a string of the field name, either custom or built-in
columnName = Trim((CustomFieldGetName(lngFieldID)))
If Len(columnName) = 0 Then
columnName = Trim(FieldConstantToFieldName(lngFieldID)) ' use the built-in field name
End If
'append return value
viewableColumns = viewableColumns & customDelimeter & columnName
End If
Next
'get rid of the first delimeter
If Len(viewableColumns) > 0 Then
viewableColumns = Right(viewableColumns, Len(viewableColumns) - 1)
End If
GetColumns = viewableColumns
End Function
A better idea might be to create/edit a specific view with a custom table that includes the columns you want. Then you don't have to check anything, just choose that view and it will always give you what you want.
If you want to automate showing the view then write a macro that uses the Application.ViewApplyEx method.
Final working code, thanks to Jerred S.
Public Sub CheckFlag20Column()
SelectRow Row:=1, RowRelative:=False 'select the 1st row then parse all columns composing that row
For Each lngFieldID In MSProject.ActiveSelection.FieldIDList
Dim columnName As String
If lngFieldID > 0 Then
columnName = Trim(FieldConstantToFieldName(lngFieldID)) ' use the built-in field name
If columnName = "Flag20" Then
SelectTaskColumn Column:="Flag20"
ColumnDelete
End If
End If
Next
End Sub
If the interest is in testing a single column, try this.
Works for either its constant name or the user assigned custom name.
Public Function IsColumnVisible(Name As String)
IsColumnVisible = False
On Error GoTo LeaveIsColumnVisible
SelectTaskColumn Column:=Name ' Will error out if the column is hidden
IsColumnVisible = True
LeaveIsColumnVisible:
End Function

Get one cell from passed column/row/range VBA Excel

i'm writing a user defined function for excel in VBA.
User may pass a whole column/row into the function instead of one cell. How do you get cell that is in the same row (for column case) and in the same column (for row case), where the function is.
For example, when you are writing in Excel in cell, say, C3 the formula "=A:A*B:B" it calculates A3*B3 in fact. I want to have the same behaiviour in my UDF.
Let's assume function that returns passed argument for simplicity reasons.
This code does not work (returns #VALUE! for columns/rows/ranges):
Public Function MyTestFunction(ByVal arg1) As Variant
MyTestFunction = arg1
End Function
My option is as follows, but I am concerned about performance and the fact that user may want to pass a value to the formula instead of Range.
Public Function MyTestFunction2(ByVal arg1 As Range) As Variant
If arg1.Count = 1 Then
MyTestFunction2 = arg1.Value
Else
' Vertical range
If arg1.Columns.Count = 1 Then
MyTestFunction2 = arg1.Columns(1).Cells(Application.Caller.Row, 1).Value
Exit Function
End If
' Horizontal range
If arg1.Rows.Count = 1 Then
MyTestFunction2 = arg1.Rows(1).Cells(1, Application.Caller.Column).Value
Exit Function
End If
' Return #REF! error to user
MyTestFunction2 = CVErr(xlErrRef)
End If
End Function
How do you solve this problem?
Thanks to valuable comments code has been slightly updated and now can be used in other formulas to filter input values.
Public Function MyTestFunction2(ByVal arg1) As Variant
If Not TypeName(arg1) = "Range" Then
MyTestFunction2 = arg1
Exit Function
End If
If arg1.Count = 1 Then
MyTestFunction2 = arg1.Value
Else
' Vertical range
If arg1.Columns.Count = 1 Then
' check for range match current cell
If arg1.Cells(1, 1).Row > Application.Caller.Row Or _
arg1.Cells(1, 1).Row + arg1.Rows.Count - 1 < Application.Caller.Row Then
' Return #REF! error to user
MyTestFunction2 = CVErr(xlErrRef)
Exit Function
End If
' return value from cell matching cell with function
MyTestFunction2 = arg1.Worksheet.Columns(1).Cells(Application.Caller.Row, arg1.Column).Value
Exit Function
End If
' Horizontal range
If arg1.Rows.Count = 1 Then
' check for range match current cell
If arg1.Cells(1, 1).Column > Application.Caller.Column Or _
arg1.Cells(1, 1).Column + arg1.Columns.Count - 1 < Application.Caller.Column Then
' Return #REF! error to user
MyTestFunction2 = CVErr(xlErrRef)
Exit Function
End If
' return value from cell matching cell with function
MyTestFunction2 = arg1.Worksheet.Rows(1).Cells(arg1.Row, Application.Caller.Column).Value
Exit Function
End If
' Return #REF! error to user
MyTestFunction2 = CVErr(xlErrRef)
End If
End Function
In the first code snippet change MyTestFunction = arg1 to Set MyTestFunction = arg1. Also add a small mechanism that recognizes the TypeName() of the arg1 and make sure that the function is receiving a Range type object.
Public Function MyTestFunction(ByVal arg1) As Variant
Set MyTestFunction = arg1
End Function
example
Then, if you get to your spreadsheet and type in =MyTestFunction(A:A) on any row and you'll receive the equivalent value from the column you're passing to the function that sits on the same row.
And your second idea about getting a similar behaviour as =A:A*B:B you can achieve with
Public Function MyTestFunction2(ParamArray arr() As Variant)
MyTestFunction2 = arr(0)
End Function
example
I think you need to use Application.ThisCell property to do it. According to MSDN:
Application.ThisCell- Returns the cell in which the user-defined
function is being called from as a Range object.
Let me present how to use it on simple example.
Imagine we have data as presented below in column A:B and we want to achieve results which comes from =A*B for each row separately.
In such situation you need the function below and put it next in C column in this way: =MyTestFunction(A:A,B:B)
Function MyTestFunction(rngA As Range, rngB As Range)
Dim funRow As Long
funRow = Application.ThisCell.Row
MyTestFunction = rngA(funRow) * rngB(funRow)
End Function
Please keep in mind that Application.ThisCell will not work if you call your function from other VBA procedure.

How can I check if a cell in Excel spreadsheet contains number

I have a column of addresses and I have to find those which don't contain street numbers. Unfortunately, addresses have been input by various users and they do not follow the same pattern so the street type, street name, suburb are in different order and I can't use functions like LEFT, RIGHT or MID to check if particular character is a number. The column looks like this:
10 Willsons Drive, Manhattan
Epping, 23 Wet Rd
Longsdale St, Kingsbury
11 Link Crt, Pakenham
Is there an Excel or VBA function that can tell me if cell / string contains numbers?
Put this into a Module, then in your worksheet, may be a column next to it, put formula =HaveNumbers(A2) and check if you want it like that (True/False). You can change it to Return a String instead. This Returns TRUE / FALSE.
Function HaveNumbers(oRng As Range) As Boolean
Dim bHaveNumbers As Boolean, i As Long
bHaveNumbers = False
For i = 1 To Len(oRng.Text)
If IsNumeric(Mid(oRng.Text, i, 1)) Then
bHaveNumbers = True
Exit For
End If
Next
HaveNumbers = bHaveNumbers
End Function
There isn't a single VBA function that will do what you want, but the following function should do the trick:
Public Function ContainsNumbers(Inp As String) As Boolean
Dim Strings() As String, Str As Variant
Strings = Split(Inp, " ")
For Each Str In Strings
If IsNumeric(Str) Then
ContainsNumbers = True
Exit For
End If
Next
End Function
Then put something like =ContainsNumbers(A1) in a nearby cell.
Thanks Monty. In my case, though, numbers were not always separated from words so I had to iterate over each character. I used following:
Function ContainsNumber(text As String)
'checks if given cell contains number
For i = 1 To Len(text)
If IsNumeric(Mid$(text, i, 1)) Then
ContainsNumber = True
Exit Function
End If
Next
ContainsNumber = False
End Function

Checking Data Types in a Range

I am trying to validate the data types of all cells in a user-selected range are the same, using a VBA function. I have the following code (simplified), which works for the most part:
Dim vTempRange As Variant
Dim vCell As Variant
vTempRange = DataRange.Value
For Each vCell In vTempRange
If Len(vCell) > 0 Then
'Use TypeName(vCell)
'Complete validation here
End If
Next vCell
Sometimes a user may select a column of percentages, sometimes a column of decimal values, and sometimes a time value (not associated with a date). VBA seems to see all three of these as a Double, which is technically not incorrect. The problem is, the format of the selection will be used as part of the final output, so 12:00:00 should display as such, and not 0.50, which is currently the case.
I looked into using something like this in conjunction:
Dim vCell As Variant
For Each vCell In DataRange
If Len(vCell) > 0 Then
'Use vCell.NumberFormat
'Complete validation here
End If
Next vCell
But the NumberFormat is not consistent. e.g., a user may have a percent listed as 0% vs. 0.000% or a time as h:m:s vs. hh:mm:ss, so I see it as being difficult to correctly capture this value.
Is there a way to accurately determine without user intervention when a time is selected vs. one of the other types? Determining a percent value versus a 0<x<1 decimal value would also be nice, but not required.
I have other options at my disposal, such as ignoring the formatting in the final output (really not desirable) or explicitly asking the user to identify the type (but this is neither as clean nor automatic as I would like).
Try this. Paste this in a module. You can then use it as a Worksheet formula.
I had this code in my database which was picked up from here and I modified it to suit your needs.
Public Function CellType(c)
Application.Volatile
Select Case True
Case IsEmpty(c): CellType = "Blank"
Case Application.IsText(c): CellType = "Text"
Case Application.IsLogical(c): CellType = "Logical"
Case Application.IsErr(c): CellType = "Error"
Case IsDate(c): CellType = "Date"
Case InStr(1, c.Text, ":") <> 0: CellType = "Time"
Case InStr(1, c.Text, "%") <> 0: CellType = "Percentage"
Case IsNumeric(c): CellType = "Value"
End Select
End Function
ScreenShot
You can further modify it to add an IF clause inside Case IsNumeric(c): CellType = "Value" to check for decimals, Scientific notation etc using INSTR
Declare vCell as Range and then do your check:
TypeName(vCell.Value)
That will accurately catch your dates.
YOu will likely need to add some if/then logic to capture "percents" since these are double-type values -- the "%" part is merely cell formatting, so you may be able to just check the Right(vCell.NumberFormat,1) = "%" .
VarType function
Returns an Integer indicating the subtype of a variable, or the type of an object's default property.
https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/vartype-function
Ex1: using to write if.
Function DataType(x As Variant) As String
If VarType(x) = vbDate Then
DataType = "Date"
ElseIf VarType(x) = vbString Then
DataType = "String"
'.....
End If
End Function
Ex2: concatenate cells in range having value.
Function ConcatenateRange(cellRange As Range) As String
Dim cel As Range, temp As String
temp = ""
For Each cel In cellRange
'use VarType to check if the cell is empty.
'if the cell is not empty concatinate it.
If VarType(cel) <> vbEmpty Then
temp = temp & cel
End If
Next
ConcatenateRange = temp
End Function