codecaine icon

Excel VBA custom functions

codecaine | PRO | 07/14/21 05:14:36 PM UTC (Edited) | 0 ⭐ | 965 👁️ | Never ⏰ | []
VBScript |

23.29 KB

|

None

|

0 👍

/

0 👎

Option Explicit
'character set for non ascii non printable characters
Private Const REGEX_ASCII_NON_PRINTABLE_PATTERN = "[\u0007-\u001F]"
 
'character set for non-ascii characters
Private Const REGEX_UNICODE_PATTERN = "[^\u0000-\u007F]"
 
Sub copyVisibleCells(rng As Range, destWorksheet As Worksheet)
    'Select visible cells in a range and paste only the visible cells to another worksheet
    rng.Select
    Selection.SpecialCells(xlCellTypeVisible).Select
 
    'Copy Visible cells only in the range and paste in target sheet
    Selection.Copy
    destWorksheet.Select
    destWorksheet.Paste
End Sub
 
Sub copyVisibleCellsEnd(rng As Range, destWorksheet As Worksheet)
    'Select visible cells in a range and paste only the visible cells to last row of worksheet
    Dim rowIndex As Long
    
    If getVisibleRowCount(rng) = 1 Then
        'exit sub if there is only a header and then select the destination worksheet
        destWorksheet.Select
        Exit Sub
    End If
   
    Set rng = rng.Offset(1).Resize(rng.Rows.count - 1)
 
 
    rng.SpecialCells(xlCellTypeVisible).Select
    'Copy Visible cells only in the range and paste in target sheet
    Selection.Copy
    rowIndex = destWorksheet.Range("A1").CurrentRegion.Rows.count + 1
    destWorksheet.Select
    destWorksheet.Range("A" & rowIndex).Select
    destWorksheet.Paste
End Sub
 
Function getColumnCount(rng As Range) As Long
'return the number of columns from a range
    getColumnCount = rng.Columns.count
End Function
 
Function getRowCount(rng As Range) As Long
'returns the number of rows from a range
    getRowCount = rng.Rows.count
End Function
 
Function getVisibleColumnCount(rng As Range) As Long
'returns the number of visible columns from a range
    Dim cellItem As Range
    Dim count As Long
    count = 0
    For Each cellItem In rng.SpecialCells(xlCellTypeVisible).Columns
        count = count + 1
    Next cellItem
    getVisibleColumnCount = count
End Function
 
Function getVisibleRowCount(rng As Range) As Long
'return the number of visible rows from a range
    Dim cellItem As Range
    Dim count As Long
    count = 0
    For Each cellItem In rng.SpecialCells(xlCellTypeVisible).Rows
        count = count + 1
    Next cellItem
    getVisibleRowCount = count
End Function
 
Function isVisibleRowGreaterThan(rng As Range, rowCount) As Boolean
'return the number of visible rows from a range
    Dim cellItem As Range
    Dim count As Long
    Dim isGreater As Boolean
    count = 0
    isGreater = False
    For Each cellItem In rng.SpecialCells(xlCellTypeVisible).Rows
        count = count + 1
        If count > rowCount Then
            isGreater = True
            Exit For
        End If
    Next cellItem
    isVisibleRowGreaterThan = isGreater
End Function
 
Function fileExists(file As String) As Boolean
'check if a file exists returns true if yes and false if not
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    fileExists = fso.fileExists(file)
    Set fso = Nothing
End Function
 
Function folderExists(Path As String) As Boolean
'check if a folder exists or not returns true if exisit and false if not
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    folderExists = fso.folderExists(Path)
    Set fso = Nothing
End Function
 
Function moveFile(filePath As String, fileDest As String) As Boolean
'move file to new location of the file does not exists
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    If fileExists(filePath) Then
        If Not fileExists(fileDest) Then
            Call fso.moveFile(filePath, fileDest)
        End If
    End If
End Function
 Function getFileCount(psPath As String) As Long
'strive4peace
'uses Late Binding. Reference for Early Binding:
'  Microsoft Scripting Runtime
   'PARAMETER
   '  psPath is folder to get the number of files for
   '     for example, c:\myPath
   ' Return: Long
   '    -1 = path not valid
   '     0 = no files found, but path is valid
   '    99 = number of files where 99 is some number
   
   'inialize return value
   getFileCount = -1
   'skip errors
   On Error Resume Next
   'count files in folder of FileSystemObject for path
   With CreateObject("Scripting.FileSystemObject")
      getFileCount = .GetFolder(psPath).Files.count
   End With
End Function
 
Function getFileNamesFromPath(Path As String, Optional ext As String = "", Optional excludePrefix As String = "") As Collection
'returns filenames from a folder path
'if ext is not empty then filter file names by file extension. Example of ext parameter file extension strings docx, exe
'if excludePrefix is not empty exclude all files from folder that begins with the prefix string
    Dim col As New Collection
    Dim filename As String
    
    'remove trailing spaces from path
    Path = Trim(Path)
    
    'exit function if the path does not exists
    If Path = "" Then
        Set getFileNamesFromPath = col
        Exit Function
    End If
    
    'add trailing space to path if the path if there is no trialing space
    If Not regexTest(Path, "\\$") Then
         Path = Path & "\"
    End If
 
    'filter not filter by file extension
    If ext <> "" Then
        filename = Dir(Path & "*." & ext, vbNormal & vbHidden)
    Else
        filename = Dir(Path, vbNormal & vbHidden)
    End If
    
    'add files names to collection and exclude files with a certain prefix if excludePrefix is not a empty string
    Do While filename <> ""
        If excludePrefix <> "" Then
            If InStr(1, filename, excludePrefix) = 0 Then
                col.Add filename
            End If
        Else
            col.Add filename
        End If
        filename = Dir
    Loop
    
    Set getFileNamesFromPath = col
End Function
 
Function deleteFolder(folderPath As String) As Boolean
'delete a folder from folder path
'this function deletes empty or non empty folder
'the function will failed if there is a permission access issue else returns true
'if the folder does not exists true is returned
    Dim fso As Object
    Dim tempPath As String
    tempPath = Trim(folderPath)
    If tempPath <> "" Then
        If Right(tempPath, 1) = "\" Then
            tempPath = Left(tempPath, Len(tempPath) - 1)
        End If
    End If
 
    On Error GoTo errHandler:
    Set fso = CreateObject("Scripting.FileSystemObject")
    If fso.folderExists(tempPath) Then
        Call fso.deleteFolder(tempPath)
    End If
    
    deleteFolder = True
exitSuccess:
    Exit Function
errHandler:
    Debug.Print Err.number, Err.Description
    GoTo exitSuccess
End Function
 
Function getFolderCount(psPath As String) As Long
'strive4peace
'uses Late Binding. Reference for Early Binding:
'  Microsoft Scripting Runtime
   'PARAMETER
   '  psPath is path to get the number of folders for
   '     for example, c:\myPath
   ' Return: Long
   '  -1 = path not valid
   '   0 = no folders found, but path is valid
   '  99 = number of folders where 99 is some number
   
   'inialize return value
   getFolderCount = -1
   'skip errors
   On Error Resume Next
   'count SubFolders in FileSystemObject for psPath
   With CreateObject("Scripting.FileSystemObject")
      getFolderCount = .GetFolder(psPath).SubFolders.count
   End With
End Function
 
Function columnNumToColumnLetter(colNum As Long) As String
'returns an excel column letter from the number number
'if the column letter cannot be determine returns vbnullstring
    Dim regex As Object
    Dim Matches As Object
    Dim addr As String
    Set regex = CreateObject("VBScript.RegExp")
    regex.pattern = "[A-Z]+"
    addr = Cells(1, colNum).Address(False, False)
    If regex.test(addr) Then
        Set Matches = regex.Execute(addr)
        columnNumToColumnLetter = Matches(0)
    Else
        columnNumToColumnLetter = ""
    End If
    Set regex = Nothing
    Set Matches = Nothing
End Function
 
Sub deleteRowIfCellBlank(rng As Range)
'delete the entire row if any cells are blank
    On Error Resume Next
 
    rng.Cells.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
End Sub
 
Function getColumnIndex(rng As Range, heading As String, Optional ColumnLetter As Boolean = False) As Variant
'returns heading column letter or number if the header is found else returns 0
'if ColumnLetter is true a letter is return if the column is found else 0
    Dim title As Range
    Dim HEADER As Range
    Set title = rng.Rows(1)
    For Each HEADER In title.Cells
        If StrComp(HEADER.Value, heading, vbTextCompare) = 0 Then
            If ColumnLetter = False Then
                getColumnIndex = HEADER.Column
            Else
                getColumnIndex = columnNumToColumnLetter(HEADER.Column)
            End If
            Exit Function
        End If
   
    Next HEADER
    getColumnIndex = 0
    Set title = Nothing
End Function
 
Function rangeToArray(rng As Range) As Variant
'returns a range of values as an array
 
    ' Declare dynamic array
    Dim tempArray As Variant
 
    ' tempArray values into array from first row
    rangeToArray = rng.Value
End Function
 
Sub arrayToRange(arr As Variant, rng As Range)
'copies array values to a range
'example Range("A1:C1] = Array[1,2,3]
    rng.Value = arr
End Sub
 
Function worksheetExists(sheetName As String) As Boolean
'checks active workbook if a worksheet exists
    Dim ws As Worksheet
      For Each ws In Application.ActiveWorkbook.Worksheets
        If sheetName = ws.Name Then
          worksheetExists = True
          Exit For
        End If
      Next ws
End Function
 
Function worksheetDelete(sheetName As String) As Boolean
'delete worksheet if the workseet exists in the active workbook by worksheet name
    If worksheetExists(sheetName) Then
        ActiveWorkbook.Worksheets(sheetName).Delete
    End If
    worksheetDelete = True
End Function
 
Function worksheetCreate(sheetName As String, Optional sheetIndex As Integer = 0) As Worksheet
' create a worksheet with provided sheetname in active workbook
    Dim objSheet As Object
    On Error GoTo errHandler
    If sheetIndex = 0 Then
        sheetIndex = Sheets.count
    End If
    Set objSheet = Sheets.Add(After:=Sheets(sheetIndex))
    objSheet.Name = sheetName
    Set worksheetCreate = objSheet
    Exit Function
errHandler:
    Debug.Print Err.number, Err.Description
End Function
 
Function worksheetCopy(wsName As String, Optional wbPath = "", Optional newWsName = "") As Boolean
'copies a worksheet from within the same workbook or from an external workbook
'if newWsName is not an empty string the copied worksheet is renamed to the newWsName
    Dim tempActiveWorkbook As Workbook, wbExternal As Workbook
    On Error GoTo errHandler
    Set tempActiveWorkbook = ActiveWorkbook
    'delete sales force worksheet if it already exists
    If wbPath <> "" Then
        Set wbExternal = Workbooks.Open(filename:=wbPath)
        wbExternal.Sheets(wsName).Copy After:=Workbooks(tempActiveWorkbook.Name).Sheets(tempActiveWorkbook.Sheets.count)
        wbExternal.Close SaveChanges:=False
    Else
        tempActiveWorkbook.Sheets(wsName).Copy After:=Workbooks(tempActiveWorkbook.Name).Sheets(tempActiveWorkbook.Sheets.count)
    End If
   
    If newWsName <> "" Then
        tempActiveWorkbook.ActiveSheet.Name = newWsName
    End If
   
    worksheetCopy = True
exitSuccess:
    Set tempActiveWorkbook = Nothing
    Set wbExternal = Nothing
    Exit Function
errHandler:
    MsgBox Err.Description
    Resume exitSuccess
End Function
 
Sub worksheetUnhideAllRows(Optional ws As Worksheet)
'unhide all rows in a worksheet
'if no worksheet is provided then the active worksheet is used
If ws Is Nothing Then
    Set ws = ActiveSheet
End If
    ws.Rows.EntireRow.Hidden = False
End Sub
 
Sub worksheetUnhideAllColumns(Optional ws As Worksheet)
'unhide all columns in a worksheet
'if no worksheet is provided then the active worksheet is used
If ws Is Nothing Then
    Set ws = ActiveSheet
End If
    ws.Rows.EntireColumn.Hidden = False
End Sub
 
Sub worksheetUnhideAllRowsAndColumns(Optional ws As Worksheet)
'unhide all rows and columns in a worksheet
'if no worksheet is provided then the active worksheet is used
If ws Is Nothing Then
    Set ws = ActiveSheet
End If
    Call worksheetUnhideAllRows(ws)
    Call worksheetUnhideAllColumns(ws)
End Sub
 
Function worksheetIsFilterMode(Optional ws As Worksheet) As Boolean
'returns true if a worksheet has a filter applied else false
'if no worksheet is provided teh active worksheet is used
    If ws Is Nothing Then
        Set ws = ActiveSheet
    End If
    worksheetIsFilterMode = ws.FilterMode
End Function
 
Sub worksheetClearFilter(Optional ws As Worksheet)
'unfilter a worksheet if it worksheet is filtered
'if no worksheet is provided teh active worksheet is used
If ws Is Nothing Then
        Set ws = ActiveSheet
    End If
    If worksheetIsFilterMode(ws) Then
        ws.ShowAllData
    End If
End Sub
 
Sub worksheetShowAllData(Optional ws As Worksheet)
'unhides all rows, columns and remove filters from a worksheet
'if no worksheet is provided the active worksheet is used
    If worksheetIsFilterMode(ws) Then
        ws.ShowAllData
    End If
    Call worksheetUnhideAllRowsAndColumns
End Sub
 
'''''''''''''''''''''''''''''''''''''''''''''''''''''
'             String Functions Section              '
'''''''''''''''''''''''''''''''''''''''''''''''''''''
 
'ASCII char URL https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/com.ibm.aix.networkcomm/conversion_table.htm
 
 
Public Function regexTest(strData As String, pattern As String, Optional isGlobal As Boolean = True, Optional isIgnoreCase As Boolean = True, Optional isMultiLine As Boolean = True) As Boolean
'returns true if a pattern match else false
 
Dim objRegex As Object
 
On Error GoTo errHandler
 
Set objRegex = CreateObject("vbScript.regExp")
With objRegex
    .Global = isGlobal
    .ignoreCase = isIgnoreCase
    .MultiLine = isMultiLine
    .pattern = pattern
    'if the pattern is a match then replace the text else return the orginal string
    If .test(strData) Then
        regexTest = True
    Else
        regexTest = False
    End If
End With
exitSuccess:
    Set objRegex = Nothing
    Exit Function
errHandler:
    regexTest = False
    Debug.Print Err.Description
    Resume exitSuccess
End Function
 
Function regexMatches(data As String, pattern As String, Optional ignoreCase As Boolean = True, Optional globalMatches As Boolean = True) As Collection
'return a collection found from a pattern using regular expressions
 
    Dim regex As Object, theMatches As Object, match As Object
    Dim col As New Collection
    Set regex = CreateObject("vbScript.regExp")
     
    regex.pattern = pattern
    regex.Global = globalMatches
    regex.ignoreCase = ignoreCase
     
    Set theMatches = regex.Execute(data)
     
    For Each match In theMatches
      col.Add match.Value
    Next
    
    Set regexMatches = col
End Function
 
Function regexFirstMatch(data As String, pattern As String, Optional ignoreCase As Boolean = True, Optional globalMatches As Boolean = True) As String
'returns the first match from a regular expression pattern
 
    Dim regex As Object, theMatches As Object, match As Object
    Set regex = CreateObject("vbScript.regExp")
     
    regex.pattern = pattern
    regex.Global = globalMatches
    regex.ignoreCase = ignoreCase
     
    Set theMatches = regex.Execute(data)
     
    For Each match In theMatches
      regexFirstMatch = match.Value
      Exit For
    Next
 
End Function
 
Function regexReplace(strData As String, pattern As String, Optional replace_with_str = vbNullString, Optional isGlobal As Boolean = True, Optional isIgnoreCase As Boolean = True, Optional isMultiLine As Boolean = True) As String
'returns string replacing data using a regex pattern
 
    Dim objRegex As Object
 
On Error GoTo errHandler
    Set objRegex = CreateObject("vbScript.regExp")
    With objRegex
        .Global = isGlobal
        .ignoreCase = isIgnoreCase
        .MultiLine = isMultiLine
        .pattern = pattern
        'if the pattern is a match then replace the text else return the orginal string
        If .test(strData) Then
            regexReplace = .Replace(strData, replace_with_str)
        Else
            regexReplace = strData
        End If
    End With
exitSuccess:
    Set objRegex = Nothing
    Exit Function
errHandler:
    regexReplace = strData
    Debug.Print Err.Description
    Resume exitSuccess
End Function
 
Function regexPatternCount(strData As String, pattern As String, Optional isGlobal As Boolean = True, Optional isIgnoreCase As Boolean = True, Optional isMultiLine As Boolean = True) As Long
'returns the number of matters matches in a string using regex
'-1 will return if there was an error
 
    Dim objRegex As Object
    Dim Matches As Object
 
On Error GoTo errHandler
    Set objRegex = CreateObject("vbScript.regExp")
    objRegex.pattern = pattern
    objRegex.Global = isGlobal
    objRegex.ignoreCase = isIgnoreCase
    objRegex.MultiLine = isMultiLine
    'Retrieve all matches
    Set Matches = objRegex.Execute(strData)
    'Return the pattern matches count
    regexPatternCount = Matches.count
exitSuccess:
    Set Matches = Nothing
    Set objRegex = Nothing
    Exit Function
errHandler:
    regexPatternCount = -1
    Resume exitSuccess
End Function
 
Function regexRemoveConcatDupChars(data As String) As String
'remove duplicates characters when concatenated together
    regexRemoveConcatDupChars = regexReplace(data, "(.)\1+", "$1")
End Function
 
Function regexContainsConcatDupChars(data As String) As Boolean
'returns true if there are concatenated characters of the same type in as string provided
    regexContainsConcatDupChars = regexPatternCount(data, "(.)\1+")
End Function
 
Function regexContainsNonAscii(data As String) As Boolean
'returns true if a string contains unicode characters else false
    If regexPatternCount(data, REGEX_UNICODE_PATTERN) > 0 Then
        regexContainsNonAscii = True
    Else
        regexContainsNonAscii = False
    End If
End Function
 
Function regexLeftTrim(data As String) As String
'returns a string removing spaces and tab characters from the beginning of a string only
    regexLeftTrim = regexReplace(data, "^[\s\t]+")
End Function
 
Function regexRightTrim(data As String) As String
'returns a string removing spaces and tab characters from the beginning and end of a string
    regexRightTrim = regexReplace(data, "[\s\t]+$")
End Function
 
Function regexTrim(data As String) As String
'returns a string removing spaces and tab characters from the beginning and end of a string
    data = regexLeftTrim(data)
    data = regexRightTrim(data)
    regexTrim = data
End Function
 
Function setFirstLetterCapitalized(data As String) As String
'returns a string with first letter capitialize
    If Len(data) = 0 Then
        setFirstLetterCapitalized = ""
    Else
        setFirstLetterCapitalized = UCase(Mid(data, 1, 1)) & Mid(data, 2, Len(data))
    End If
End Function
 
Function setProperCase(data As String) As String
'returns a string with all words starting with a capital letter and the rest lowercase
    setProperCase = StrConv(data, vbProperCase)
End Function
 
Function sqlStrFormat(data As String) As String
'returns a string replacing single quotes with double single quotes
    Const SINGLE_QUOTE_CHAR = "'"
    sqlStrFormat = Replace(data, SINGLE_QUOTE_CHAR, SINGLE_QUOTE_CHAR & SINGLE_QUOTE_CHAR)
End Function
 
Private Sub displayError(Optional toImmediateWindow As Boolean = True)
'display error code number and description in the immediate window by default
'if toImmediateWindow is false then the error is displayed in a messagebox
'this subroutine is used for ON ERROR GoTo statements error handler section
    If toImmediateWindow Then
        Debug.Print Err.number, Err.Description
    Else
        MsgBox Err.number & " " & Err.Description, vbCritical
    End If
End Sub
 
Function createDictionary(Optional ignoreCase As Boolean = False) As Object
'returns a dictionary object
'if ignore case is true the dictionary keys will not be case sensitive. The default is case sensitive
    Dim dict As Object
    
    Set dict = CreateObject("Scripting.Dictionary")
    
    If ignoreCase Then
        dict.comparemode = vbTextCompare
    End If
    
    Set createDictionary = dict
End Function
 
Function isValueInRange(rng As Range, search As String, Optional lookIn As XlFindLookIn = XlFindLookIn.xlFormulas, Optional lookAt As XlLookAt = XlLookAt.xlWhole, Optional matchCase As Boolean = False) As String
'returns string address of the cell where the value if found in the range
'if the value is not found than an empty string is returned
 
    Dim cell As Range
    
    Set cell = rng.Find(What:=search, lookIn:=lookIn, lookAt:=lookAt, matchCase:=matchCase)
    
    If cell Is Nothing Then
        isValueInRange = ""
    Else
        isValueInRange = cell.Address
    End If
 
End Function
 
Function countNumberOfNonBlankCells(rng As Range) As Long
'returns the count of cells that are not empty
    countNumberOfNonBlankCells = Application.WorksheetFunction.CountA(rng)
End Function
 
Sub quickSort(vArray As Variant, inLow As Long, inHi As Long)
'sort an array in ascending order
'example quickSort(arr, LBound(arr), UBound(arr))
  Dim pivot   As Variant
  Dim tmpSwap As Variant
  Dim tmpLow  As Long
  Dim tmpHi   As Long
 
  tmpLow = inLow
  tmpHi = inHi
 
  pivot = vArray((inLow + inHi) \ 2)
 
  While (tmpLow <= tmpHi)
 
     While (vArray(tmpLow) < pivot And tmpLow < inHi)
        tmpLow = tmpLow + 1
     Wend
 
     While (pivot < vArray(tmpHi) And tmpHi > inLow)
        tmpHi = tmpHi - 1
     Wend
 
     If (tmpLow <= tmpHi) Then
        tmpSwap = vArray(tmpLow)
        vArray(tmpLow) = vArray(tmpHi)
        vArray(tmpHi) = tmpSwap
        tmpLow = tmpLow + 1
        tmpHi = tmpHi - 1
     End If
 
  Wend
 
  If (inLow < tmpHi) Then quickSort vArray, inLow, tmpHi
  If (tmpLow < inHi) Then quickSort vArray, tmpLow, inHi
 
End Sub
 
Function binarySearch(lookupArray As Variant, lookupValue As Variant) As Long
'binary search lookup for arrays
'the array must be sorted when using this function
'-1 is return if not found else the index of where the item is found
 
    Dim lngLower As Long
    Dim lngMiddle As Long
    Dim lngUpper As Long
 
    lngLower = LBound(lookupArray)
    lngUpper = UBound(lookupArray)
 
    Do While lngLower < lngUpper
        
        lngMiddle = (lngLower + lngUpper) \ 2
 
        If lookupValue > lookupArray(lngMiddle) Then
            lngLower = lngMiddle + 1
        Else
            lngUpper = lngMiddle
        End If
        
    Loop
    
    If lookupArray(lngLower) = lookupValue Then
        binarySearch = lngLower
    Else
        binarySearch = -1    'search does not find a match
    End If
End Function
 
Function collectionToArray(col As Collection) As Variant
'returns an array from a collection object
    Dim result  As Variant
    Dim cnt     As Long
    
    ReDim result(col.count - 1)
 
    For cnt = 0 To col.count - 1
        result(cnt) = col(cnt + 1)
    Next cnt
 
    collectionToArray = result
End Function
 
 
 
 

Comments