Thursday, 6 April 2023

VisualCron API & GUI

VisualCron API & GUI

Introduction

VisualCron is a powerful GUI tool that allows users to schedule jobs similar to Windows Tasks Scheduler, except it provides additional powerful features to easily perform complex processing using numerous cutting edge technologies ranging from as simple as running Cmd/PowerShell commands, JSON, Excel, PDF processing, data flow control, calls to SSIS packages, and for anything that is not built-in you can run your code code using almost any programming language of your choice.


VisualCron also comes a comprehensive API that allows you to interact/control your jobs via .NET code (using one of the language that can handle .NET such as C#, VB.Net, PowerShell etc.)


Following tutorial includes some useful tip, tricks and guidelines on various topic on VisualCron that I wrote over the years during developing process automation and problem solving using VisualCron.


Full documentation can be seen at https://www.visualcron.com/documentation.aspx.


TODO.



Why use API?

Recently we needed to decommission a server which was being used by few (unknown) VisualCron jobs. All we knew was that some jobs were exporting / copying output files into this server (that was being decommissioned).

A task was created for one of the teams to go through all the (hundreds of) jobs in the VisualCron UI to identify which jobs was copying files to this server. Considering the sheer number of jobs that contain tens of tasks / steps, it would have been an extremely time/resource consuming task.

I discovered that VisualCron comes with an API allowing us to programmatically query/interact with the VisualCron instance, which I ended up using to resolve our problem. The outcome was incredibly productive.

Here is the code that I wrote in PowerShell (which can be easily be translated in C# if needed).


Following code lists the specific jobs and tasks that copy files to specified server (along with the file destinations):


Another useful utilisation of the API was to list the recipient(s) from the Email-Task.


Accessing VisualCron Variables & Values via API

There are 2 types of variables in VisualCron: a) Global User variables, and b) Job variables, following code allows us to query both of these variables (names & values etc.)



TODO...



Sunday, 3 July 2022

TextDiff - Excel VBA Utility

TextDiff - Excel VBA Utility

TextDiff - Excel VBA Utility for character-by-character differences between two paragraphs/code snippets.


'******************************************************************************************
'Written by:    Eddie Kumar Shrivastava
'Main method:   Sub CompareAndHighlightDiffs() - this is the main/parent function that calls relevant dependencies/sub-routines.
'Description:   Utility to quickly find character-by-character differences between two paragraphs/code snippets in Cells [A1] & [B1].
'Usage:         In Excel spreadsheet copy-paste the two paragraphs/code snippets (that need to be compared) in Cells [A1] & [B1], then run the CompareAndHighlightDiffs() function.
'Date Created:  29-09-2017
'Date Modified: 23-10-2017
'Version:   1.2
'Version info/Change Log:
'   - Added feature to span/merge multiple cells, when text overflows out of first cell.
'   - Fixed issue with recognition of the repeating words.
'******************************************************************************************

'Following (CompareAndHighlightDiffs) is the main/parent function that calls relevant dependencies/sub-routines.
'TEXT COMPARISON: Following sub compares text and highlights the differences between cells A1 against B1:
Sub CompareAndHighlightDiffs()
    Call BasicEqualityCheck([A1], [B1], False)
'TRIMMING WHITE-SPACE:
  'Trimming Left & Right BLANKS:
    [A1].Value = Trim([A1].Value)
    [B1].Value = Trim([B1].Value)
  'Trimming Left & Right NEWLINE-CHARS:
    [A1].Value = TrimNewLineCharsFromEdges([A1].Value)
    [B1].Value = TrimNewLineCharsFromEdges([B1].Value)
    Call SortThenCopyA1B1ValuesToA3B3
    Call CompareAndHighlightPassedValues([A1], [B1])
    Call CompareAndHighlightPassedValues([A3], [B3])
    
    Call FixRowHeightLimit
End Sub
Sub CompareAndHighlightPassedValues(CellA As Range, CellB As Range)
'------------------------
'CHAR-BY-CHAR Comparison:
'------------------------
    Dim posA, posRevA As Integer
    Dim posB, posRevB As Integer
    'Dim s1, s2 As String
    's1 = CellA
    's2 = CellB
    
    s1 = CellA.Value
    s2 = CellB.Value
    s1 = ReplaceWhiteSpaces(s1)
    s2 = ReplaceWhiteSpaces(s2)
    trimmedTxt1 = Replace(s1, "¬", "")
    trimmedTxt2 = Replace(s2, "¬", "")
    Call BasicEqualityCheck(trimmedTxt1, trimmedTxt2, True)
    
    'Hightlighting entire text of both cells:
    Call HighlightCellText(CellA)
    Call HighlightCellText(CellB)
    
    posA = GetFirstDiffNonWhitespaceCharPosition(s1, s2)
    posB = GetFirstDiffNonWhitespaceCharPosition(s2, s1)
    s1 = StrReverse(s1)
    s2 = StrReverse(s2)
'    Debug.Print s1
    posRevA = GetFirstDiffNonWhitespaceCharPosition(s1, s2)
    posRevB = GetFirstDiffNonWhitespaceCharPosition(s2, s1)
'CellA Formatting:
    'With CellA.Characters(posA, Len(s1) - ((posRevA + posA) - 2)).Font 'character in the middle.
    With CellA.Characters(1, posA - 1).Font
        .ColorIndex = 0  'blackCIndex
        .fontStyle = "Regular"
    End With
'    If Len(s1) > (posA + posRevA) - 2 Then
        With CellA.Characters(Len(s1) - (posRevA - 2), Len(s1)).Font
            .ColorIndex = 0  'blackCIndex
            .fontStyle = "Regular"
        End With
'    End If
    
'CellB Formatting:
    'With CellB.Characters(posB, Len(s1) - ((posRevB + pos) - 2)).Font 'characters in the middle.
    With CellB.Characters(1, posB - 1).Font
        .ColorIndex = 0  'black Index
        .fontStyle = "Regular"
    End With
'    If Len(s2) > (posB + posRevB) - 2 Then
        With CellB.Characters(Len(s2) - (posRevB - 2), Len(s2)).Font
            .ColorIndex = 0  'blackCIndex
            .fontStyle = "Regular"
        End With
'    End If
'
'------------------------
'WORD-BY-WORD Comparison (for unmatched words):
'------------------------
'    MsgBox Mid(CellA, pos, Len(s1) - ((posRevA + pos) - 2)) 'characters in the middle.
'    MsgBox Mid(CellB, pos, Len(s2) - ((posRevA + pos) - 2)) 'characters in the middle.
    'Starting positions of Unmatch-text:
    Dim unmatPosA, unmatPosB As Integer
    unmatPosA = posA    'Start comparing from posA - where first diff-char was found.
    unmatPosB = posB
    
    'Initialisation of the variable holding Unmatched-text:
    Dim unmatchedA, unmatchedB As String  'unmatched characters in the middle.
    If Len(s1) > ((posRevA + posA) - 2) Then
        unmatchedA = WorksheetFunction.Trim(Mid(CellA, posA, Len(s1) - ((posRevA + posA) - 2)))
    Else
        unmatchedA = ""
    End If
    If Len(s2) > ((posRevB + posB) - 2) Then
        unmatchedB = WorksheetFunction.Trim(Mid(CellB, posB, Len(s2) - ((posRevB + posB) - 2)))
    Else
        unmatchedB = ""
    End If
    Dim cIndex As Integer: cIndex = 0
    Dim fontStyle As String: fontStyle = "Regular"
    'Comparing word-by-word using Arrays:
WordByWord:
    Dim arr1() As String
    Dim arr2() As String
    unmatchedA = Replace(unmatchedA, Chr(10), " ")  'Soft enter
    'unmatchedA = Replace(unmatchedA, Chr(13), " ") 'Hard enter - doesn't exist within Cell.
    arr1() = Split(unmatchedA, Chr(32)) 'split by blank-spaces
    'MsgBox Join(arr1, ", ")
    unmatchedB = Replace(unmatchedB, Chr(10), " ")
    'unmatchedB = Replace(unmatchedB, Chr(13), " ")
    arr2() = Split(unmatchedB, Chr(32))
    For Each elem In arr2
        If elem <> "" Then
            If IsInArrayInt(elem, arr1) <> -1 Then
                unmatPosA = InStr(unmatPosA, CellA, elem, vbTextCompare)
                With CellA.Characters(unmatPosA, Len(elem)).Font
                    .ColorIndex = 0  'blackCIndex
                    .fontStyle = "Regular"
                End With
                unmatPosA = unmatPosA + Len(elem)
            End If
        Else
            If (InStr(unmatPosA, Mid(CellA, unmatPosA, 1), " ", vbTextCompare) Or InStr(unmatPosA, Mid(CellA, unmatPosA, 1), Chr(10), vbTextCompare)) Then
                unmatPosA = unmatPosA + 1
            End If
        End If
    Next
    For Each elem In arr1
        If elem <> "" Then
            If IsInArrayInt(elem, arr2) <> -1 Then
                unmatPosB = InStr(unmatPosB, CellB, elem, vbTextCompare)
                With CellB.Characters(unmatPosB, Len(elem)).Font
                    .ColorIndex = 0  'blackCIndex
                    .fontStyle = "Regular"
                End With
                unmatPosB = unmatPosB + Len(elem)
            End If
        Else
            If (InStr(unmatPosB, Mid(CellB, unmatPosB, 1), " ", vbTextCompare) Or InStr(unmatPosB, Mid(CellB, unmatPosB, 1), Chr(10), vbTextCompare)) Then
                unmatPosB = unmatPosB + 1
            End If
        End If
    Next
    On Error GoTo HighlightSuccessful
    If CellA.Font.ColorIndex < 1 And CellB.Font.ColorIndex < 1 Then
'        unmatchedA = Application.WorksheetFunction.Trim(CellA)
'        unmatchedB = Application.WorksheetFunction.Trim(CellB)
        unmatchedA = CellA
        unmatchedB = CellB
        unmatPosA = 1   'Start comparing from begining i.e. entire text.
        unmatPosB = 1
        Call HighlightCellText(CellA)
        Call HighlightCellText(CellB)
        GoTo WordByWord
    End If
HighlightSuccessful:
    'All done - resetting error handling:
    On Error GoTo 0
End Sub
'Following hightlights the text of passed cell-range:
Sub HighlightCellText(rng As Range)
    With rng.Characters(1, Len(s1)).Font
        .ColorIndex = 3     'redCIndex
        .fontStyle = "Bold"
    End With
End Sub
'Following function removes the new line character chr(10) from both edges of the passed string then returns it.
Function TrimNewLineCharsFromEdges(ByVal s As String)
    While Right(s, 1) = Chr(10)
        s = Mid(s, 1, Len(s) - 1)
    Wend
    While Left(s, 1) = Chr(10)
        s = Mid(s, 2, Len(s))
    Wend
    TrimNewLineCharsFromEdges = s
End Function
Sub CheckWhiteSp()
    FindWhiteSpaceInString ("Hello " & Chr(10))
End Sub
'Following finds (and prints to Immediate window) any White-Spaces in passed string:
Sub FindWhiteSpaceInString(ByVal s As String)
    For i = 9 To 11
        If InStr(s, Chr(i)) <> 0 Then
            Debug.Print "Chr(" & i & ") found in string"
        End If
    Next i
    'Debug.Print InStr(1, [A1], Chr(13), vbTextCompare)
    'MsgBox InStr(1, [B1], Chr(13), vbTextCompare)
End Sub
'Usage: IsInArrayInt("item1", arrName) -> this will check if passed arg1 is in the array-2nd-arg. Cast-INsensitive for string-comparisons.
'Returns: Position As Integer (not index number), returns -1 if element not found in array.
'e.g. If (IsInArrayInt(Cells(1, c), arr)) Then
Function IsInArrayInt(ByVal stringToBeFound As Variant, ByVal arr As Variant) As Integer
    If UBound(arr) = -1 Then    'array is not initialised - if not checked here Match() will return "Type mismatch" error.
        IsInArrayInt = -1
        Exit Function
    Else
        If Not IsError(Application.Match(stringToBeFound, arr, 0)) Then
            IsInArrayInt = Application.Match(stringToBeFound, arr, 0)
        Else
            IsInArrayInt = -1
        End If
    End If
End Function
Sub IsInArrayIntTest()
    arr = Split("A B C D E F", " ")
    'MsgBox UBound(a) 'a(0)
    MsgBox IsInArrayInt("a", arr) '=> 1 as "a" is at 1st position (finds regardless of lower-case).
End Sub
'Following function replaces blank spaces and CrLf (with ¬ sign) from the passed string.
'Usage: cleanString = ReplaceWhiteSpaces(string_with_whitespaces)
Function ReplaceWhiteSpaces(ByVal s As String)
    s = Replace(s, Chr(10), "¬")
    s = Replace(s, Chr(13), "¬")
    s = Replace(s, Chr(32), "¬")
    ReplaceWhiteSpaces = s
End Function
'Following function compares two passed strings and return the postion of the first different character.
'Returns: 0 if no difference found.
'Usage: diffCharPosition = GetDiffCharPosition(string1, string2)
Function GetFirstDiffCharPosition(ByVal s1 As String, ByVal s2 As String)
    If StrComp(s1, s2, vbTextCompare) = 0 Then
        GetFirstDiffCharPosition = 0
    Else
        For i = 1 To Len(s1)
            If StrComp(Mid(s1, i, 1), Mid(s2, i, 1), vbTextCompare) <> 0 Then
                GetFirstDiffCharPosition = i
                Exit Function
            End If
        Next i
    End If
End Function
'Following function compares two passed strings and return the postion of the first different character.
'Returns: 0 if no difference found.
'Usage: diffLetterPosition = GetFirstDiffNonWhitespaceCharPosition(string1, string2)
Function GetFirstDiffNonWhitespaceCharPosition(ByVal s1 As String, ByVal s2 As String)
    If StrComp(s1, s2, vbTextCompare) = 0 Then
        GetFirstDiffNonWhitespaceCharPosition = 0
    Else
        i = 1: j = 1
        Do
            If StrComp(Mid(s1, i, 1), Mid(s2, j, 1), vbTextCompare) <> 0 Then
                Exit Do
            End If
            If Mid(s1, i, 1) = "¬" Then
                While Mid(s1, i, 1) = "¬": i = i + 1: Wend
            Else
                i = i + 1
            End If
            If Mid(s2, j, 1) = "¬" Then
                While Mid(s2, j, 1) = "¬": j = j + 1: Wend
            Else
                j = j + 1
            End If
        Loop While (i <= Len(s2)) Or (j <= Len(s2))
        GetFirstDiffNonWhitespaceCharPosition = i
    End If
End Function
'Following inserts a new sheet and copies the ActiveCell into new sheet's [A1] & [B1]:
Sub Copy2xCellsToNewSheet()
    Dim aCell As Range: Set aCell = ActiveCell
    Dim bCell As Range: Set bCell = ActiveCell.Offset(0, 1)
'    Set wbNew = Workbooks.Add
    Dim originalSheet As Worksheet: Set originalSheet = ActiveSheet
    Dim NewSh As Worksheet
    Set NewSh = Sheets.Add(after:=ActiveSheet)
    aCell.Copy
    With NewSh.[A1]
        .PasteSpecial xlPasteColumnWidths
        .PasteSpecial xlPasteFormats
        .PasteSpecial xlPasteValues
    End With
    bCell.Copy
    With NewSh.[B1]
        .PasteSpecial xlPasteColumnWidths
        .PasteSpecial xlPasteFormats
        .PasteSpecial xlPasteValues
    End With
    
    'Trimming Left & Right whitespace:
    [A1].Value = Trim([A1].Value)
    [B1].Value = Trim([B1].Value)
End Sub
'Following inserts a new temporary workbook and copies the ActiveCell to new sheet's [A1]:
Sub CurrCellToNewTempBook()
    Dim aCell As Range: Set aCell = ActiveCell
'    Dim bCell As Range: Set bCell = ActiveCell.Offset(0, 1)
    Dim NewWb As Workbook
    Set NewWb = Workbooks.Add
    Dim originalSheet As Worksheet: Set originalSheet = ActiveSheet
'    Dim NewSh As Worksheet
'    Set NewSh = Sheets.Add(after:=ActiveSheet)
    aCell.Copy
    With NewWb.Sheets(1).[A1]
        .PasteSpecial xlPasteColumnWidths
        .PasteSpecial xlPasteFormats
        .PasteSpecial xlPasteValues
    End With
    'bCell.Copy
    With NewWb.Sheets(1).[B1]
        .PasteSpecial xlPasteColumnWidths
'        .PasteSpecial xlPasteFormats
'        .PasteSpecial xlPasteValues
    End With
    
    'Trimming Left & Right whitespace:
    [A1].Value = Trim([A1].Value)
    '[B1].Value = Trim([B1].Value)
    'Checking Row-Height, if needed, increase it beyond Excel limits (for better readability):
    Call FixRowHeightLimit
End Sub
'Following sub merges two cells to increase/overcome the max Row-Height (409.5):
Sub FixRowHeightLimit()
    ActiveSheet.Cells.VerticalAlignment = xlTop
    Debug.Print [A1].RowHeight
    If [A1].RowHeight > 380 Then
        [A2].RowHeight = 409.5
        Range("A1:A2").Merge
        Range("B1:B2").Merge
    End If
    If [A3].RowHeight > 380 Then
        [A4].RowHeight = 409.5
        Range("A3:A4").Merge
        Range("B3:B4").Merge
    End If
    [B2].Select
End Sub
'Following sub checks the basic equality of the passed parameters and shows MsgBox accordingly:
Sub BasicEqualityCheck(ByVal txt1 As String, ByVal txt2 As String, trimmedTxt As Boolean)
    Dim trmMsg As String: trmMsg = ""
    If trimmedTxt Then
        trmMsg = ", however there are some variations in white-spaces."
    End If
    If txt1 = txt2 Then
        MsgBox "No difference found in text" + trmMsg, vbOKOnly & vbInformation, "Text-Diff Result"
        End
    End If
End Sub

'Following sorts the lines/values in [A1] & [B1] cells, then copies into [A3] & [B3] cells respectively:
Sub SortThenCopyA1B1ValuesToA3B3()
    Dim arr1() As String
    Dim arr2() As Variant
    'Creating a array list:
    Set arrList = CreateObject("System.Collections.ArrayList")
    
  '[A1] -> [A3]:
    'arr1() = Split([A1], Chr(10)) 'split by Soft-Enters / new-lines
    arr1() = Split([A1], vbLf) 'split by Soft-Enters / new-lines
    
    arrList.Clear   'Clearing Array-List
    ' adding each lines in the array_list:
    For Each element In arr1()
        arrList.Add element
    Next
    arrList.Sort    'sorting arrList
    arr2() = arrList.toarray  'Array-List elements into array.
    'MsgBox Join(arr2, vbLf)
    [A3] = Join(arr2, vbLf)
    
  '[B1] -> [B3]:
    arr1() = Split([B1], vbLf) 'split by Soft-Enters / new-lines
    ' adding each lines in the array_list:
    arrList.Clear   'Clearing Array-List
    For Each element In arr1()
        arrList.Add element
    Next
    arrList.Sort    'sorting arrList
    arr2() = arrList.toarray  'Array-List elements into array.
    [B3] = Join(arr2, vbLf)
End Sub



'------------x------------



Monday, 10 January 2022

Useful VBA Utilities for Microsoft Excel (Core Eddie-Code)


Useful VBA Utilities for Microsoft Excel (Core Eddie-Code):

Author: Eddie Kumar Shrivastava



'Useful VBA Utilities for Microsoft Excel (Core Eddie-Code):
'******************************************************************************************
'Written by:    Eddie Kumar Shrivastava
'Description:   Eddie-Tools & Utilities to speed up the data-processing/formatting etc. in the Excel.
'Readonly link: https://officeapptips.blogspot.com/2022/01/useful-vba-utilities-for-microsoft.html
'Editable-Repo: https://www.blogger.com/u/1/blog/post/edit/5593580733288490407/7516263398716064107
'Date Created:  16-01-2010
'Last Modified: 24-11-2023
'Version:       21.9
'Version info/Change Log/Subs-List:
'------------------------------------------------------------------------------------------
'             SUBs                      -  Keyboard-Shortcuts Assigned
'------------------------------------------------------------------------------------------
'''''''''''''''''''''''' LIST OF ALL SUBs & FUNCTIONs ''''''''''''''''''''''''
'-      Function GetExecutionDuration_TurnOnTimer(ByVal switch As Boolean)
'-      Sub Testing_GetExecutionDuration_TurnOnTimer()
'-      Function TurnOffOtherExcelActivities(switch As Boolean)
'-      Sub SetFullScreen(fullScr As Boolean)
'-      Function ColumnNumberToLetter(ByVal colNum As Integer)
'-      Function ColumnLetterToNumber(ByVal colLetter As String)
'-      Function ColumnNum2Letter(iCol As Integer)
'-      Function ColumnNum2Letter(iCol As Integer)
'-      Function GetColumnNumberByHeaderName(headerName As String)
'-      Function GetTargetColumn()
'-      Function hasAlphabetsOnly(s As String)
'-      Function hasAlphabets(s As String)
'-      Sub WriteToClipboard(ByVal text As String)
'-      Sub PasteFromClipboardToA1()
'-      Sub PastePlainText()
'-      Sub CreateCopyOfCurrentSheet()
'-      Sub DeleteAllButCurrentSheet()
'-      Sub DeleteAllButActiveSheet()
'-      Sub UnhideRows()
'-      Sub JumpToLastColumn()
'-      Sub ConfirmRun(subName)
'-      Sub SetFontCalibri10()
'-      Sub PasteAndFormatTable()
'-      Sub SplitColsAndFormat()
'-      Sub FormatEdTableWithHeader() / EdFormat
'-      Sub UnformatData()
'-      Sub FormatAbortedRows()
'-      Sub MaxColumnWide60()
'-      Sub ParseTextToDates()
'-      Sub DateFormattingForTime()
'-      Sub CheckIfDateHasTimePart()
'-      Sub GetValueFromClipboard()
'-      Sub CaptureAndResumeSheetView()
'-      Sub ColorShadeAlternateRows(rng As Range)
'-      Sub ListFiles()
'-      Sub 'Rename_Files()
'-      Function IsWorkBookOpen(fileName As String)
'-      Function SheetExists(n As String)
'-      Sub BulkConvertionFromXlsIntoXlsmOrXlsx()
'-      Sub ExportAsTxtTabDelimited()
'-      Sub ExportAsCSV()
'-      Sub ExportAsCSV_Old()
'-      Sub SaveAsXlsx() 'ExportAsXlsx()
'-      Sub SplitWorkbookExportSheetsAsXlsx()
'-      Function strLegalFileName(strFileNameIn As String)
'-      Sub CompareAandBColumnsHighlightNoMatches()       'At cell-level
'-      Sub CompareSelectedColumnsAndHighlightNoMatches()
'-      Sub CompareAndHighlightDiffs()                    'At character-level (advanced but slow)
'-      Sub DedupeKeywordsAndCopyInNewColumn()
'-      Sub DeDupContactsBeforeImport()
'-      Sub FilterAndShowRowsContainingSelectedText()  'or Sub FilterByWords-List()
'-      Sub FilterAndShowRowsContainingSelectedText__SingleWordSearch_Ver1()
'-      Sub FilterAndShowRowsContainingSelectedText_AppliesToEntireSheet_OLD()
'-      Sub FiterSouthEastAndLondonPostcodesOnly()
'-      Sub DeleteExtraWhitespaceBetweenWords()
'-      Sub DeleteEmptyCellsCollapseEntireTable()
'-      Sub DeleteEmptyCellsCollapseSingleColumn()
'-      Sub MoveAllDataInOneColumnA()
'-      Sub MoveCellValuesIntoColA()
'-      Sub DeleteEmptyRows()
'-      Sub DeleteEmptyColumns()
'-      Sub MoveRowToAnotherSheet()
'-      Sub MoveRowFromIncompleteToCompleteSheet()
'-      Sub SortByCountryUKonTop()
'-      Sub SortByCountryCode()
'-      Sub ChangeCase2ProperIf1stCharLower_OLDER()
'-      Sub SplitAndJoinArray()
'-      Sub ChangeCase2ProperIf1stCharLower()
'-      Function IsInArray(ByVal stringToBeFound As String, ByVal arr As Variant)
'-      Function IsInArrayInt(ByVal stringToBeFound As Variant, ByVal arr As Variant)
'-      Sub IsInArrayIntTest()
'-      Sub DeleteCols_RenameCols_SortByMailName()
'-      Sub DeleteCols4Communigator()
'-      Sub DeleteUnnecessaryCols()
'-      Sub Delete1AlphabetLongFirstName()
'-      Sub DelCol_FormatsForPostalMailingList()
'-      Sub SwapCol2with3()
'-      Sub DedupeOnEmail()
'-      Sub RenameTrilogyCols()
'-      Sub RenameCols_SortByMailName()
'-      Sub DelLenCols()
'-      Sub AddLenCols()
'-      Sub DelLenCols()
'-      Sub SortDesc()
'-      Sub ValidateTelNumber()
'-      Sub ValidateEmail()
'-      Sub HighlightCell(cellAddr As Range)
'-      Sub ValidateIfEUCountries()
'-      Sub ValidateHighlightEUCountries()
'-      Sub TestLikeOperator()
'-      Sub ValidateCountryNames()
'-      Function EdFuzzyMatch(s1 As String, s2 As String)
'-      Sub MatchFunctionErrorHandlingTest()
'-      Sub CollapseTableStructureLeftAglignData_byNavigating()
'-      Sub CollapseTableStructureLeftAglignData()
'-      Sub DuplicateRowFinder()
'-      Sub InsertUniqueValues_RemoveDupes()
'-      Sub BouncedEmailListUpdate()
'-      Sub UnsubscribedEmailListUpdate2()
'-      Sub UnsubscribedEmailListUpdate()
'-      Sub goto_curr_week()
'-      Sub InsertSubTotalOfVisibleOnlyCells()
'-      Sub DedupeRowsBy1PositionsPerOrganisation()
'-      Sub DedupeSubscribersWithBothInvoiceAndDeliveryAddr()
'-      Sub DedupeRowsBySimpleNextPreviousValueComparison()
'-      Sub DedupeAddressInvoiceVsDeliveryV1()
'-      Sub DedupeRowsBySimpleNextPreviousValueComparison()
'-      Sub DedupeAddressInvoiceVsDeliveryV2()
'-      Sub DedupeRowsBySimpleNextPreviousValueComparison()
'-      Sub DedupeAddressInvoiceVsDeliveryV3()
'-      Sub CheckIfArrayElementInitialized()
'-      Sub CheckIfVariableAssigned()
'-      Sub CheckEmptyString()
'-      Sub FirstAndLastRowsInUsedRange()
'-      Sub HideMultipleRowsAtOnce()
'-      Sub FindInUnionedRange()
'-      Sub SpecialCellsTest()
'-      Function NextColourIndex()
'-      Sub InitBrightColourIndex()
'-      Sub InitDarkColourIndex()
'-      Sub DeleteDrawingObjects()
'-      Sub StratifyByYears()
'-      Sub CopyColumnValuesOnlyInEmptyCells()
'-      Sub FindAndReplaceKeysWithValues()
'-      Sub SplitJoinFunctionsCheck()
'-      Sub MonthlyReportSortBySubs()
'-      Sub MonthlyReportSortBySubs_WithLDMH()
'-      Sub ExtractFirstName()
'-      Sub ExtractSubstring()
'-      Sub SortByColour()
'-      Function ColumnsToText(rng As Range, Optional DelimitBy As String = " ")
'-      Sub ConcatenateLeftCells()
'-      Sub ConcatenateLeftCellsTillLastRow()
'-      Sub ColumnToCommaSeparatedValuesIntoNextColumn()
'-      Sub DeleteIfNextCellIsEmpty()
'-      Sub DeleteIfNextCellHasValue()
'-      Sub CopyXlsbFileIntoUsersAppDataFolder()
'-      Function CreateFolderCheckIfNotExists(folderNameWithFullPath As String)
'-      Sub AddBouncedEmailsPercentColumn()
'-      Sub BringLastStaggeredColumnsValuesToFirstColumnA()
'-      Function GetLastRow(Sh As Worksheet)
'-      Function GetLastCol(Sh As Worksheet)
'-      Sub CopyRangeFromMultiWorksheets()
'-      Sub ApplyColourScaleOnColumn(c As String)
'-      Sub SplitRowsIn1000sAndExportAsCSV()
'-      Sub ExportCurrSheetAsCSV_WithoutPrompt(wBookPath As String)
'-      Sub SimpleRangeCheck()
'-      Sub Select1000Rows()
'-      Sub SpecFormatTmp()
'-      Sub GetSpecId()
'-      Sub NonStandardCharactersCheckInSheet(), NonStandardCharactersCheckInSpecifiedColumn()
'-      Sub NonStandardCharactersCheckInSheet()  'Sub SpotDodgyCharsInSheet()
'-      Sub NonStandardCharactersCheckInSpecifiedColumn()     'SpotDodgyCharsInSpecCol()
'-      Sub SpotDodgyCharPositionInTargetCell()
'-      Sub SpotDodgyCharPositionInTargetCell_CheckOnly()
'-      Sub StatusBarExample()
'-      Sub ProgressBarUsingForm()
'-      Sub ShowProgressBar()
'-      Sub ShowProgressBar()
'-      Sub ProgressBarUsingForm()
'-      Sub PrintArrayIn1Cell(myArr As Variant, cell As Range)
'-      Sub PrintArrayInRange(myArr As Variant, cell As Range)
'-      Sub TestPrintArrayIntoSheet()
'-      Sub UnassignedVsEmptyVsNull()
'-      Sub ErrHandlerInLoop()
'-      Sub ErrHandler()
'-      Sub SleepTest()
'-      Sub WaitTest()
'-      Sub getPathOfCurrentFile()
'-      Sub CompareAndHighlightDiffs()
'-      Sub CompareAndHighlightPassedValues(CellA As Range, CellB As Range)
'-      Sub HighlightCellText(rng As Range)
'-      Function TrimNewLineCharsFromEdges(ByVal s As String)
'-      Sub CheckWhiteSpaceInString()
'-      Sub FindWhiteSpaceInString(ByVal s As String)
'-      Function ReplaceWhiteSpaces(ByVal s As String)
'-      Function GetFirstDiffCharPosition(ByVal s1 As String, ByVal s2 As String)
'-      Function GetFirstDiffNonWhitespaceCharPosition(ByVal s1 As String, ByVal s2 As String)
'-      Sub Copy2xCellsToNewSheet()
'-      Sub CurrCellToNewTempBook()
'-      Sub FixRowHeightLimit()
'-      Sub BasicEqualityCheck(ByVal txt1 As String, ByVal txt2 As String, trimmedTxt As Boolean)
'-      Sub SortThenCopyA1B1ValuesToA3B3()

'''''''''''''''''''''''' RE-USABLE ED-LIBS / EdFunctions / UTILS ''''''''''''''
'-   Sub x()             - Ctrl + Shift + F
'''''''''''''''''''''''' QUICK EXPORTS ''''''''''''''''''''''''
'-   Sub x()             - Ctrl + Shift + Alt + Del
'''''''''''''''''''''''' Ed-Useful-Excel-VBA-Code-Sample / Extras ''''''''''''''''
'-   Sub y()             -

'******************************************************************************************



''Following module-level global vars are used in Sub InitBrightColourIndex() and NextColourIndex() subs
Public BrightColourIndexes() As Variant
Public DarkColourIndexes() As Variant
Private colourIdx As Integer
'Private colourIdx As Integer: colourIdx = 4

'Following global variable is used by Function GetExecutionDuration_TurnOnTimer()
Public startTime As Single

'Following global declaration is required by the Sleep() function:
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal milliseconds As LongPtr) 'needed by Sleep() function - for Office 64 Bit




'''''''''''''''''''Useful one-liners:
    'MsgBox CreateObject("Scripting.FileSystemObject").GetFolder("C:\tmp").Files.count      'returns file-counts in folder
    'usrDesktopFolder = CreateObject("WScript.Shell").specialfolders("Desktop")             'returns user's Desktop folder as String


'These Modular / Global level variables must be declared ABOVE the subs they are being used within.
''Capturing current status:
''Following module-level global vars are used in sub FormatEdTableWithHeader()  'NOT CAPTURING FOR BETTER PORTABILITY OF THE CODE
    ' Private sheetIdx As Integer
    ' Private cellAddr As String
    ' Private scrollRows As Integer
    ' Private scrollCol As Integer
    ' currCalc = Application.Calculation
    ' currStatusBar = Application.DisplayStatusBar
    ' currEvents = Application.EnableEvents
    ' currPageBreaks = ActiveSheet.DisplayPageBreaks


'''''''''''''''''''''''''''''''''''''
'Following function, when called passing True will turn-on the stop-watch, and when called passing False will return the Lapsed time (since the start-time)
'Usage:
'   GetExecutionDuration_TurnOnTimer(True) 'turn on stop-watch.
'   MsgBox ("your_message_here." & vbNewLine & vbNewLine & "The code took " & GetExecutionDuration_TurnOnTimer(False) & " seconds to run.")
Function GetExecutionDuration_TurnOnTimer(ByVal switch As Boolean)
'Following variable is declared at global level:
'Public startTime As Single
    If switch Then
        startTime = Timer                               ' Get start time.
    Else
        Dim endTime As Single
        endTime = Timer                                 ' Get end time.
        GetExecutionDuration_TurnOnTimer = Format(endTime - startTime, "Fixed") 'returning lapsed time.
    End If
End Function
Sub Testing_GetExecutionDuration_TurnOnTimer()
    GetExecutionDuration_TurnOnTimer (True)
    MsgBox "wait a sec."
    MsgBox "you waited: " & GetExecutionDuration_TurnOnTimer(False) & " seconds."
End Sub


'Following function toggles all (possible) activities of Excel except running VBA code in order to speed up the execution.
'ARGUMENTS:
    'True: turns off other activities.
    'False: turns on other activities.
'USAGE:
    'TurnOffOtherExcelActivities(True)  OR  TurnOffOtherExcelActivities(False)
'Credit/idea taken from: "http://datapigtechnologies.com/blog/index.php/ten-things-you-can-do-to-speed-up-your-excel-vba-code/"
Function TurnOffOtherExcelActivities(switch As Boolean)
    If Not Application.ActiveProtectedViewWindow Is Nothing Then
        Application.ActiveProtectedViewWindow.Edit
    End If
    If switch Then
        Application.Calculation = xlCalculationManual
        Application.EnableEvents = False
        Application.ScreenUpdating = False
        Application.DisplayStatusBar = False
        Application.DisplayAlerts = False
'        If ActiveSheet.DisplayPageBreaks Then
'            ActiveSheet.DisplayPageBreaks = False
'        End If
    Else
        Application.Calculation = xlCalculationAutomatic
        Application.EnableEvents = True
        Application.ScreenUpdating = True
        Application.DisplayStatusBar = True
        Application.DisplayAlerts = True
'        ActiveSheet.DisplayPageBreaks = True
    End If
End Function

'Use following to enable the presentation mode / full-screen: (Note: Disable script is placed in 'UnhindeRows' sub)
'Usage: SetFullScreen(True) -- this will enable the Excel's full screen.
Sub SetFullScreen(fullScr As Boolean)
    If fullScr Then
        ActiveWindow.DisplayGridlines = False
        ActiveWindow.DisplayHeadings = False
        Application.DisplayFormulaBar = False
        Application.DisplayFullScreen = True
    Else
        ActiveWindow.DisplayGridlines = True
        ActiveWindow.DisplayHeadings = True
        Application.DisplayFormulaBar = True
        Application.DisplayFullScreen = False
    End If
End Sub

'Column Number to Column Name/Letters:
'Usage: MsgBox ColumnNumberToLetter(1)
Function ColumnNumberToLetter(ByVal colNum As Integer)
   ColumnNumberToLetter = Split(Cells(, colNum).Address, "$")(1)
End Function

'Column Column Name/Letters to Number:
'Usage: MsgBox ColumnLetterToNumber("AA")
Function ColumnLetterToNumber(ByVal colLetter As String)
    ColumnLetterToNumber = Range(colLetter & 1).Column
End Function
'------------https://support.microsoft.com/en-gb/kb/833402
'Function ColumnNum2Letter(iCol As Integer) As String
Function ColumnNum2Letter(iCol As Integer) As String
   Dim iAlpha As Integer
   Dim iRemainder As Integer
   iAlpha = Int(iCol / 27)
   iRemainder = iCol - (iAlpha * 26)
   If iAlpha > 0 Then
      ColumnNum2Letter = Chr(iAlpha + 64)
   End If
   If iRemainder > 0 Then
      ColumnNum2Letter = ColumnNum2Letter & Chr(iRemainder + 64)
   End If
End Function


'This function returns the column-number of the column that contains "email" or "userdefined4" in its header, returns 0 otherwise.
'ASSUMPTION:
    'This function assumes that caller table has a header row on top within used-range (it may have empty rows above).
'USAGE:
'    Dim emailCol As Integer: emailCol = GetColumnNumberByHeaderName("email")  'if not found then 0 is returned.
Function GetColumnNumberByHeaderName(headerName As String)
    Dim lastCol, c  As Integer: lastCol = Cells(ActiveSheet.UsedRange.Row, Columns.count).End(xlToLeft).Column
    GetColumnNumberByHeaderName = 0
    For c = 1 To lastCol
        'Email: If InStr(1, CStr(Cells(1, c)), "email", vbTextCompare) > 0 Or InStr(1, CStr(Cells(1, c)), "userdefined4", vbTextCompare) > 0 Then
        If InStr(1, CStr(Cells(1, c)), headerName, vbTextCompare) > 0 Then
            GetColumnNumberByHeaderName = c
            Exit For
        End If
    Next c
End Function

'This function prompts user to input a column-letter, then returns containing column-letter a string
  ' in case of error (or if user's empty-entry) it returns an empty-sting.
'USAGE:
'    Dim strCol As String: strCol = GetTargetColumn()
'    If strCol = "" Then
'        End
'    End If
Function GetTargetColumn()
    Dim col As Variant
GetCol:
    col = Trim(InputBox(Prompt:="Enter the target Column-Letter (such as 'A' for 1st Column)"))    'Without "APPLICATION." Cancel button retuns empty-string AND arrows-key becomes deactive hence don't reference cell-addresses.
    'col = Trim(Application.InputBox(Prompt:="Enter the target Column-Letter (such as 'A' for 1st Column)")) 'With "APPLICATION." Cancel button retuns False AND pressing arrows-keys insert cell-references in the Inputbox.
    'Debug.Print TypeName(col) & " - " & col
    If col = False Then  'user cancelled - clicking 'Cancel' button returns Boolean-False (NOT vbCancel).
        GetTargetColumn = ""
    ElseIf col = "" Then    'user clicked OK without inputing anything, returns empty-string "".
        MsgBox "No text! you didn't enter anything."
        GetTargetColumn = ""
    ElseIf IsNumeric(col) Or Len(col) > 2 Or IsNumeric(Left(col, 1)) Or IsNumeric(Right(col, 1)) Then
        MsgBox "Invalid column letter, please enter only text - max 2 letters (not a number, space, special chars etc.)"
        GoTo GetCol
    Else
        GetTargetColumn = col
    End If
End Function


'This function returs true if passed argument only contains alphabets i.e. arg Like "[a-zA-Z]+"
'Usage:    MsgBox hasAlphabets("abcd32")
Function hasAlphabetsOnly(s As String) As Boolean
    hasAlphabetsOnly = True
    For intPos = 1 To Len(s)
        Select Case Asc(Mid(s, intPos, 1)) 'If Not Mid(s, intPos, 1) Like "[a-zA-Z]" Then
            Case 65 To 90, 97 To 122
                'hasAlphabetsOnly = True
            Case Else
                hasAlphabetsOnly = False
                Exit For
        End Select
    Next intPos
End Function
'This function returs true if passed argument only contains alphabets i.e. arg Like "[a-zA-Z]+"
'Usage:    MsgBox hasAlphabets("abcd32")
Function hasAlphabets(s As String) As Boolean
    hasAlphabets = True
    For intPos = 1 To Len(s)
        If Not Mid(s, intPos, 1) Like "[a-zA-Z]" Then
                hasAlphabets = False
                Exit For
        End If
    Next intPos
End Function

'Following writes passed argument into the Clipboard.
'Usage:     WriteToClipboard "asdf"
Public Sub WriteToClipboard(ByVal text As String)
    CreateObject("htmlfile").ParentWindow.ClipboardData.SetData "text", text
End Sub

'Following pastes the Clipboard contents from the range [A1] (normally splitting columns by Tabs):
Sub PasteFromClipboardToA1()
    ' Error handler
    On Error GoTo PasteFailed

    Range("A1").Select
    ActiveSheet.Paste
    
    ' Error-Handler Label:
PasteFailed:
End Sub

'Following pastes the Clipboard contents as plain-text:
Sub PastePlainText()
    'PastePlainText
    Selection.PasteSpecial xlPasteValues
End Sub


'Creates copy of current sheet and places next to it.
Sub CreateCopyOfCurrentSheet()
    Dim sName As String: sName = ActiveSheet.name
    If Len(sName) >= 30 Then
        sName = Left(sName, Len(sName) - 3)
        While SheetExists(sName)
            sName = sName & WorksheetFunction.RandBetween(0, 999)
        Wend
        ActiveSheet.name = sName
    End If
    ActiveSheet.Copy after:=ActiveSheet
    'Sheets(sName).Copy after:=Sheets(sName)
End Sub


'Use with caution: Delete all sheets except current sheet:
Sub DeleteAllButCurrentSheet()
    Dim currSheetName As String: currSheetName = ActiveSheet.name
    Dim ans As String
    ans = MsgBox("BEWARE:" & vbNewLine & vbNewLine & "Are you sure you want to proceed with deletion of all sheets except current sheet: """ & currSheetName & """", vbYesNo + vbCritical + vbDefaultButton2, "Confirm Sheets Deletion")
    If ans = vbNo Then
        End
    End If
'    On Error Resume Next
'    ActiveWorkbook.Save 'just in case if undo of deletion is needed.
'    On Error GoTo 0
'   Sub DeleteAllButActiveSheet()
    Application.DisplayAlerts = False
    For Each ws In Worksheets
      If currSheetName <> ws.name Then
        ws.Delete
      End If
    Next ws
    Application.DisplayAlerts = True
    'MsgBox ("Deletion complete.  If unsure SaveAs a different file-name to preserve the deleted sheets in original file.")
End Sub


'Follwoing unhides all (hidden) rows in current sheet.
Sub UnhideRows()
  'use following to disable the presentation mode / full-screen:
    ActiveWindow.DisplayGridlines = True
    ActiveWindow.DisplayHeadings = True
    Application.DisplayFormulaBar = True
    Application.DisplayFullScreen = False
    'CommandBars.ExecuteMso "HideRibbon"    'use DisplayFullScreen instead.
    Rows("1:" & Cells.Rows.count).EntireRow.Hidden = False
'    Call ConfirmRun("UnhideRows")
'    Dim currRow As Long
'    currRow = ActiveCell.row
'    Rows("1:" & ActiveSheet.UsedRange.Rows.count).EntireRow.Hidden = False
'    Rows(currRow).EntireRow.Select
End Sub

'Following code moves the cursor to the last column of the current row.
Sub JumpToLastColumn()
    Dim didSelect As Boolean: x = False
    On Error Resume Next
    'Cells(ActiveCell.Row, Columns.count).End(xlToLeft).Offset(0, -1).Select
    didSelect = Cells(ActiveCell.Row, Columns.count).End(xlToLeft).Select
    Debug.Print (x)
    On Error GoTo 0
    If didSelect = False Then
        MsgBox "Worksheet is empty."
    End If
End Sub

Sub ConfirmRun(subName)
    Dim ans As String
    ans = MsgBox("Would you like to continue with """ & subName & """", vbYesNo, "Confirm Execution")
    If ans = vbNo Then
        End
    End If
End Sub

'Sets Font:Calibri size:10 for whole (immediate) table:
Sub SetFontCalibri10()
    ActiveSheet.UsedRange.Select
    Selection.Font.name = "Calibri"
    Selection.Font.Size = 10
    'Selection.HorizontalAlignment = xlLeft
    'Range("A1").Select
    Selection(1).Select
End Sub

'Paste from clipboard then call FormatEdTableWithHeader() sub to format table (used for DU-Output):
Sub PasteAndFormatTable()
    'Paste from Clipboard:
    Range("A1").Select
    ActiveSheet.Paste

    Call FormatEdTableWithHeader

    'Copy to Clipboard:
    ActiveSheet.UsedRange.Select
    Selection.Copy
End Sub


'Splits text-to-columns then calls FormatEdTableWithHeader() sub to format table (used for DU-Output):
Sub SplitColsAndFormat()
    'Sheets("DU_Check_Automation").Select
    Range("A1").Select
    Range(Selection, Selection.End(xlDown)).Select
    Selection.TextToColumns Destination:=Range("A1"), DataType:=xlDelimited, _
        TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=True, _
        Semicolon:=False, Comma:=False, Space:=False, Other:=False, TrailingMinusNumbers:=True
         ', FieldInfo:=Array(Array(1, 1), Array(2, 1), Array(3, 1))
    Call FormatEdTableWithHeader
    Columns("C").HorizontalAlignment = xlCenter
End Sub

'Following code formats current table, follwoing elements are formatted:
'Borders, FontStyle, FontWeight, Alignment, it also Freezes top row / top-pane & sets margins.
Sub FormatEdTableWithHeader()
    'Dim re As Object: Set re = CreateObject("VBScript.RegExp")
    'Dim LastCol As Integer: LastCol = Cells(1, Columns.count).End(xlToLeft).Column
    Dim validDate As Boolean
    Dim lastCol As Integer: lastCol = GetLastCol(ActiveSheet)
    'Dim lastRow As Double: lastRow = Cells(Rows.count, 1).End(xlUp).row
    'Dim rng As Range: Set rng = Range(Cells(1, 1), Cells(lastRow, lastCol))
    'Dim rng As Range: Set rng = Range(ActiveSheet.UsedRange.Cells(1, 1), ActiveSheet.UsedRange.SpecialCells(xlCellTypeLastCell))
    Dim rng As Range: Set rng = ActiveSheet.UsedRange

    'Call CaptureAndResumeSheetView 'Note: if ScreenUpdating is turned off at the begining of your code, then you may not need to use this sub.
'    Debug.Print sheetIdx
'    Debug.Print cellAddr
'    Debug.Print scrollRows
'    Debug.Print scrollCol

    Call DeleteEmptyRows
    Cells.Replace What:="NULL", Replacement:="", LookAt:=xlWhole, MatchCase:=False 'replacing NULLs.

   'Freezing top row - this MUST be done BEFORE ScreenUpdating is switched off, otherwise ActiveCell gets frozen.
    Rows("2:2").Select
    ActiveWindow.FreezePanes = True

    Application.ScreenUpdating = False

    'Background Colour of Header/1st Row:
    'Range(Cells(1, 1), Cells(1, lastCol)).Interior.ColorIndex = 19
    Range(Cells(1, 1), Cells(1, lastCol)).Interior.Color = RGB(234, 255, 245)

    ActiveSheet.UsedRange.WrapText = False
    ActiveSheet.UsedRange.MergeCells = False

   'Border & FontStyle:
    'With ActiveSheet.UsedRange
    With rng
        .Font.name = "Calibri"
        .Font.Size = 10.5
        .Borders.LineStyle = xlContinuous
        .EntireRow.AutoFit
    End With
    
   'FontWeight & Alignment:
    With Rows("1:1")
        .Font.Bold = True
        .HorizontalAlignment = xlCenter
        .VerticalAlignment = xlBottom
        '.WrapText = True
    End With
   
   'Set margins: Uncomment following if paper-printing is needed:
'    With ActiveSheet.PageSetup
'        .LeftMargin = Application.InchesToPoints(0.25)
'        .RightMargin = Application.InchesToPoints(0.25)
'        .TopMargin = Application.InchesToPoints(0.65)
'        .BottomMargin = Application.InchesToPoints(0.65)
'    End With

    'MsgBox "ResumeSheetView"
    'ResumeSheetView:

    'Call ColorShadeAlternateRows(rng) 'colour shades alternate rows

    'Resuming the initial view of sheet:
'    Sheets(sheetIdx).Select
'    Range(cellAddr).Select
'    ActiveWindow.ScrollRow = scrollRows
'    ActiveWindow.ScrollColumn = scrollCol
    
    'Cells.EntireColumn.AutoFit
    
     'Formatting Date columns e.g. to "dd/mm/yyyy" OR "dd/mm/yyyy hh:mm:ss":
    On Error Resume Next
    Err.Clear
    For c = 1 To Cells(1, Columns.count).End(xlToLeft).Column
        validDate = False
        'If InStr(1, CStr(Cells(1, c)), "date", vbTextCompare) Then
        '    Columns(c).NumberFormat = "dd/mm/yyyy"
        'End If
        
            tVal = CDate(Cells(2, c))  'tVal = CDate(44294.453605787)
            validDate = IsDate(tVal)
            If (Err.Number <> 0 Or Len(Trim(Cells(2, c))) = 0) Then
                Err.Clear
                GoTo NextCol
            End If

            If (validDate) Then
                'If (Year(tVal) < 1936 Or Year(tVal) > 2036) Then
                If (Year(tVal) < 2019 Or Year(tVal) > 2030) Then
                    validDate = False
                End If
            End If
            'Debug.Print validDate
            'Debug.Print tVal
            'Debug.Print CDbl(tVal)
            'If Fix(tVal) = tVal Then
            '    Debug.Print "No decimal places"
            'Else
            '     Debug.Print "Yes has decimal places"
            'End If
    'End
        If (validDate) Then
            If Fix(tVal) = tVal Then 'No decimal places
                Columns(c).NumberFormat = "dd/mm/yyyy"
            Else  'has decimal places
                'Checking for Milliseconds:  TODO
                'Cells(2, c).NumberFormat = "dd/mm/yyyy hh:mm:ss.000"
                'Debug.Print Right(CStr(Cells(2, c)), 3)  'doesn't work excel trims the ms
                Columns(c).NumberFormat = "dd/mm/yyyy hh:mm:ss"         'without ms
                'Columns(c).NumberFormat = "dd/mm/yyyy hh:mm:ss.000"    'with ms
            End If
            're.Pattern = "\d{1,2}[\/-]\d{1,2}[\/-]\d{1,4}"                            'to match: "07/06/2022"
            'If re.Test(tVal) Then
            '    Columns(c).NumberFormat = "dd/mm/yyyy"
            'End If
            're.Pattern = "\d{1,2}[\/-]\d{1,2}[\/-]\d{1,4}[ ]\d{1,2}:\d{1,2}:\d{1,2}"  'to match: "07/06/2022  09:23:26"
            'If re.Test(tVal) Then
            '    Columns(c).NumberFormat = "dd/mm/yyyy hh:mm:ss"
            'End If
        End If
NextCol:
    Next c
    On Error GoTo 0
   
    'Formatting columns e.g. price to Currency:
    For c = 1 To Cells(1, Columns.count).End(xlToLeft).Column
        'If IsNumeric(Cells(2, c)) Then
        If InStr(1, CStr(Cells(1, c)), "amount", vbTextCompare) Or InStr(1, CStr(Cells(1, c)), "price", vbTextCompare) Then
            'Columns(c).Style = "Currency"
            Columns(c).NumberFormat = "£###,###,###,##0.00"
        End If
        If InStr(1, CStr(Cells(1, c)), "tel", vbTextCompare) _
          Or InStr(1, CStr(Cells(1, c)), "phone", vbTextCompare) _
          Or InStr(1, CStr(Cells(1, c)), "fax", vbTextCompare) _
          Or InStr(1, CStr(Cells(1, c)), "isbn", vbTextCompare) Then
            Columns(c).HorizontalAlignment = xlRight
            Cells(1, c).HorizontalAlignment = xlCenter
        End If
    Next c
    Call MaxColumnWide60
    'ActiveSheet.UsedRange.AutoFilter 'turn on the AutoFilter
    'ActiveSheet.Cells.Find What:="", SearchDirection:=xlNext, Lookat:=xlPart
    'Cells.EntireColumn.AutoFit    'AutoFit column width
    Application.ScreenUpdating = True
End Sub

'Following removes/reverses all formatting done by Sub FormatEdTableWithHeader()
Sub UnformatData()
    Cells.Select
    ActiveWindow.FreezePanes = False
    With Selection.Interior
        .Pattern = xlNone
        .TintAndShade = 0
        .PatternTintAndShade = 0
    End With
    Cells.FormatConditions.Delete
    Selection.ColumnWidth = 8.43
    Selection.Font.Size = 11
    'removing border lines:
    With Selection
        .Borders(xlDiagonalDown).LineStyle = xlNone
        .Borders(xlDiagonalUp).LineStyle = xlNone
        .Borders(xlEdgeLeft).LineStyle = xlNone
        .Borders(xlEdgeTop).LineStyle = xlNone
        .Borders(xlEdgeBottom).LineStyle = xlNone
        .Borders(xlEdgeRight).LineStyle = xlNone
        .Borders(xlInsideVertical).LineStyle = xlNone
        .Borders(xlInsideHorizontal).LineStyle = xlNone
'        .WrapText = False
'        .MergeCells = False
    End With
    Range("A1").Select
End Sub

'Following script ensures to keep the aborted rows only (and deletes other error-status such as OK/Completed etc.)
'it also formats the aborted rows.
Sub FormatAbortedRows()
    Cells.Replace What:="=-1=RequestCompleted", Replacement:="1=RequestCompleted", LookAt:=xlWhole, _
        SearchOrder:=xlByColumns, MatchCase:=False, SearchFormat:=False, ReplaceFormat:=False, FormulaVersion:=xlReplaceFormula2
    
    'Selection.AutoFilter  'AutoFilter to hide "Aborted" rows (to preserve them)
    'Improvements: now finds 'Error' (which is then used for Field:=# below) and finds active/used range for autofilter:
    
    colNumDur = Application.Match("Duration", ActiveSheet.Rows(1), 0)
    Columns(colNumDur).NumberFormat = "hh:mm:ss;@"
    
    colNumErr = Application.Match("Error", ActiveSheet.Rows(1), 0)
    ActiveSheet.UsedRange.AutoFilter Field:=colNumErr, Criteria1:=Array("0=OK", "1=Error", "1=RequestCompleted"), Operator:=xlFilterValues
    Rowz = ActiveSheet.AutoFilter.Range.Columns(1).SpecialCells(xlCellTypeVisible).Cells.count - 1
    If (Rowz > 0) Then
        Range("A2").Select  'exclude the header-row
        Range(Selection, ActiveCell.SpecialCells(xlLastCell)).Select 'Ctrl+End'
        Selection.SpecialCells(xlCellTypeVisible).Select             'Alt+; to select only visible rows'
        Selection.EntireRow.Delete
    End If
    Selection.AutoFilter
    
    Call FormatEdTableWithHeader
    Range("A1").Select
End Sub

'Sets max column-wide to 60, checks ALL columns in sheet:
Sub MaxColumnWide60()
    Cells.Columns.AutoFit  'Autofit all columns.
    'Enforce max column-wide to 60:
    For c = 1 To Cells(1, Columns.count).End(xlToLeft).Column
        If Columns(c).ColumnWidth > 60 Then
            Columns(c).ColumnWidth = 60
        End If
    Next c
End Sub


'DATE HANDLING:


'Following converts / attempts to parse text (in selected cells) into dates.
'Beware: the date-value (converted from text) is overwritten to the containing cell.
'Sub ConvertTextToDates()
Sub ParseTextToDates()
    If IsEmpty(ActiveCell) Then
        MsgBox "Empty Cell !" & vbNewLine & vbNewLine & "Cannot convert value into date type as no value found in the selected cell."
        End
    End If
    Dim r As Range
    For Each r In Selection
        r.Value = CDate(r.text)
    Next r
End Sub

Sub DateFormattingForTime()
    MsgBox Format(Date, "dd-MM-yyyy_hhmmss")  'Date returns: no-time no-time no-time no-time ye ye ye ye
    MsgBox Format(Now, "MM/dd/yyyy h:mm:ss tt") 'Now returns: both Date & Time (but without Milliseconds).
End Sub

'Following checks if a date-value also contains the Time-part:
Sub CheckIfDateHasTimePart()
    On Error Resume Next
        tVal = CDate(44294.7050769444)
'        tVal = CDate(44294.453605787)
'        tVal = CDate(44294)
        'tVal = CDate(Cells(2, 1))
        validDate = IsDate(tVal)
        If (validDate) Then
            If (Not (Year(tVal) >= 1936 And Year(tVal) <= 2036)) Then  'excluding unwanted Number to Date conversions.
                validDate = False
            End If
        End If
        Debug.Print "Value passed in " & tVal
        Debug.Print "Is valid date?  " & validDate
        Debug.Print "Value as number " & CDbl(tVal)
        If Fix(tVal) = tVal Then
            Debug.Print "No decimals found, meaning it contains only Date-part."
        Else
            Debug.Print "Yes it has decimal places, meaning it contains Time-part too"
            'Checking for Milliseconds:
            'if () then
            Debug.Print "Formatted: " & Format(tVal, "dd/mm/yyyy hh:mm:ss.000")
            Debug.Print "Current Date/Time stamp with Milliseconds: " _
                        & Strings.Format(Now, "dd-MMM-yyyy HH:nn:ss") & "." _
                        & Strings.Right(Strings.Format(Timer, "#0.000"), 3)
            'Debug.Print tVal.NumberFormat = "dd/mm/yyyy hh:mm:ss.000"
            
        End If
    On Error GoTo 0
End Sub




'CLIPBOARD HANDLING:

'Following reads content from the Windows clipboard.
Sub GetValueFromClipboard()
    Dim DataObj As Object: Set DataObj = CreateObject("MSForms.DataObject")  'early binding.
    'Dim DataObj As MSForms.DataObject: Set DataObj = New MSForms.DataObject 'late binding.
    DataObj.GetFromClipboard
    strPaste = DataObj.GetText(1)
    Debug.Print strPaste
End Sub



'Following Sub captures the SheetView, by storing various screen-positions in some Module/Global-level variables,
'Usage: Call this sub at the begining of your code, then move around freely in the sheet, then at the end/bottom
'of your code use these global-variable to position your screen back to where it was (before running VBA code).
'Note: if ScreenUpdating is turned off at the begining of your code, then you may not need to use this sub.
Sub CaptureAndResumeSheetView()
    sheetIdx = ActiveSheet.Index
    cellAddr = ActiveCell.Address
    scrollRows = ActiveWindow.ScrollRow
    scrollCol = ActiveWindow.ScrollColumn
End Sub
'Following colour shades the alternate rows:
Sub ColorShadeAlternateRows(rng As Range)
    Dim lastCol As Integer
    'Set Rng = ActiveSheet.UsedRange
    'Rng.Interior.ColorIndex = xlNone
    rng.FormatConditions.Add Type:=xlExpression, Formula1:="=mod(row()+1,2)"
    'Rng.FormatConditions(1).Interior.ColorIndex = 19
    rng.FormatConditions(1).Interior.Color = RGB(224, 235, 235)
    lastCol = Cells(1, Columns.count).End(xlToLeft).Column
    Range(Cells(1, 1), Cells(1, lastCol)).Interior.Color = RGB(236, 249, 236)
End Sub


Sub ListFiles()
    Dim MyFolder As String: MyFolder = "C:\Users\ashrivastava\Downloads\"
    Dim MyFile As String: MyFile = Dir(MyFolder & "*.xls")
    Dim a As Integer: a = 0
    Do While MyFile <> ""
        a = a + 1
        Cells(a, 1).Value = MyFile
        MyFile = Dir
    Loop
    MsgBox "Existing filenames are now listed in Column 'A', now type new filenames in Column 'B' (for each file)." & vbCrLf & _
        "Enter valid filenames, then Run Sub 'Rename_Files()' when ready."
End Sub
'Following function helps to determine if an Excel workbook exists,
'Note: An attempt to open non-existant workbook gives error "out of subscript".
Function IsWorkBookOpen(fileName As String)
    Dim ff As Long, ErrNo As Long
    On Error Resume Next
    ff = FreeFile()
    Open fileName For Input Lock Read As #ff
    Close ff
    ErrNo = Err
    On Error GoTo 0
    Select Case ErrNo
        Case 0:    IsWorkBookOpen = False
        Case 70:   IsWorkBookOpen = True
        Case Else: Error ErrNo
    End Select
End Function
'''usage:
'    Dim fName As String: fName = "C:\SPG\process.xlsm"
'    Dim wbk, currBook As Workbook: Set currBook = ActiveWorkbook
'    If Not IsWorkBookOpen(fName) Then
'        Set wbk = Workbooks.Open(FileName:=fName)
'    End If
'    'Debug.Print VarType(wbk) '9 = object


'Following function checks and returns true if passed worksheet-name exists.
'Usage:
'    If Not SheetExists("SheetName") Then
'        Worksheets.Add(After:=Worksheets(Worksheets.Count)).name = "SheetName"
'    End If
  ' OR
'    If SheetExists("sName") Then MsgBox ("YES, this sheet exists.")
'    If Not SheetExists("sName") Then MsgBox ("NO, this sheet does NOT exist.")
Function SheetExists(n As String) As Boolean
  SheetExists = False
  For Each ws In Worksheets
    If n = ws.name Then
      SheetExists = True
      Exit Function
    End If
  Next ws
End Function


'Following code checks if workbook has VBProject, if yes saves files as .xlsm otherwise saves as .xlsx.
Sub BulkConvertionFromXlsIntoXlsmOrXlsx()
    Call ConfirmRun("BulkConvertionFromXlsIntoXlsmOrXlsx")
    Dim strPath As String: strPath = "C:\Users\ashrivastava\Downloads\"     ' Path must end in trailing backslash
    Dim strFile As String: strFile = Dir(strPath & "*.xls")
    Dim wbk As Workbook
    Debug.Print strFile
    ScreenUpdating = False
    Do While strFile <> ""
        If Right(strFile, 3) = "xls" Then
            Set wbk = Workbooks.Open(fileName:=strPath & strFile)
            Debug.Print VarType(wbk) '9 = object
            If wbk.HasVBProject Then
              wbk.SaveAs fileName:=strPath & strFile & "m", FileFormat:=xlOpenXMLWorkbookMacroEnabled
            Else
               wbk.SaveAs fileName:=strPath & strFile & "x", FileFormat:=xlOpenXMLWorkbook
            End If
            wbk.Close SaveChanges:=False
        End If
        strFile = Dir
    Loop
    ScreenUpdating = True
End Sub


'Following code saves the current worksheet as CSV file with same name as current sheet-name in the current folder.
Sub ExportAsTxtTabDelimited()
 Dim wBookPath As String
 If Len(ActiveWorkbook.Path) <= 0 Or ActiveWorkbook.name Like "book*" Then
       wBookPath = "C:\temp"  'alternatively use netowrk UNC path.
       'wBookPath = CreateObject("WScript.Shell").SpecialFolders("mydocuments") & "\temp" '-> returns customised Documents folder (not the User Document folder.)
 Else
    wBookPath = ActiveWorkbook.Path
 End If
 If ActiveSheet.name Like "Sheet*" Then
     wBookName = "List_" & Format(Now, "dd-MM-yyyy_hhmmss") & ".txt"
 Else
     wBookName = ActiveSheet.name
 End If
 'ChDir "\\slgpipav01\SGPData\Data-Management\Data-Manager"
 ActiveWorkbook.SaveAs fileName:=wBookPath & "\" & wBookName & ".txt", FileFormat:=xlText, CreateBackup:=False
 MsgBox "Sheet exported as: """ & wBookName & """" & vbNewLine & vbNewLine & "Saved under: " & wBookPath
End Sub


'Following code saves the current worksheet as CSV file with same name as current sheet-name in the current folder.
Sub ExportAsCSV()
 Dim wBookPath As String
 If Len(ActiveWorkbook.Path) <= 0 Or ActiveWorkbook.name Like "book*" Then
    wBookPath = "C:\temp"  'alternatively use netowrk UNC path.
    'wBookPath = CreateObject("WScript.Shell").SpecialFolders("mydocuments") & "\temp" '-> returns customised Documents folder (not the User Document folder.)
 Else
    wBookPath = ActiveWorkbook.Path
 End If
 If ActiveSheet.name Like "Sheet*" Then
     wBookName = "List_" & Format(Now, "dd-MM-yyyy_hhmmss") & " (for Communigator).csv"
 Else
     wBookName = ActiveSheet.name
 End If
'    wBookPath = ActiveWorkbook.Path 'must be assigned BEFORE the ActiveSheet.Copy command
     ActiveSheet.Copy
     'Call DeleteUnnecessaryCols
     'wBookName = "CK remaining members 241116 ()"
     ActiveWorkbook.SaveAs fileName:=wBookPath & "\" & wBookName, FileFormat:=xlCSV, CreateBackup:=False
     ActiveWorkbook.Close False
     ThisWorkbook.Activate
     MsgBox "Sheet exported as: """ & wBookName & """" & vbNewLine & vbNewLine & "Saved under: " & wBookPath
End Sub


'Following code saves the current worksheet as CSV file with same name as current sheet-name in the current folder.
Sub ExportAsCSV_Old() '''''''TODO
 Dim fileName As String: fileName = IIf(IsEmpty(ActiveWorkbook.name), ActiveSheet.name, ActiveWorkbook.name)
 If Len(fileName) > 0 Then
    fileName = Left(fileName, InStrRev(fileName, ".") - 1) 'use of InStrRev() is better than chopping fixed length using Len() e.g. Left(fileName, Len(fileName) - 4)
 End If
 On Error GoTo ErrSave
 'ActiveWorkbook.SaveAs fileName:=ActiveWorkbook.Path & "\" & fileName & ".csv", FileFormat:=xlCSV, CreateBackup:=False
 ActiveWorkbook.SaveAs fileName:=ActiveWorkbook.Path & "\" & ActiveSheet.name & ".csv", FileFormat:=xlCSV, CreateBackup:=False

ErrSave:
    bFileSaveAs = Application.GetSaveAsFilename(InitialFileName:=fileName, fileFilter:="Excel Files (*.csv), *.csv")
    'bFileSaveAs = Application.Dialogs(xlDialogSaveAs).Show  'Dialogs() function gives less options/arguments in comparison to GetSaveAsFilename()
    If bFileSaveAs = False Then MsgBox "You cancelled, file didn't save.", vbCritical
End Sub


'Following code saves the current workbook as .XLSX file with same name as current sheet-name in the "C:\temp" or current folder:
Sub SaveAsXlsx()
    Dim wBookPath As String
    If Len(ActiveWorkbook.Path) <= 0 Or ActiveWorkbook.name Like "book*" Then
       wBookPath = "C:\temp"  'alternatively use netowrk UNC path.
       'wBookPath = CreateObject("WScript.Shell").SpecialFolders("mydocuments") & "\temp" '-> returns customised Documents folder (not the User Document folder.)
    Else
       wBookPath = ActiveWorkbook.Path
    End If
    If ActiveSheet.name Like "Sheet*" Then
        wBookName = "List_" & Format(Now, "dd-MM-yyyy_hhmmss") & " (for review).xlsx"
    Else
        wBookName = ActiveSheet.name
    End If
 
    Set fso = CreateObject("Scripting.FileSystemObject")
    If fso.FileExists(wBookPath) Then
        If MsgBox("File already exists in this location.  Do you want to replace it?", vbYesNo) = vbYes Then
            ActiveWorkbook.SaveAs wBookPath, 52
            'ActiveWorkbook.SaveAs fileName:="\\slgpipav01\Homedrive\eddie.shrivastava\Documents\test3.xlsx", FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
            ActiveWorkbook.SaveAs fileName:=wBookPath & "\" & wBookName, FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
        End If
    Else
        ActiveWorkbook.SaveAs fileName:=wBookPath & "\" & wBookName, FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
        'ActiveWorkbook.SaveAs FilePath, 52
    End If
    Set fso = Nothing
    MsgBox "Sheet exported as: """ & wBookName & """" & vbNewLine & vbNewLine & "Saved under: " & wBookPath & "\"
End Sub


'Following code exports/saves the current worksheet as a separate .XLSX file with same name as current sheet-name in the "C:\temp" or current folder:
Sub ExportCurrentSheetAsXlsx()
    Dim wBookPath As String
    If Len(ActiveWorkbook.Path) <= 0 Or ActiveWorkbook.name Like "book*" Then
       wBookPath = "C:\temp"  'alternatively use netowrk UNC path.
       'wBookPath = CreateObject("WScript.Shell").SpecialFolders("mydocuments") & "\temp" '-> returns customised Documents folder (not the User Document folder.)
    Else
       wBookPath = ActiveWorkbook.Path
    End If
    If ActiveSheet.name Like "Sheet*" Then
        wBookName = "List_" & Format(Now, "dd-MM-yyyy_hhmmss") & " (for review).xlsx"
    Else
        wBookName = ActiveSheet.name
    End If
 
    Worksheets(ActiveSheet.name).Copy  'there is now a new active workbook (with your sheet in it) which can be saved.
    On Error GoTo ErrHandler:
    Set fso = CreateObject("Scripting.FileSystemObject")
    If fso.FileExists(wBookPath) Then
        If MsgBox("File already exists in this location.  Do you want to replace it?", vbYesNo) = vbYes Then
            ActiveWorkbook.SaveAs wBookPath, 52
            'ActiveWorkbook.SaveAs fileName:="\\slgpipav01\Homedrive\eddie.shrivastava\Documents\test3.xlsx", FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
            ActiveWorkbook.SaveAs fileName:=wBookPath & "\" & wBookName, FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
        End If
    Else
        ActiveWorkbook.SaveAs fileName:=wBookPath & "\" & wBookName, FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
        'ActiveWorkbook.SaveAs FilePath, 52
    End If
    Set fso = Nothing
    MsgBox "Sheet exported as: """ & wBookName & """" & vbNewLine & vbNewLine & "Saved under: " & wBookPath & "\"
    
ErrHandler:
    If Err.Number = 1004 Then  'error# 11 = divide by zero
        Debug.Print "Err#1004: A file with same name currently open, therefore cannot be overwritten.  Please close that file and try again."
        Debug.Print Err.Number
        Debug.Print Err.Description
        MsgBox "Err#1004: A file with same name currently open, therefore cannot be overwritten.  Please close that file and try again.", vbCritical, "Unexpected error"
    Else
        MsgBox "Unexpected error occurred, see error-details in Immediate windowpane. Please contact Eddie Shrivastava to resolve.", vbCritical, "Unexpected error"
        Debug.Print Err.Number
        Debug.Print Err.Description
    End If
    On Error GoTo 0
End Sub

'Following sub exports each sheet of the ActiveWorkbook as a separate .xlsx file.
'It is a very useful generic utility:
Sub SplitWorkbookExportSheetsAsXlsx()
    Dim FileExtStr As String
    Dim FileFormatNum As Long
    Dim xWs As Worksheet
    Dim xWb As Workbook
    Dim FolderName As String
    Application.ScreenUpdating = False
    Set xWb = Application.ActiveWorkbook
    DateString = Format(Now, "yyyy-mm-dd hh-mm-ss")
    'FolderName = xWb.Path & "\" & xWb.Name & " " & DateString
    FolderName = xWb.Path & "\" & "CK Customer Usage Files " & DateString
    MkDir FolderName
    For Each xWs In xWb.Worksheets
        If xWs.name Like "Member Analytics*" Then
            GoTo NextIteration
        End If
        xWs.Copy
        If Val(Application.Version) < 12 Then
            FileExtStr = ".xls": FileFormatNum = -4143
        Else
            Select Case xWb.FileFormat
                Case 51:
                    FileExtStr = ".xlsx": FileFormatNum = 51
                Case 52:
                    If Application.ActiveWorkbook.HasVBProject Then
                        FileExtStr = ".xlsm": FileFormatNum = 52
                    Else
                        FileExtStr = ".xlsx": FileFormatNum = 51
                    End If
                Case 56:
                    FileExtStr = ".xls": FileFormatNum = 56
                Case Else:
                    FileExtStr = ".xlsb": FileFormatNum = 50
            End Select
        End If
        xFile = FolderName & "\" & Application.ActiveWorkbook.Sheets(1).name & FileExtStr
        Application.ActiveWorkbook.SaveAs xFile, FileFormat:=FileFormatNum
        Application.ActiveWorkbook.Close False
NextIteration:
    Next
    MsgBox "Files exported to the following folder: " & vbNewLine & vbNewLine & FolderName
    Application.ScreenUpdating = True
End Sub


'Vba Code Eliminate "Illegal Characters" from a filename
Function strLegalFileName(strFileNameIn As String) As String
    Dim i As Integer
    Const strIllegals = "\/|?*<>"":"
    strLegalFileName = strFileNameIn
    For i = 1 To Len(strIllegals)
        strLegalFileName = Replace(strLegalFileName, Mid$(strIllegals, i, 1), "_")
    Next i
End Function



'Following compares Column A against B & highlights values not found in the other Column
'Note: alternatively you can use Match() WS function manually, e.g. in C1: ="A" & MATCH(B1, $A$1:$A$41, 0)
Sub CompareAandBColumnsHighlightNoMatches()
    Dim lastRowA, lastRowB, r As Long
    Dim rng As Range
    lastRowA = Cells(Rows.count, 1).End(xlUp).Row
    lastRowB = Cells(Rows.count, 2).End(xlUp).Row
    Dim hasDiff As Boolean: hasDiff = False
  'checking each value in B column agains full range of column A:
    Set rng = Range("A1:A" & lastRowA)
    For r = 1 To lastRowB
        If IsError(Application.Match(Cells(r, "B"), rng, 0)) Then
            'Cells(r, "B").Interior.Color = RGB(255, 255, 0)
            Cells(r, "B").Interior.ColorIndex = 22
            Debug.Print Cells(r, "B")
            hasDiff = True
        End If
    Next r
  'checking each value in column A agains full range of column B:
    Set rng = Range("B2:B" & lastRowB)
    For r = 2 To lastRowA
        If IsError(Application.Match(Cells(r, "A"), rng, 0)) Then
            Cells(r, "A").Interior.ColorIndex = 39
            Debug.Print Cells(r, "A")
            hasDiff = True
        End If
    Next r
    If hasDiff Then
        MsgBox "Differences in Columns A vs B highlighted.", vbInformation
    Else
        MsgBox "No difference found in Columns A vs B.", vbInformation
    End If
End Sub

'Following compares selected columns & highlights values not found in the other Column
'Note: alternatively you can use Match() WS function manually, e.g. in C1: ="A" & MATCH(B1, $A$1:$A$41, 0)
Sub CompareSelectedColumnsAndHighlightNoMatches()
    Dim lastRowA, lastRowB, r As Long
    
    'Get 1st column from user:
    Dim col1 As String: col1 = GetTargetColumn()
    If col1 = "" Then
        col1 = "A"
    End If
    
    'Get 2nd column from user:
    Dim col2 As String: col2 = GetTargetColumn()
    If col2 = "" Then
        col2 = "B"
    End If
    
    Dim rng As Range
    lastRowA = Cells(Rows.count, col1).End(xlUp).Row
    lastRowB = Cells(Rows.count, col2).End(xlUp).Row
    
    Dim hasDiff As Boolean: hasDiff = False
  'checking each value in B column agains full range of column A:
    Set rng = Range(col1 & "1:" & col1 & lastRowA)
    For r = 1 To lastRowB
        If IsError(Application.Match(Cells(r, col2), rng, 0)) Then
            'Cells(r, col2).Interior.Color = RGB(255, 255, 0)
            Cells(r, col2).Interior.ColorIndex = 22
            'Debug.Print Cells(r, col2)
            hasDiff = True
        End If
    Next r
  'checking each value in column A agains full range of column B:
    Set rng = Range(col2 & "1:" & col2 & lastRowB)
    For r = 1 To lastRowA
        If IsError(Application.Match(Cells(r, col1), rng, 0)) Then
            'Cells(r, col1).Interior.Color = RGB(255, 255, 0)
            Cells(r, col1).Interior.ColorIndex = 39
            'Debug.Print Cells(r, col1)
            hasDiff = True
        End If
    Next r
    If hasDiff Then
        MsgBox "Differences in selected columns have been highlighted.", vbInformation
    Else
        MsgBox "No difference found in selected columns.", vbInformation
    End If
End Sub


'Following code removes duplicate Interests/Keywords, basically it inserts a new column on the right of "sourceCol"
'then copies the deduped values (of "sourceCol") into the newly inserted "targetCol"
Sub DedupeKeywordsAndCopyInNewColumn()
    Application.ScreenUpdating = False
    Dim lastRow As Long, r As Long
    Dim sourceCol As Integer: sourceCol = 4  'source column containing Interests.
    Columns(sourceCol).Offset(0, 1).EntireColumn.Insert
    Dim targetCol As Integer: targetCol = sourceCol + 1  'Target column will contain unique Interests.
    Dim matched As Boolean
    Dim idx As Integer
    Dim kw() As String
    Dim dedupedKw() As String
    lastRow = Cells(Rows.count, sourceCol).End(xlUp).Row
    For r = 1 To lastRow
        If Not IsEmpty(Cells(r, sourceCol)) Then
            kw() = Split(Cells(r, sourceCol), "#")
            'Deduping process: 1) write 1st item regardless as deduped-output
            '2) (starting from 2nd item) match/check for equality only to the left item,
            '3) if matched skip it & immediately exit to outer loop, if unmatched write as deduped-output.
            ReDim dedupedKw(1)
            dedupedKw(0) = kw(0) '1) writing 1st item regardless as deduped-output
            idx = 1
            For i = 1 To UBound(kw)
                matched = False
                For j = i - 1 To 0 Step -1
                    If kw(i) = kw(j) Then
                        matched = True
                        Exit For
                    End If
                Next j
                If matched = False Then
                    ReDim Preserve dedupedKw(idx + 1)
                    dedupedKw(idx) = kw(i)
                    idx = idx + 1
                End If
            Next i
            Cells(r, targetCol) = Left(Join(dedupedKw, "#"), Len(Join(dedupedKw, "#")) - Len("#")) 'to omit the last delimiter: Left( Join(arr,"delimiter"), Len(Join(arr,"delimiter")) - Len(delimiter) )
            'Cells(r, targetCol) = Join(dedupedKw, "#")
        End If
    Next r
    Application.ScreenUpdating = True
End Sub


'This sub works in cunjunction with the =SubTotal(--(A1&B1 = $A$1:$A$20&$B$1:$B$20)) function (in col 'H'), which
'compares entire row (colRowSubTotal) for duplication, it produces value>1 if another identical row is found in the dataset.
'Make sure that column 'A' is sorted. To keep latest record, sort the dataset in descending-time/chronological order.
'This sub then deletes duplicates rows (from bottom), it also highlights the rows that have same value in col 'E'.
Sub DeDupContactsBeforeImport()
    Application.ScreenUpdating = False
    Dim recForReview, recDeleted As Integer: recForReview = 0: recDeleted = 0
    Dim lastRow As Long, r As Long
    Dim tVal, colContainingEmail, colRowSubTotal As String
    colContainingEmail = "E"    'this is the column containing emails.
    colRowSubTotal = "H"       'this is the column containing =SubTotal() furmula.
    lastRow = Cells(Rows.count, colRowSubTotal).End(xlUp).Row
    For r = lastRow To 2 Step -1
        If Cells(r, colContainingEmail) = Cells(r - 1, colContainingEmail) And Cells(r, colRowSubTotal) > 1 Then
            Rows(r).EntireRow.Delete
            recDeleted = recDeleted + 1
            GoTo NextIteration
        ElseIf Cells(r, colContainingEmail) = Cells(r - 1, colContainingEmail) And Cells(r, colRowSubTotal) = 1 Then
            recForReview = recForReview + 1
            Rows(r).Interior.ColorIndex = 3
        End If
NextIteration:
    Next r
    Application.ScreenUpdating = True
    MsgBox "All done. " & vbNewLine & recForReview & " records for review." & vbNewLine & recDeleted & " records deleted."
End Sub


'Following code filters and only shows rows containing selected OCI
Sub FilterAndShowRowsContainingSelectedText__SingleWordSearch_Ver1()
    'Call ConfirmRun("FilterAndShowRowsContainingSelectedText")  'no need to confirm as prompts another pop up.
    'ScreenUpdating = False
    Dim lastRow, r As Long: lastRow = ActiveSheet.UsedRange.Rows.count 'Cells(Rows.Count, 1).End(xlUp).row
    Dim aCell As Range
    Dim s, col As String
    Dim name, showHide As Variant
'    If ActiveSheet.name <> "Sheet1" And ActiveSheet.name <> "IncompleteSheet" Then
'        MsgBox "Cannot run code from within current worksheet!!! Select correct worksheet."
'        End
'    End If
    name = InputBox("Enter text to filter rows (hide rows that do not contain your text)", "Filter Rows")
    If name = False Then  'user cancelled - clicking 'Cancel' button returns Boolean-False (NOT vbCancel).
        End
    ElseIf name = "" Then    'user clicked OK without inputing anything, returns empty-string "".
        MsgBox "No text! you didn't enter anything."
        End
    End If
    showHide = MsgBox("Click 'Yes' to SHOW rows containing your text, click 'No' to HIDE rows containing your text", vbYesNoCancel + vbQuestion, "Show or Hide rows")
    If showHide = vbCancel Then
        End
    End If

    If name <> "" Then
        'Set aCell = Range("F2:K200").Find(What:=name, LookIn:=xlValues, MatchCase:=False, Lookat:=xlPart, SearchFormat:=False, SearchOrder:=xlByRows or xlByColumns)
        Set aCell = ActiveSheet.UsedRange.Find(name)
        If Not aCell Is Nothing Then
            For r = lastRow To 1 Step -1
                s = ""
                For i = 1 To Cells(r, Columns.count).End(xlToLeft).Column
                    s = s & Cells(r, i)
                Next i
                If showHide = vbYes Then
                    If InStr(1, s, name, vbTextCompare) = 0 Then  'hide if NOT found
                        Rows(r).Hidden = True
                    End If
                ElseIf showHide = vbNo Then
                    If InStr(1, s, name, vbTextCompare) > 0 Then   'hide if Found
                        Rows(r).Hidden = True
                    End If
                End If
            Next r
        Else
            MsgBox """" & name & """" & " not found."
        End If
    End If
    'ScreenUpdating = True
End Sub


'Following code filters and either shows or hides rows containing text entered by user,
' also only applies filter to the row numbers(Row-Range) that user has entered.
'DESCRIPTION:
'  This script provides customised advanced filter like functionality to allow search of non-consecutive words
'  in a row or cell. It also provide selectable range options as both multiple column-range as well as multiple
'  row-range feature on which custom filter can be applied.
'Sub FilterByWords-List()
Sub FilterAndShowRowsContainingSelectedText()
    'Call ConfirmRun("FilterAndShowRowsContainingSelectedText")  'no need to confirm as prompts another pop up.
    Dim lastRow, r As Long: lastRow = ActiveSheet.UsedRange.Rows.count 'Cells(Rows.Count, 1).End(xlUp).row
    Dim aCell, rngCols, rng As Range
    Dim elementFound, validRng As Boolean: validRng = True
    Dim s, col As String
    Dim name, showHide, ansRng, arr, arrRng, arrRows2Unhide As Variant
    Dim i, j As Integer
    Dim sngStart, sngEnd, sngElapsed As Single
    Dim strCol, rows2Unhide As String

    name = Application.InputBox("Enter words (separated by comma)" & vbNewLine & vbNewLine & "This will filter the rows - depending on whether you choose to hide or display the rows containing your word(s).", "Enter words to filter rows")
    If name = False Then  'user cancelled - clicking 'Cancel' button returns Boolean-False (NOT vbCancel).
        End
    ElseIf name = "" Then    'user clicked OK without inputing anything, returns empty-string "".
        MsgBox "No text! you didn't enter anything."
        End
    End If
    'checking existance of search terms again within the ENTIRE-Sheet:
    Dim notFoundEndSub As Boolean: notFoundEndSub = True
    
    If name <> "" Then
        arr = Split(name, ",")
        'getting rid of whitespaces:
        For i = 0 To UBound(arr)
            arr(i) = Trim(arr(i))
            Set aCell = ActiveSheet.UsedRange.Find(What:=arr(i), LookIn:=xlValues, MatchCase:=False, LookAt:=xlPart, SearchFormat:=False)
            If Not aCell Is Nothing Then
                notFoundEndSub = False
                Exit For
            End If
        Next i '      MsgBox Join(arr, "_")
    End If
    If notFoundEndSub Then
        MsgBox "Word(s) don't exist in this sheet." & vbNewLine & vbNewLine & " You entered:  """ & name & """"
        'MsgBox "Word(s) don't exist in this sheet (in a single row or a cell)." & vbNewLine & vbNewLine & " You entered:  """ & name & """"
        End
    End If

  'Getting Row-Range:
    validRng = True
    ansRng = InputBox("Enter row number(s) / row-range separated by hyphen (e.g. 3-9) to limit the range on which filter is to be applied." & vbNewLine & vbNewLine & "If left blank, filter will be applied to all rows.", "Enter Row-Range")
'    If ansRng = "" Then     'user cancelled or clicked OK without inputing anything, hence returned empty-string "".
    If ansRng <> "" Then     'user neither cancelled nor entered empty-string (both checked as call without "Application.")
        If (InStr(ansRng, "-") <= 0) Then
            ansRng = "1-" & ansRng  'Include header row in search.
            'ansRng = "2-" & ansRng  'Excludes header row in search.
        End If
        arrRng = Split(ansRng, "-")
        'RowRngValidation: Validating row numbers and getting rid of whitespaces:
        For i = 0 To UBound(arrRng)
            arrRng(i) = Trim(arrRng(i))
            If Not IsNumeric(arrRng(i)) Or arrRng(i) <= 0 Or arrRng(i) > 1048576 Then
                MsgBox "Incorrect range format, filter will be applied to all rows" & vbNewLine & vbNewLine & "(Note: range must be entered as numbers seperated by hyphen (e.g. 2-7 for 2nd-to-7th rows)" & vbNewLine & vbNewLine & "You entered:  " & ansRng
                validRng = False
                Exit For
            End If
        Next i
    Else
        validRng = False
    End If
    If validRng Then
        firstRow = Trim(arrRng(0))
        If UBound(arrRng) >= 1 Then
            If arrRng(1) <> "" Then
                lastRow = arrRng(1)
            End If
        Else
            lastRow = ActiveSheet.UsedRange.SpecialCells(11).Row
        End If
    Else
        firstRow = ActiveSheet.UsedRange.Row  ' + 1
        lastRow = ActiveSheet.UsedRange.SpecialCells(11).Row  'last used row
    End If
    'checking existance of search terms again within the given ROW-Range:
    notFoundEndSub = True
    For i = 0 To UBound(arr)
        arr(i) = Trim(arr(i))
        'Set aCell = ActiveSheet.UsedRange.Find(arr(i))
        Set aCell = Rows(firstRow & ":" & lastRow).Find(What:=arr(i), LookIn:=xlValues, MatchCase:=False, LookAt:=xlPart, SearchFormat:=False, SearchOrder:=xlByRows)
        If Not aCell Is Nothing Then
            notFoundEndSub = False
            Exit For
        End If
    Next i
    If notFoundEndSub Then
            MsgBox "Your word(s) don't exist in the row-range you typed." & vbNewLine & vbNewLine & "Search-term(s)/Word(s) you entered:  """ & name & """" & vbNewLine & vbNewLine & "Row-range you entered:  """ & ansRng & """"
            'MsgBox "Your word(s) don't exist in the row-range you typed (within a single row or in a cell)." & vbNewLine & vbNewLine & "Search-term(s)/Word(s) you entered:  """ & name & """" & vbNewLine & vbNewLine & "Row-range you entered:  """ & ansRng & """"
            End
    End If

  'Getting Column-Range:
    validRng = True
    'ansRng = InputBox("Enter column letter(s) separated by comma(e.g. A, C, F for individual columns) OR column-range separated by hyphen (e.g. A-F from A through F columns) to limit the range on which filter is to be applied." & vbNewLine & vbNewLine & "If left blank, filter will be applied to all columns.", "Enter Column-Range")
    ansRng = Trim(InputBox("Enter column letter(s) separated by comma(e.g. A, C, F for individual columns) to limit the range on which filter is to be applied." & vbNewLine & vbNewLine & "If left blank, filter will be applied to all columns.", "Enter Column-Range"))
'    If ansRng = "" Then     'user cancelled or clicked OK without inputing anything, hence returned empty-string "".
    If ansRng <> "" Then     'user entered some text, i.e. neither cancelled nor entered empty-string (both are checked as call without "Application.")
    ''''''''''is it range separated by "-" or individual multiple columns seperated by ","
'        If InStr(ansRng, "-", vbTextCompare) Then ElseIf InStr(ansRng, ",", vbTextCompare) Then  ...  End If
        arrRng = Split(ansRng, ",")
        'RowRngValidation: Validating row numbers and getting rid of whitespaces:
        For i = 0 To UBound(arrRng)
            arrRng(i) = Trim(arrRng(i))
            If Not hasAlphabets(arrRng(i) & "") Or Len(arrRng(i)) <= 0 Or Len(arrRng(i)) > 3 Then
                MsgBox "Incorrect column letters format, filter will be applied to all columns" & vbNewLine & vbNewLine & "(Note: Column range must be entered as Excel Column-Alphabets seperated by comma (e.g. A,C for 1st & 3rd Columns)" & vbNewLine & vbNewLine & "You entered  :" & ansRng
                validRng = False
                Exit For
            End If
            If i = 0 Then
                Set rngCols = Range(Cells(firstRow, arrRng(i)), Cells(lastRow, arrRng(i)))
            Else
                Set rngCols = Union(rngCols, Range(Cells(firstRow, arrRng(i)), Cells(lastRow, arrRng(i))))
            End If
        Next i
    Else
        validRng = False
    End If
    If Not validRng Then
        strCol = ""
    Else
        notFoundEndSub = True
        For i = 0 To UBound(arr)
            Set aCell = rngCols.Find(What:=arr(i), LookIn:=xlValues, MatchCase:=False, LookAt:=xlPart, SearchFormat:=False, SearchOrder:=xlByColumns)
            If Not aCell Is Nothing Then
                notFoundEndSub = False
                Exit For
            End If
        Next i
        If notFoundEndSub Then
            MsgBox "Your word(s) don't exist in the column-range you typed." & vbNewLine & vbNewLine & "Word(s) you entered  : """ & name & """" & vbNewLine & vbNewLine & "Column range you entered: """ & ansRng & """"
            End
        End If
        strCol = arr(0)
    End If
'    strCol = Trim(InputBox(Prompt:="Enter the target Column-Letter (e.g. 'A' for 1st Column)." & vbNewLine & vbNewLine & "Leave blank to apply filter to all columns.", Title:="Enter Column-Range"))
    showHide = MsgBox("Click 'Yes' to SHOW the rows containing all your words, click 'No' to HIDE rows containing your words", vbYesNoCancel + vbQuestion, "Choose whether to Show or Hide rows")
    If showHide = vbCancel Then
        End
    End If
    TurnOffOtherExcelActivities (True)
'---------------------------------------
    sngStart = Timer                               ' Get start time.
    For r = lastRow To firstRow Step -1
        'Set s = Nothing 'completely deletes of variable, instead of purging its value, hence unusable in this situation.
        s = ""
        If strCol = "" Then
            For j = 1 To Cells(r, Columns.count).End(xlToLeft).Column
                s = s & "¬" & Cells(r, j)
            Next j
        Else
        '''''' for multiple columns: loop here in each columns
            For i = 0 To UBound(arrRng)
                s = s & Cells(r, arrRng(i))
            Next i
        End If
        elementFound = False
        For i = 0 To UBound(arr)
            If InStr(1, s, arr(i), vbTextCompare) > 0 Then  'check if found
                'this "if" section filters as if search-terms have "OR" (means check for ANY(one) of the search-terms presence)
                elementFound = True
                Exit For 'enable/uncomment this for "OR" logic i.e. ANY words + disable/comment the "Exit For" in the "Else" section below + change the logics at <range>.Find above.
            Else
                'this "else" section filters as if search-terms have "AND" (means check for ALL of the search-terms presence)
                elementFound = False
                'Exit For 'enable/uncomment this for "AND" logic i.e. ALL words + disable/comment the "Exit For" in the "IF" section above + change the logics at <range>.Find above.
            End If
        Next i
        If elementFound Then  'hide if NOT found
            'Rows(r).Hidden = True
            rows2Unhide = rows2Unhide & r & ","
        End If
    Next r
    arrRows2Unhide = Split(rows2Unhide, ",")
    isFirstIteration = True
    For i = 0 To UBound(arrRows2Unhide) - 1
        x = arrRows2Unhide(i)
        If i = 0 Then
            Set rng = Range(x & ":" & x)
        Else
            Set rng = Union(rng, Range(x & ":" & x))
        End If
        'strRows2Unhide = strRows2Unhide & "Rows(""" & x & ":" & x & """),"
        'strRows2Unhide = Mid(strRows2Unhide, 1, Len(strRows2Unhide) - 1)
    Next i
    'Debug.Print rng.Address
    If showHide = vbYes Then
        Rows(firstRow & ":" & lastRow).EntireRow.Hidden = True
        rng.EntireRow.Hidden = False  'Range() method only takes upto 45 parameters, after which it throws error.
    Else
        rng.EntireRow.Hidden = True  'Range() only takes upto 45 parameters, after which it throws error.
    End If
    Rows(1).Hidden = False  'Unhide header row. Toggle this to show/hide the header row.
    aCell.Select
    TurnOffOtherExcelActivities (False)
'---------------------------------------
    sngEnd = Timer                                 ' Get end time.
    sngElapsed = Format(sngEnd - sngStart, "Fixed") ' Elapsed time.
    MsgBox ("Custom filter applied." & vbNewLine & vbNewLine & "The code took " & sngElapsed & " seconds to run.")
End Sub


'Following code filters and either shows or hides rows containing text entered by user.
Sub FilterAndShowRowsContainingSelectedText_AppliesToEntireSheet_OLD()
    'Call ConfirmRun("FilterAndShowRowsContainingSelectedText")  'no need to confirm as prompts another pop up.
    Dim lastRow, r As Long: lastRow = ActiveSheet.UsedRange.Rows.count 'Cells(Rows.Count, 1).End(xlUp).row
    Dim aCell As Range
    Dim elementFound As Boolean
    Dim s, col, arr() As String
    Dim name, showHide As Variant
    Dim i, j As Integer
    Dim sngStart, sngEnd, sngElapsed As Single
'    If ActiveSheet.name <> "Sheet1" And ActiveSheet.name <> "IncompleteSheet" Then
'        MsgBox "Cannot run code from within current worksheet!!! Select correct worksheet."
'        End
'    End If
    name = InputBox("Enter words (separated by comma), this will filter the rows - letting you choose whether to hide or show rows that contain your word(s).", "Filter Rows")
    If name = False Then  'user cancelled - clicking 'Cancel' button returns Boolean-False (NOT vbCancel).
        End
    ElseIf name = "" Then    'user clicked OK without inputing anything, returns empty-string "".
        MsgBox "No text! you didn't enter anything."
        End
    End If
    showHide = MsgBox("Click 'Yes' to SHOW the rows containing all your words, click 'No' to HIDE rows containing your words", vbYesNoCancel + vbQuestion, "Choose whether to Show or Hide rows")
    If showHide = vbCancel Then
        End
    End If
    ScreenUpdating = False

    sngStart = Timer                               ' Get start time.
    If name <> "" Then
        arr() = Split(name, ",")
        'getting rid of whitespaces:
        For i = 0 To UBound(arr)
            arr(i) = Trim(arr(i))
        Next i
'      MsgBox Join(arr, "_")
        Set aCell = ActiveSheet.UsedRange.Find(What:=arr(0), LookIn:=xlValues, MatchCase:=False, LookAt:=xlPart, SearchFormat:=False, SearchOrder:=xlByRows)
        If Not aCell Is Nothing Then
            For r = lastRow To 2 Step -1
                s = ""
                For j = 1 To Cells(r, Columns.count).End(xlToLeft).Column
                    s = s & Cells(r, j)
                Next j
                elementFound = False
                If showHide = vbYes Then
                    For i = 0 To UBound(arr)
                        If InStr(1, s, arr(i), vbTextCompare) > 0 Then  'check if found
                            elementFound = True
                        Else
                            elementFound = False
                            Exit For  'even if one of the elements not found
                        End If
                    Next i
                    If Not elementFound Then  'hide if NOT found
                        Rows(r).Hidden = True
                    End If
                ElseIf showHide = vbNo Then
                    For i = 0 To UBound(arr)
                        If InStr(1, s, arr(i), vbTextCompare) > 0 Then  'check if found
                            elementFound = True
                        Else
                            elementFound = False
                            Exit For  'even if one of the elements not found
                        End If
                    Next i
                    If elementFound Then  'hide if found
                        Rows(r).Hidden = True
                    End If
                End If
            Next r
        Else
            MsgBox """" & name & """" & " not found."
        End If
    End If
    ScreenUpdating = True
    sngEnd = Timer                                 ' Get end time.
    sngElapsed = Format(sngEnd - sngStart, "Fixed") ' Elapsed time.
    MsgBox ("Custom filter applied." & vbNewLine & vbNewLine & "The code took " & sngElapsed & " seconds to run.")
End Sub


Sub FiterSouthEastAndLondonPostcodesOnly()
    Dim tVal, c, PostCodeColumn As String
    'PostCodeColumn = "E"       'set target COLUMN here, which contains Postcodes.
    PostCodeColumn = GetTargetColumn()
    If PostCodeColumn = "" Then
        End
    End If
    
    Dim arr As Variant  'arr must be Variant or String type as Range() & Split() return Sub-Strings (array).
    Dim arr1Chars As Variant  'arr must be Variant or String type as Range() & Split() return Sub-Strings (array).
    Dim r, lastRow As Long
  'POSTCODE PREFIXES of SOUTH EAST + LONDON + some postcodes near south east:
    'arr = Split("BN,BR,CR,CT,DA,GU,HP,KT,ME,MK,OX,PO,RG,RH,SL,SM,SO,TN,TW,UB,WD,EC,NW,SE,SW,W,WC,GL,SN,SP,BH,CO,SS,AL,LU,HA,IG,RM,EN", ",")
    'arr = Split("BN,BR,CR,CT,DA,GU,HP,KT,ME,PO,RG,RH,SL,SM,SO,TN,TW,UB,WD,EC,NW,SE,SW,W,WC,SN,SP,BH,SS,HA,IG,RM,EN", ",")
    'arr = Split("BN,BR,CR,CT,DA,GU,HP,KT,ME,PO,RG,RH,SL,SM,SO,TN,TW,SN,SP,BH", ",")
    'arr1Chars = Split("N,E,W", ",")
    r = PostCodeColumn
    For r = 2 To ActiveSheet.UsedRange.Rows.count
        'If Not ((IsInArray(Left(Cells(r, PostCodeColumn), 2), arr)) Or (IsInArray(Left(Cells(r, PostCodeColumn), 1), arr1Chars) And IsNumeric(Mid(Cells(r, PostCodeColumn), 2, 1)))) Then
        If Not (IsInArray(Left(Cells(r, PostCodeColumn), 2), arr)) Then
        'InStr(1, "Hello World", "Hello W") = 1
            Rows(r).EntireRow.Hidden = True
        End If
    Next r
End Sub

'Folloing code deletes extra whitespaces in between the words (in column 'F') leaving 1 whitespace.
Sub DeleteExtraWhitespaceBetweenWords()
    Dim lastRow, r As Long: lastRow = Cells(Rows.count, 1).End(xlUp).Row
    Dim s As String
    For r = lastRow To 1 Step -1
        s = Cells(r, "F")
        If InStr(s, "    ") <> 0 Then
            s = Replace(s, "    ", " ")
            Cells(r, "F") = s
            'Debug.Print
        End If
    Next r
End Sub

'This code deletes all empty cell in current column - collapsing the entire table into compact unstructured set of data:
Sub DeleteEmptyCellsCollapseEntireTable()
    Call ConfirmRun("DeleteEmptyCellsCollapseEntireTable")
    Dim tmpRng As Range
    On Error Resume Next
    Set tmpRng = Columns(ActiveCell.Column).SpecialCells(xlBlanks)
    If Not tmpRng Is Nothing Then
        Debug.Print WorksheetFunction.CountA(ActiveSheet.UsedRange) 'BEFORE: 'total used rows should remain same after running this sub.
        ActiveSheet.UsedRange.SpecialCells(xlBlanks).Delete Shift:=xlUp  'will delete only blank cells shifting up all cells below.
        Debug.Print WorksheetFunction.CountA(ActiveSheet.UsedRange) 'AFTER: 'total used rows should remain same after running this sub.
    Else
        MsgBox "No empty cells found."
    End If
    On Error GoTo 0
End Sub


'This code deletes all empty cell in current column - collapsing the column into compact unstructured column of data:
Sub DeleteEmptyCellsCollapseSingleColumn()
    Call ConfirmRun("DeleteEmptyCellsCollapseSingleColumn")
    Dim tmpRng As Range
    Debug.Print WorksheetFunction.CountA(Columns(ActiveCell.Column)) 'BEFORE: 'total used rows should remain same after running this sub.
    On Error Resume Next
    Set tmpRng = Columns(ActiveCell.Column).SpecialCells(xlBlanks)
    'If TypeName(tmpRng) <> "Empty" Then
    If Not tmpRng Is Nothing Then
        Columns(ActiveCell.Column).SpecialCells(xlBlanks).Delete Shift:=xlUp  'will delete only blank cells shifting up all cells below.
    Else
        MsgBox "No empty cells found."
    End If
    Debug.Print WorksheetFunction.CountA(Columns(ActiveCell.Column)) 'AFTER: 'total used rows should remain same after running this sub.
    On Error GoTo 0
End Sub


'This code moves all cells in column A - collapsing entire table into one column - this is useful to get unique values (in entire table).
'note: it one-by-one places columns below Column-A, starting with Col-B under Col-A then Col-C, then D and so on.
Sub MoveAllDataInOneColumnA()
    'Call ConfirmRun("MoveAllDataInOneColumnA")
    Dim endRow, endCol, targetRowNum As Long
    ScreenUpdating = False
    Call DeleteEmptyCellsCollapseEntireTable
    targetRowNum = Range("A" & Rows.count).End(xlUp).Row + 1
    'MsgBox targetRowNum
    endCol = ActiveSheet.UsedRange.SpecialCells(11).Column  '11 = xlCellTypeVisible
    For i = 2 To endCol
        Range(Cells(1, i), Cells(Rows.count, i).End(xlUp)).Cut Destination:=Range("A" & targetRowNum)
        targetRowNum = Range("A" & Rows.count).End(xlUp).Row + 1
'        MsgBox targetRowNum
'        End
    Next
    'endRow = .UsedRange.SpecialCells(xlCellTypeLastCell).row
    Dim lngMaxRow As Long
    lngMaxRow = Range("D" & Rows.count).End(xlUp).Row
    Set InputRange = Range("D1:D" & lngMaxRow)
    ScreenUpdating = True
    MsgBox "Done... All cells now moved to one column (column A)."
End Sub


'Following sub moves all the cell-values into Column-A row-by-row:
Sub MoveCellValuesIntoColA()
    Application.ScreenUpdating = True
    Range("A1").Select
    ActiveCell.SpecialCells(xlLastCell).Select
    ActiveCell.Offset(0, 1).End(xlUp).Select
    Call ConcatenateLeftCellsTillLastRow
    Range(Selection, Selection.End(xlDown)).Select
    Selection.Copy
    ActiveCell.Offset(0, 1).Select
    Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
    ActiveCell.Offset(0, -1).Select
    Range(Selection, Cells(ActiveCell.Row, 1)).Select
    Selection.EntireColumn.Delete
    Range("A1").Select
    Application.ScreenUpdating = False
End Sub


'This code deletes all empty rows in ActiveSheet:
Sub DeleteEmptyRows()
    ScreenUpdating = False
    'endRow = ActiveSheet.UsedRange.Rows.Count
    endRow = Cells(Rows.count, 1).End(xlUp).Row
    'Debug.Print WorksheetFunction.CountA(Columns(1)) 'total used rows should remain same after running this sub.
    'Debug.Print endRow
    For i = endRow To 1 Step -1
        If WorksheetFunction.CountA(Rows(i)) = 0 Then
            Rows(i).Delete
        End If
    Next
    ScreenUpdating = True
End Sub

'Following code deletes all empty columns in ActiveSheet (only):
Sub DeleteEmptyColumns()
'If TypeName(Selection) = "Range" Then
'    MsgBox "You selected a Range"
'Else
'    MsgBox "Woops! you selected a " & TypeName(Selection)
'End If
    ScreenUpdating = False
    lastCol = Cells(1, Columns.count).End(xlToLeft).Column 'assuming 1st row has headers.
    'lastCol = ActiveSheet.UsedRange.Columns.Count
    For i = lastCol To 1 Step -1
        'Debug.Print WorksheetFunction.CountA(Columns(i))
        If WorksheetFunction.CountA(Columns(i)) = 0 Then
            Columns(i).Delete
        End If
    Next
    ScreenUpdating = True
End Sub


'Following sub moves the current row from current worksheet to another Sheet.
Sub MoveRowToAnotherSheet()
    Debug.Print ActiveWorkbook.Sheets.count
    'Dim arrSheetsInBook(ActiveWorkbook.Sheets.Count) As String 'You can't DIM an array using a variable, solution is to ReDim it.
    Dim arrSheetsInBook() As String
    ReDim arrSheetsInBook(ActiveWorkbook.Sheets.count)
'    Dim sh, ans As String
        'Destination sheet:
        
        
For Each cLoc In ws.Range("LocationList")

Next cLoc

'    Dim r As Integer
'    Dim incNo As Long 'Must be Long not Integer be able to store 8 digit Inc number, otherwise 'Overflow Runtime 6' error.
'    sh = ActiveSheet.name
'    r = ActiveCell.row
'    incNo = Cells(r, 1)
'    ans = MsgBox("Would you like to move the current row with Inc No: " & incNo & " to the ""Completed"" sheet?", vbYesNo, "Move Row!")
'    If ans = vbYes Then
'        ActiveSheet.Cells(r, "G").Interior.ColorIndex = xlNone
'        ActiveSheet.Cells(r, "G") = "Completed"
'        ActiveSheet.Cells(r, "N") = Date
'        ActiveCell.EntireRow.Cut _
'        Destination:=Sheets("Completed").Cells(Rows.Count, 1).End(xlUp).Offset(1, 0)
'        MsgBox "Inc No: " & incNo & " from row number: " & r & " has been moved to ""Completed"" sheet"
'        Sheets(sh).Rows(r).EntireRow.Delete
'    End If
End Sub
Sub MoveRowFromIncompleteToCompleteSheet()
    Dim Sh, ans As String
    Dim r As Integer
    Dim incNo As Long 'Must be Long not Integer be able to store 8 digit Inc number, otherwise 'Overflow Runtime 6' error.
    Sh = ActiveSheet.name
    r = ActiveCell.Row
    incNo = Cells(r, 1)
    ans = MsgBox("Would you like to move the current row with Inc No: " & incNo & " to the ""Completed"" sheet?", vbYesNo, "Move Row!")
    If ans = vbYes Then
        ActiveSheet.Cells(r, "G").Interior.ColorIndex = xlNone
        ActiveSheet.Cells(r, "G") = "Completed"
        ActiveSheet.Cells(r, "N") = Date
        ActiveCell.EntireRow.Cut _
        Destination:=Sheets("Completed").Cells(Rows.count, 1).End(xlUp).Offset(1, 0)
        MsgBox "Inc No: " & incNo & " from row number: " & r & " has been moved to ""Completed"" sheet"
        Sheets(Sh).Rows(r).EntireRow.Delete
    End If
End Sub


'Following does two things:
' 1) Separates UK records with Overseas by placing UK records at top,
' 2) it also sorts overseas records by Country name.
Sub SortByCountryUKonTop()
    Application.ScreenUpdating = False
    Dim strCol As String: strCol = GetTargetColumn()
    If strCol = "" Then
        End
    End If
    Dim lastRow, lastCol As Long, r As Long
    lastCol = Cells(1, Columns.count).End(xlToLeft).Column + 1
    Columns(lastCol).EntireColumn.Insert
    lastRow = Cells(Rows.count, 2).End(xlUp).Row
    For r = 1 To lastRow
        tVal = UCase(Trim(Cells(r, strCol).Value))
        If tVal = "UK" Then
            Cells(r, lastCol) = "000"
        Else
            Cells(r, lastCol) = tVal
        End If
    Next r
    ActiveSheet.UsedRange.Sort Key1:=Cells(1, lastCol), Order1:=xlAscending, MatchCase:=False, Header:=xlYes
    Columns(lastCol).EntireColumn.Delete
    Application.ScreenUpdating = True
End Sub


'Following VBA code formats the exported-data, it:
' 1) Separates UK records with Overseas by placing Overseas' subs at top & UK records at the bottom of the table.
' 2) it also sorts overseas records by Country name.
Sub SortByCountryCode()
    Application.ScreenUpdating = False
    [A1].EntireColumn.Insert
    Dim lastRow, lastCol As Long, r As Long
    lastRow = Cells(Rows.count, 2).End(xlUp).Row
    For r = 1 To lastRow
        lastCol = Cells(r, Columns.count).End(xlToLeft).Column
        tVal = UCase(Trim(Cells(r, lastCol).Value))
        If tVal = "REPUBLIC OF" Then    'taking care of Trilogy-export's comma / double-quotes problem.
            tVal = UCase(Cells(r, lastCol).Offset(0, -1).Value) & ", " & tVal
            Cells(r, lastCol).Offset(0, -1).Value = tVal
            Cells(r, lastCol).Clear
        Else
            If Trim(Cells(r, lastCol).Value) <> tVal Then   'making sure if value in last col is UPPERCASE.
                Cells(r, lastCol).Value = tVal
            End If
        End If
        
        If IsNumeric(Left(tVal, 3)) Then
            Cells(r, 1) = tVal
        Else
            If IsNumeric(Mid(tVal, 2, 1)) Or IsNumeric(Mid(tVal, 3, 1)) Or tVal = "UK" Then
                Cells(r, 1) = "000"
            Else
                Cells(r, 1) = tVal
                'Cells(r, Columns.Count).End(xlToLeft).Copy Destination:=Cells(r, 1)
            End If
        End If
    Next r
    'ActiveSheet.Range("A1", "M" & lastRow).Sort Key1:=Range("A1"), Order1:=xlAscending, MatchCase:=False, Header:=xlNo
    ActiveSheet.UsedRange.Sort Key1:=Range("A1"), Order1:=xlAscending, MatchCase:=False, Header:=xlNo
    [A1].EntireColumn.Delete
    Application.ScreenUpdating = True
End Sub



'Changes case to ProperCase if 1st character is in lower case, ignoring ALL-CAPS, in given column (asks column-letter from user).
Sub ChangeCase2ProperIf1stCharLower_OLDER()
    Dim lastRow As Long, r As Long
    Dim tVal As String
    Dim col As Variant
    col = Application.InputBox(Prompt:="Enter column letter, in which you want to change lowercase values into ProperCase.")
    Debug.Print TypeName(col) & " - " & col
    If col = False Then  'user cancelled - clicking 'Cancel' button returns Boolean-False (NOT vbCancel).
        End
    ElseIf col = "" Then    'user clicked OK without inputing anything, returns empty-string "".
        MsgBox "No text! you didn't enter anything."
        End
    End If
    Application.ScreenUpdating = False
    lastRow = Cells(Rows.count, 2).End(xlUp).Row
    For r = 2 To lastRow
        tVal = Trim(Cells(r, col))
        If UCase(Left(tVal, 1)) <> Left(tVal, 1) Then
            Cells(r, col) = WorksheetFunction.Proper(tVal)
        ElseIf UCase(tVal) = tVal And Len(tVal) > 3 Then 'applies ProperCase if all chars in UPPERCASE & more than 2 chars in cell
            Cells(r, col) = WorksheetFunction.Proper(tVal)
        End If
    Next r
    Application.ScreenUpdating = True
End Sub
Sub SplitAndJoinArray()
    Dim a() As String
    a() = Split([D1], " ")
    MsgBox [D1]
    'MsgBox UBound(a)
    MsgBox Join(a(), "-")
End Sub

'Changes case to ProperCase if 1st character is in lower case, ignoring ALL-CAPS, in given column (asks column-letter from user).
Sub ChangeCase2ProperIf1stCharLower()
    Dim lastRow As Long, r As Long
    Dim tVal As Variant
    Dim col As Variant
    col = Application.InputBox(Prompt:="Enter column letter, in which you want to change lowercase values into ProperCase.")
    Debug.Print TypeName(col) & " - " & col
    If col = False Then  'user cancelled - clicking 'Cancel' button returns Boolean-False (NOT vbCancel).
        End
    ElseIf col = "" Then    'user clicked OK without inputing anything, returns empty-string "".
        MsgBox "No text! you didn't enter anything."
        End
    End If
    
    Dim aWord() As String   'for each word in each cell.
    Dim Exclusions() As String
    Exclusions() = Split("and, of, for, to, with, in, on, a, an, the", ", ")  'One-liner array assignment.
    'Exclusions() = Split("And, Of, For, To, With, In, On, A, An, The", ", ")  'One-liner array assignment.
    Application.ScreenUpdating = False
    lastRow = Cells(Rows.count, col).End(xlUp).Row
    For r = 1 To lastRow
        aWord() = Split(Cells(r, col), " ")
        For i = LBound(aWord) To UBound(aWord)
        'For Each tVal In aWord
            tVal = aWord(i)
            If UCase(tVal) = "BJFM" Then
                aWord(i) = "BJFM"
            ElseIf UCase(Left(tVal, 1)) <> Left(tVal, 1) Then
                aWord(i) = WorksheetFunction.Proper(tVal)
                For Each x In Exclusions
                   If tVal = x Then  'If InStr(tVal, x) Then
                        aWord(i) = LCase(tVal)
                   End If
                Next x
            ElseIf UCase(tVal) = tVal And Len(tVal) > 3 Then  'applies ProperCase if all chars in UPPERCASE & more than 2 chars in cell
                    aWord(i) = WorksheetFunction.Proper(tVal)
            End If
        Next i
        Cells(r, col) = Trim(Join(aWord, " "))
        Erase aWord
    Next r
    Application.ScreenUpdating = True
End Sub


'usage: IsInArray("item1", arrName) -> this will check if "item1" is in array arrName
'e.g. If (IsInArray(Cells(1, c), arr)) Then
Function IsInArray(ByVal stringToBeFound As String, ByVal arr As Variant) As Boolean
    If UBound(arr) = -1 Then    'array is not initialised - if not checked here Match() will return "Type mismatch" error.
        IsInArray = False
        Exit Function
    Else
        IsInArray = Not IsError(Application.Match(stringToBeFound, arr, 0))
    End If
End Function
'Usage: IsInArrayInt("item1", arrName) -> this will check if passed arg1 is in the array-2nd-arg. Cast-INsensitive for string-comparisons.
'Returns: Position As Integer (not index number), returns -1 if element not found in array.
'e.g. If (IsInArrayInt(Cells(1, c), arr)) Then
Function IsInArrayInt(ByVal stringToBeFound As Variant, ByVal arr As Variant) As Integer
    If UBound(arr) = -1 Then    'array is not initialised - if not checked here Match() will return "Type mismatch" error.
        IsInArrayInt = -1
        Exit Function
    Else
        If Not IsError(Application.Match(stringToBeFound, arr, 0)) Then
            IsInArrayInt = Application.Match(stringToBeFound, arr, 0)
        Else
            IsInArrayInt = -1
        End If
    End If
End Function
Sub IsInArrayIntTest()
    arr = Split("A B C D E F", " ")
    'MsgBox UBound(a) 'a(0)
    MsgBox IsInArrayInt("a", arr) '=> 1 as "a" is at 1st position (finds regardless of lower-case).
End Sub

'This code loops through & deletes all columns except column-headers written in the 'arr' array
'code also renames columns as per another array elements and finally sorts table as per column 1.
'Formats data for Detailed Email list:
Sub DeleteCols_RenameCols_SortByMailName()
    Dim arr As Variant  'arr must be Variant or String type as Range() & Split() return Sub-Strings (array).
    Dim new_names As Variant
    Dim arr_keep As Variant
    Dim lastRow As Long, lastCol As Integer, c As Integer, r As Long
    'Deleting unnecessary columns:
    'arr = Split("Account_No,Known_As,forenames,surname,language,title,filecode,area,Journal,type_code,source_code,prefix,MainTelNo,userdefined4,Mailing_Ref,address_type,postcode2,Post_Code_Group,Country_code,DXAddress,FaxNo,Postal_group_periodic", ",")
    arr = Split("Known_As,Language,filecode,Journal,type_code,source_code,Prefix,Mailing_Ref,address_type,postcode2,Post_Code_Group,Country_code,DXAddress,FaxNo,Postal_group_periodic", ",")
    For c = ActiveSheet.UsedRange.Columns.count To 1 Step -1
        If (IsInArray(Cells(1, c), arr)) Then
            Cells(1, c).EntireColumn.Delete
        End If
    Next c
    Call FormatEdTableWithHeader    'format entire table.

    'Renaming columns:
    arr_keep = Split("Account_No,forenames,surname,Mail_Name,title,department,position_,area,organisation,MainTelNo,userdefined4,addressline1,addressline2,addressline3,locality,postown,county,postcode,country", ",")
    new_names = Split("AccountNo,Forenames,Surname,MailName,Title,Department,Position,Area,Organisation,Tel,Email,Addressline1,Addressline2,Addressline3,Locality,PostTown,County,Postcode,Country", ",")
    For i = 1 To ActiveSheet.UsedRange.Columns.count
        If Cells(1, i) = arr_keep(i - 1) Then
            Cells(1, i).Value = new_names(i - 1)
        End If
        If Columns(i).ColumnWidth > 30 Then
            Columns(i).ColumnWidth = 30
        End If
    Next i
    
    'Sorting by MailName:
    ActiveSheet.UsedRange.Sort Key1:=Range("A1"), Order1:=xlAscending, MatchCase:=False, Header:=xlYes
End Sub
'Only delete columns (no rename / no sort):
'Sub DeleteCols4Communigator() 'Icon-Name: "Delete Cols 4 Communigator"
Sub DeleteUnnecessaryCols() 'Icon-Name: "Delete Cols 4 Communigator"
    Dim arr As Variant  'arr must be Variant or String type as Range() & Split() return Sub-Strings (array).
    Dim lastRow As Long, lastCol As Integer, c As Integer, r As Long
    'Deleting unnecessary columns:
    arr = Split("Title,Subscription Rate Types,Price,Em,Ph,Full_Name,mail_name,Master,description,address_type,addressline1,addressline2,addressline3,area,Cost_Price,country,Country_code,county,Customer_Interest,interest,Interests,Subject,Customer_Keyword,keyword,department,DXAddress,Emailable,FaxNo,filecode,Job_No,Journal,Known_As,language,locality,Mailing_Ref,MainTelNo,Telephone,master_account,organisation,position_,Post_Code_Group,Postal_group_periodic,postcode,postcode2,postown,prefix,Purchase_Date,Qty,source_code,Stock_No,Total_Value,type_code,Tel,Phoneable,Start_Date,End_Date,Subscription_rate,Purchase_Price,Quantity,Subs-Type,Renewal,ActiveSub,Disabled,Deceased,Subs_Rate_Type,Cancelled,Lapsed,Position,PositionType", ",")
    For c = ActiveSheet.UsedRange.Columns.count To 1 Step -1
        If (IsInArray(Cells(1, c), arr)) Then
            Cells(1, c).EntireColumn.Delete
        End If
    Next c
    Call RenameTrilogyCols
    Call Delete1AlphabetLongFirstName
    Call FormatEdTableWithHeader    'format entire table.
End Sub


Sub Delete1AlphabetLongFirstName()
    Dim lastCol, c, ForenamesCol As Integer: lastCol = Cells(1, Columns.count).End(xlToLeft).Column
    For c = 1 To lastCol
        If InStr(1, CStr(Cells(1, c)), "Forenames", vbTextCompare) > 0 Or InStr(1, CStr(Cells(1, c)), "First Name", vbTextCompare) > 0 Then
            ForenamesCol = c
            Exit For
        End If
    Next c
    If ForenamesCol = 0 Then
        MsgBox "Dataset doesn't contain a 'Forenames' column."
        Exit Sub
    End If
    Columns(ForenamesCol + 1).Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
    'Cells(1, ForenamesCol + 1) = "Forenames (excluding initials)"
    Cells(1, ForenamesCol + 1) = "Forenames"
    'B2 = Cells(2, ForenamesCol)
    Cells(2, ForenamesCol + 1).Formula = "=IF(OR(LEN(RC[-1])<=1,AND(LEN(RC[-1])=2,RIGHT(RC[-1])=""."")),"""",RC[-1])"
    Dim lastRow As String: lastRow = Cells(Rows.count, ForenamesCol).End(xlUp).Row 'ActiveSheet.UsedRange.Rows.Count
    Set fillDest = Range(Cells(2, ForenamesCol + 1), Cells(lastRow, ForenamesCol + 1))
    Cells(2, ForenamesCol + 1).AutoFill Destination:=fillDest
    Columns(ForenamesCol + 1).Copy
    Columns(ForenamesCol + 1).PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
    Columns(ForenamesCol).EntireColumn.Delete
    'Application.CutCopyMode = False
    'MsgBox "A column ""Forenames (without initials)"" has been inserted next to the ""Forenames"" column" _
    MsgBox "A column ""Forenames (without initials)"" has replaced the ""Forenames"" column" _
    & vbNewLine & "The new columns does not contain single-letter or one character long values in Forenames column."
    
End Sub


'Formats data for Postal Mailing List (i.e. keeps the addresses columns)
'Sub also does following: DeleteCols SwapCol2with3 RenameCols FormatAsTable
Sub DelCol_FormatsForPostalMailingList()
    Dim arr As Variant  'arr must be Variant or String type as Range() & Split() return Sub-Strings (array).
    Dim new_names As Variant
    Dim arr_keep As Variant
    Dim lastRow As Long, lastCol As Integer, c As Integer, r As Long
    'Deleting unnecessary columns:
    arr = Split("record_date,Interest,PositionType,Subject,Acc_No,AccountNo,Reference,Country_code,TelNo,E-Mail,Tel,Email,FaxNo,Title,Forenames,Surname,Account_No,Known_As,language,filecode,area,Journal,type_code,source_code,prefix,MainTelNo,userdefined4,Mailing_Ref,address_type,postcode2,Post_Code_Group,Country_code,DXAddress,FaxNo,Postal_group_periodic", ",")
    For c = ActiveSheet.UsedRange.Columns.count To 1 Step -1
        If (IsInArray(Cells(1, c), arr)) Then
            Cells(1, c).EntireColumn.Delete
        End If
    Next c
    'Call SwapCol2with3              'swaping contents of entire Column-B with Column-C : Position then Dept.
    Call RenameTrilogyCols          'rename columns to more readable.
    'Call FormatEdTableWithHeader    'format entire table.
End Sub
'Following sub swaps contents of entire Column-B with Column-C.
Sub SwapCol2with3()
    Columns("C:C").Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
    Columns("B:B").Cut Destination:=Columns("C:C")
    Columns("D:D").Cut Destination:=Columns("B:B")
    Columns("D:D").Delete Shift:=xlToLeft
End Sub

'Following finds the column header that contains "Email" or "Userdefined4", then runs Excels Remove Duplicate command on it.
Sub DedupeOnEmail()
    Dim lastCol, c, emailCol As Integer: lastCol = Cells(1, Columns.count).End(xlToLeft).Column
    For c = 1 To lastCol
        If InStr(1, CStr(Cells(1, c)), "email", vbTextCompare) > 0 Or InStr(1, CStr(Cells(1, c)), "userdefined4", vbTextCompare) > 0 Then  'on Email only
        'If InStr(1, CStr(Cells(1, c)), "account", vbTextCompare) > 0 Then   'on Account_no
            emailCol = c
            Exit For
        End If
    Next c
    If emailCol <> 0 Then
        ActiveSheet.UsedRange.RemoveDuplicates Columns:=emailCol, Header:=xlYes
    Else
        MsgBox """Email"" column not found." & vbNewLine & vbNewLine & "Worksheet must have a column with header-row containing term ""email""."
    End If
End Sub

'Only rename columns and sort:
Sub RenameTrilogyCols() 'Icon-Name: Rename Trilogy Cols
    Dim lastCol, c As Integer: lastCol = Cells(1, Columns.count).End(xlToLeft).Column
    'Check & rename each columns:
    For c = 1 To lastCol
        Cells(1, c) = WorksheetFunction.Proper(Cells(1, c))
        If Cells(1, c) = "Userdefined4" Then
            Cells(1, c) = "Email"
        ElseIf Cells(1, c) = "Mail_Name" Then Cells(1, c) = "Full_Name"
        ElseIf Cells(1, c) = "Maintelno" Then Cells(1, c) = "Telephone"
        ElseIf Cells(1, c) = "Userdefined4" Then Cells(1, c) = "Email"
        ElseIf Cells(1, c) = "Position_" Then Cells(1, c) = "Position"
        ElseIf Cells(1, c) = "Master_account" Then Cells(1, c) = "Master_Acc"
        'ElseIf Cells(1, c) = "account_no" Then Cells(1, c) = "Account_no"
        'ElseIf Cells(1, c) = "forenames" Then Cells(1, c) = "Forenames"
        'ElseIf Cells(1, c) = "surname" Then Cells(1, c) = "Surname"
        'ElseIf Cells(1, c) = "Mail_name" Then Cells(1, c) = "Full_Name"
        'ElseIf Cells(1, c) = "Title Forenames Surname" Then Cells(1, c) = "Full_Name"
        'ElseIf Cells(1, c) = "department" Then Cells(1, c) = "Department"
        'ElseIf Cells(1, c) = "keyword" Then Cells(1, c) = "Keyword"
        'ElseIf Cells(1, c) = "email" Then Cells(1, c) = "Email"
        'ElseIf Cells(1, c) = "postcode" Then Cells(1, c) = "Post_Code"
        'ElseIf Cells(1, c) = "country" Then Cells(1, c) = "Country"
        'ElseIf Cells(1, c) = "organisation" Then Cells(1, c) = "Organisation"
        'ElseIf Cells(1, c) = "area" Then Cells(1, c) = "Area"
        End If
    Next c
'    ActiveSheet.UsedRange.Sort Key1:=Range("A1"), Order1:=xlAscending, MatchCase:=False, Header:=xlYes
End Sub
'Rename and sort columns:
Sub RenameCols_SortByMailName()
    Dim arr_keep As Variant  'must be Variant or String type as Range() & Split() return Sub-Strings (array).
    Dim new_names As Variant
    Dim lastRow As Long, lastCol As Integer, c As Integer, r As Long

    arr_keep = Split("Account_No,forenames,surname,Mail_Name,department,position_,area,organisation,userdefined4,addressline1,addressline2,addressline3,locality,postown,county,postcode,country", ",")
    'Renaming columns:
    new_names = Split("AccountNo,Forenames,Surname,MailName,Department,Position,Area,Organisation,Email,Addressline1,Addressline2,Addressline3,Locality,PostTown,County,Postcode,Country", ",")
    c = ActiveSheet.UsedRange.Columns.count
    For i = 1 To c
        If Cells(1, i) = arr_keep(i - 1) Then
            Cells(1, i).Value = new_names(i - 1)
        End If
    Next i
'    ActiveSheet.UsedRange.Sort Key1:=Range("A1"), Order1:=xlAscending, MatchCase:=False, Header:=xlYes
End Sub



'Following adds the EdColWidth columns, to find any overly long entries in each column - once finished run Sub DelLenCols().
Sub AddLenCols()
    Dim lastCol, c, col As Integer: c = 0
    Dim lastRow As Long
    Dim colRng As Range
    lastCol = ActiveSheet.UsedRange.SpecialCells(11).Column
    'LastCol = Cells(1, Columns.count).End(xlToLeft).Column
    lastRow = ActiveSheet.UsedRange.SpecialCells(11).Row
    'LastRow = Cells(Rows.count, 1).End(xlUp).row
    For col = 2 To lastCol + 1
        c = c + 2
        Columns(c).Insert Shift:=xlToRight ', CopyOrigin:=xlFormatFromLeftOrAbove
        Cells(1, c) = "EdColWidth"
        Cells(2, c).Formula = "=LEN(RC[-1])"
        Set colRng = Range(Cells(2, c), Cells(lastRow, c))
        If lastRow > 2 Then
            Cells(2, c).AutoFill Destination:=colRng
        End If
        colRng.Value = colRng.Value
        colRng.Interior.ColorIndex = 15
    Next col
    'Range("A1", Cells(LastRow, LastCol)).Sort Key1:=Range("B1"), Order1:=xlDescending, MatchCase:=False, Header:=xlYes
    [A1].CurrentRegion.Sort Key1:=Range("B1"), Order1:=xlDescending, MatchCase:=False, Header:=xlYes
    Range(Columns(1), Columns(lastCol * 2)).AutoFit
End Sub
'Following deletes the EdColWidth columns
Sub DelLenCols()
    Dim col, lastCol As Integer: lastCol = Cells(1, Columns.count).End(xlToLeft).Column
    For col = 2 To lastCol + 1
        If Cells(1, col) = "EdColWidth" Then
            Columns(col).EntireColumn.Delete
        End If
    Next col
End Sub

Sub SortDesc()
    ActiveSheet.UsedRange.Sort Key1:=ActiveCell, Order1:=xlDescending, MatchCase:=False, Header:=xlYes
End Sub


'Tel number validation - Highlight problemetic ones & fix too many white-spaces:
Sub ValidateTelNumber()
    Dim tVal, c, TelNumColumn As String
    'TelNumColumn = "F"       'set target COLUMN here, which contains telephone numbers.
    TelNumColumn = GetTargetColumn()
    If TelNumColumn = "" Then
        End
    End If
    On Error GoTo Fin
    Dim lastRow As Long, r As Long
    Dim re As Object: Set re = CreateObject("VBScript.RegExp")
    Dim i, j, errCount As Integer
    Dim errRow As String: errRow = ""
    Application.ScreenUpdating = False
    lastRow = Cells(Rows.count, 1).End(xlUp).Row
    For r = 2 To lastRow
        tVal = Cells(r, TelNumColumn)

        If Trim(tVal) = "" Then
            errRow = errRow & r & ".    "
            GoTo NextIteration
        End If

        re.Pattern = "^\s+.|.+$\s+" 'check & fix any leading or trailing whitespace(s)
        If re.test(tVal) Then
        'If Left(tVal, 1) = " " Or Right(tVal, 1) = " " Then 'can ONLY check Single space
            tVal = Trim(Cells(r, TelNumColumn))
            Cells(r, TelNumColumn) = tVal  'applying trim() to cell
        End If

        re.Pattern = "\s{2,}"   'remove multiple consecutive spaces
        If re.test(tVal) Then
        're.Replace() returns <arg1> but with matched-patterns (in arg1) replaced with <arg2>
            tVal = re.Replace(tVal, "")     'replacing spaces in tVal variable
            Cells(r, TelNumColumn) = tVal            'writing tVal back on cell
        End If

        re.Pattern = "[^0-9-+()\s]+"
        If re.test(tVal) Then
            Call HighlightCell(Cells(r, EmailColumn))
            errRow = errRow & r & ".    "
            GoTo NextIteration
        End If

        re.Pattern = "[+]{2,}"   'If InStr(2, tVal, "+") > 0 Then
        If re.test(tVal) Then
            Call HighlightCell(Cells(r, EmailColumn))
            errRow = errRow & r & ".    "
            GoTo NextIteration
        End If
      
        re.Pattern = "^0{3,}"  'first 0 repeating consequtively more than 2 times
        If re.test(tVal) Then
            Call HighlightCell(Cells(r, EmailColumn))
            errRow = errRow & r & ".    "
            GoTo NextIteration
        End If

        re.Pattern = "(.)\1{4,}"  'a char repeating consequtively more than 4 times
        If re.test(tVal) Then
            Call HighlightCell(Cells(r, EmailColumn))
            errRow = errRow & r & ".    "
            GoTo NextIteration
        End If

        re.Pattern = "^(.)\1{3,}"  'first char repeating more than 3 times
        If re.test(tVal) Then
            Call HighlightCell(Cells(r, EmailColumn))
            errRow = errRow & r & ".    "
            GoTo NextIteration
        End If
        
        If Len(tVal) < 5 Or Len(tVal) > 30 Then    'checking length min 4 & max 30 chars incl spaces +() etc.
            Call HighlightCell(Cells(r, EmailColumn))
            errRow = errRow & r & ".    "
            GoTo NextIteration
        End If
NextIteration:
    Next r
    errCount = UBound(Split(errRow, ".  "))
    Debug.Print errRow
    MsgBox errCount & " error(s)/blank(s) found in following row-numbers:" & vbNewLine & errRow & vbNewLine & "(row-number(s) with erroneous email addresses also printed in the Immediate window.)"
    'MsgBox errCount & " errors/blanks found."
Fin:
    Set re = Nothing
    If Err.Number <> 0 Then MsgBox "Error: " & Err.Number & " " & Err.Description
    Application.ScreenUpdating = True
End Sub


'Email validation - Highlight problemetic ones & fix too many white-spaces:
Sub ValidateEmail()
    Dim tVal, c As String
    Dim EmailColumn As Variant: EmailColumn = GetColumnNumberByHeaderName("email")
    If EmailColumn = 0 Then
        'EmailColumn = "E"       'set target COLUMN here, which contains email addresses.
        EmailColumn = GetTargetColumn()
        If EmailColumn = "" Then End
    End If
    Application.ScreenUpdating = False
    On Error GoTo Fin
 'LESS RESTRICTIVE EMAIL ADDRESSES:
    'VALID SPECIAL CHARS: Characters !#$%&'*+-/=?^_`{|}~ (ASCII: 33, 35-39, 42, 43, 45, 47, 61, 63, 94-96, 123-126)
    'VALID but IN QUOTES ONLY: Space"(),:;<>@[\] (ASCII: 32, 34, 40, 41, 44, 58, 59, 60, 62, 64, 91-93)
    'VALID IN QUOTES+ESCAPED: space, backslash \ and "quotation mark" (ASCII: 32, 92, 34) must also be preceded by a backslash \ (e.g. "\ \\\"").
 'VERY RESTRICTIVE EMAIL ADDRESSES:
    'ONLY: alphanumerics, dot (.), underscore (_) and hyphen (-)
    Const strREPattern = "^([a-z0-9'_\-\.&]+)@[a-z0-9\-\.]+\.([a-z]{2,4})$"  'IgnoreCase must be used.
    Dim lastRow As Long, r As Long
    Dim i, j, errCount As Integer: errCount = 0
    Dim errRow As String: errRow = ""
    Dim re As Object: Set re = CreateObject("VBScript.RegExp")
    re.IgnoreCase = True
    lastRow = Cells(Rows.count, EmailColumn).End(xlUp).Row
    For r = 2 To lastRow
        tVal = Cells(r, EmailColumn)

        If Trim(tVal) = "" Then
            errRow = errRow & r & ".    "
            GoTo NextIteration
        End If
        
        re.Pattern = "\s+"   'remove spaces (anywhere) in string
        If re.test(tVal) Then
        're.Replace() returns <arg1> but with matched-patterns (in arg1) replaced with <arg2>
            tVal = re.Replace(tVal, "")     'replacing spaces in tVal variable
            Cells(r, EmailColumn) = tVal            'writing tVal back on cell
        End If

        re.Pattern = "(.)\1{3,}"  'a char repeating consequtively more than 4 times
        If re.test(tVal) Then
            Call HighlightCell(Cells(r, EmailColumn))
            errRow = errRow & r & ".    "
            GoTo NextIteration
        End If

        If Len(tVal) <= 5 Or Len(tVal) > 254 Then    'length must be between min 6 & max 254 chars.
            Call HighlightCell(Cells(r, EmailColumn))
            errRow = errRow & r & ".    "
            GoTo NextIteration
        End If

        re.Pattern = strREPattern
        If Not re.test(tVal) Then
            Call HighlightCell(Cells(r, EmailColumn))
            errRow = errRow & r & ".    "
            GoTo NextIteration
        End If
NextIteration:
    Next r
    If errRow = "" Then
        MsgBox "No erroneous email addresses found."
    Else
        errCount = UBound(Split(errRow, ".  "))
        MsgBox errCount & " errors/blanks found in following row-numbers:" & vbNewLine & errRow & vbNewLine & "(row-number also printed in the Immediate window)"
    End If
Fin:
    Set re = Nothing
    If Err.Number <> 0 Then MsgBox "Error: " & Err.Number & " " & Err.Description
    Application.ScreenUpdating = True
End Sub
'Following hightlights the passed cell ref.
Sub HighlightCell(cellAddr As Range)
            cellAddr.Interior.ColorIndex = 19
            cellAddr.Font.ColorIndex = 3
            cellAddr.Font.Bold = True
End Sub

'Sub ValidateIfEUCountries()
Sub ValidateHighlightEUCountries()
    'All EU countries:
        'Albania,Andorra,Armenia,Austria,Azerbaijan,Belarus,Belgium,Bosnia,Bulgaria,Croatia,Cyprus,Czech,Denmark,Estonia,Finland,France,Georgia,Germany,Greece,Hungary,Iceland,Ireland,Italy,Kazakhstan,Kosovo,Latvia,Liechtenstein,Lithuania,Luxembourg,Macedonia,Malta,Moldova,Monaco,Montenegro,Netherlands,Norway,Poland,Portugal,Romania,Russia,Marino,Serbia,Slovakia,Slovenia,Spain,Sweden,Switzerland,Turkey,Ukraine,Vatican
        'FULLNAMES: 'Albania,Andorra,Armenia,Austria,Azerbaijan,Belarus,Belgium,Bosnia and Herzegovina,Bulgaria,Croatia,Cyprus,Czech Republic,Denmark,Estonia,Finland,France,Georgia,Germany,Greece,Hungary,Iceland,Ireland,Italy,Kazakhstan,Kosovo,Latvia,Liechtenstein,Lithuania,Luxembourg,Macedonia,Malta,Moldova,Monaco,Montenegro,Netherlands,Norway,Poland,Portugal,Romania,Russia,San Marino,Serbia,Slovakia,Slovenia,Spain,Sweden,Switzerland,Turkey,Ukraine,Vatican City
    
    Dim countryColNum As Integer: countryColNum = GetColumnNumberByHeaderName("country")  'if not found then 0 is returned.
    If countryColNum = 0 Then
        countryColNum = ColumnLetterToNumber(GetTargetColumn)
    End If
    Dim lastRow As Long: lastRow = Cells(Rows.count, countryColNum).End(xlUp).Row
    Dim arrEUCountries() As String
    arrEUCountries() = Split("Albania,Andorra,Armenia,Austria,Azerbaijan,Belarus,Belgium,Bosnia,Bulgaria,Croatia,Cyprus,Czech,Denmark,Estonia,Finland,France,Georgia,Germany,Greece,Hungary,Iceland,Ireland,Italy,Kazakhstan,Kosovo,Latvia,Liechtenstein,Lithuania,Luxembourg,Macedonia,Malta,Moldova,Monaco,Montenegro,Netherlands,Norway,Poland,Portugal,Romania,Russia,Marino,Serbia,Slovakia,Slovenia,Spain,Sweden,Switzerland,Ukraine,Vatican", ",")
    For r = 2 To lastRow
        For Each country In arrEUCountries
            If InStr(1, Cells(r, countryColNum), country, vbTextCompare) Then
            'If Cells(r, countryColNum) Like "*" & country & "*" Then   'Requires: Option Compare Text
                Cells(r, countryColNum).Interior.ColorIndex = 24
                Exit For
            End If
        Next
    Next r
End Sub

Sub TestLikeOperator()
    x = "abc"
    MsgBox "zzzabcssss" Like "*" & x & "*"    'Requires: Option Compare Text
    'MsgBox InStr(1, "zzzabcssss", "axc", vbTextCompare)
End Sub

Sub ValidateCountryNames()
'All EU countries:
    'Albania,Andorra,Armenia,Austria,Azerbaijan,Belarus,Belgium,Bosnia and Herzegovina,Bulgaria,Croatia,Cyprus,Czech Republic,Denmark,Estonia,Finland,France,Georgia,Germany,Greece,Hungary,Iceland,Ireland,Italy,Kazakhstan,Kosovo,Latvia,Liechtenstein,Lithuania,Luxembourg,Macedonia,Malta,Moldova,Monaco,Montenegro,Netherlands,Norway,Poland,Portugal,Romania,Russia,San Marino,Serbia,Slovakia,Slovenia,Spain,Sweden,Switzerland,Turkey,Ukraine,Vatican City
'All countries:
'"AFGHANISTAN REPUBLIC#   ALBANIA REPUBLIC OF#    ANDORRA#    ANGOLA REPUBLIC OF# ANGUILLA (I.O. BR.)#    ANTIGUA AND BARBUDA#    ARGENTINA#  ARMENIA REPUBLIC OF#    ARUBA, (CODE: NAN)# AUSTRALIA#  AZERBAIJAN REPUBLIC#    AZORES AND MADERA#  BAHRAIN#    BARBADOS#   BANCA COMMERCIALE ITALIANA (FAO)#   BURUNDI#    BELGIUM#    BENIN#  BERMUDA#    BANGLADESH# BAHAMAS#    BHUTAN# BOSNIA AND HERZEGOVINA# BURKINA FASO#   BOLIVIA#    BONAIRE#    BOTSWANA REPUBLIC OF#   BRAZIL# BRUNEI# BULGARIA#   BRITISH VIRGIN ISLANDS# BELARUS REPUBLIC OF#    BELIZE# CENTRAL AFRICAN REPUBLIC#   CANADA# C.A.S.A.#   CAYMAN ISLANDS# CARIBBEAN COMMUNITY#    CZECH REPUBLIC THE# CHANNEL IS., (CODE: UK)#    CHAD#   CHILE#  COOK ISLANDS#   CAMBODIA KINGDOM OF#    CAMEROON REPUBLIC OF#   CORSE, FRANCIA (CODE: FRA)# COMOROS#    COLOMBIA#   COSTA RICA# CHINA PEOPLE'S REPUBLIC#    FORMER CODE OF: MIC#    CROATIA REPUBLIC OF#    CUBA#   CURACAO#    CAPE VERDE# CYPRUS# DENMARK#    DJIBOUTI#   DOMINICA#   DOMINICAN REPUBLIC
'# KOREA PEOPLES DEMOCRATIC REPUBLIC# ECUADOR#    EGYPT#  EL SALVADOR#    EQUATORIAL GUINEA#  ERITREA#    ESTONIA REPUBLIC OF#    ETHIOPIA#   IS.FAEROE,DANIMARCA (CODE:DEN)# FRENCH ANTILLES#    FERNANDO POO, (CODE: EQG)#  FFOA - FORMER FAO/WFP STAFF MEMBERS#    FRENCH GUIANA#  FIJI#   FINLAND#    IS. FALKLAND#   FRENCH POLYNESIA#   FRANCE# GABON#  GAMBIA REPUBLIC OF THE# GUINEA-BISSAU#  GEORGIA REPUBLIC OF#    GERMANY#    GHANA#  GIBRALTAR#  CONFERENCE,COUNCIL & PROTOCOL AFF.DIV.# GREECE# GREENLAND#  GRENADA#    GUATEMALA#  GUADELOUPE# GUINEA# GUYANA# HAITI#  HOLY SEE (CITTA DEL VATICANO)#  HONG KONG#  HONDURAS#   HORN, CAPE (CODE: CHI)# HUNGARY#    ICELAND#    INDIA#  INDONESIA#  IRAN#   IRAQ#   ISRAEL# ITALY#  COTE D'IVOIRE#  JAMAICA#    JORDAN HASHEMITE KINGDOM#   JAPAN#  KAZAKHSTAN REPUBLIC#    KENYA REPUBLIC OF#  KIRIBATI#   KUWAIT# KYRGYZSTAN REPUBLIC#    LAO PEOPLE'S DEMOCRATIC REPUBLIC#   LATVIA REPUBLIC OF# LEBANON#    LEGAL OFFICE#   LEEWARD IS.#    LESOTHO#    LIBYAN ARAB JAMAHIRIYA# LIECHTENSTEIN
'#  LIBERIA#    LITHUANIA REPUBLIC OF# LUXEMBOURG GRAND DUC.#  ISLE OF MAN, (CODE: UK)#    MACAO#  MADERA (CODE: AZR)# MADAGASCAR# MARTINIQUE# MAURITIUS REPUBLIC OF#  MARSHALL ISLANDS REP.#  MAURITANIA# THE FORMER YUGOSLAV (MACEDONIA)#    MALDIVES#   MEXICO# MICRONESIA FEDERATED#   MALI#   MALAWI# MONACO# MOLDOVA, REPUBLIC OF#   MONGOLIA#   MOROCCO#    MONTSERRAT# MOZAMBIQUE# IS. MARIANNE#   MYANMAR UNION OF#   NAMIBIA REPUBLIC OF#    NETHERLANDS ANTILLES#   NAURU#  NEW CALEDONIA#  NEPAL#  NIGER#  NICARAGUA#  NIGERIA#    NIUE ISLAND#    NORWAY# SULTANATE OF OMAN#  PAKISTAN#   PALESTINE#  PANAMA# PARAGUAY#   PERU#   PHILIPPINES#    PAPUA NEW GUINEA#   POLAND# PORTUGAL#   CONGO REPUBLIC OF#  PUERTO RICO#    QATAR#  REUNION#    KOREA REPUBLIC OF#  ROMANIA#    RWANDA# IS. RYUKYU# SOUTH AFRICA#   SAMOA#  SARAWAK (CODE: MAL)#    SAUDI ARABIA, KINGDOM OF#   SENEGAL#    SEYCHELLES# SIERRA LEONE#   SINGAPORE#  SLOVAK REPUBLIC#    ST.MARTIN, (CODE: NAN)# SOVEREIGN ORDER OF MALTA#   SAN MARINO
'# SOLOMON ISLANDS#    SPAIN#  SRI LANKA#  ST.CHRISTOPHER & NEVIS (CODE: STK)#    ST. EUSTATIUS (NETHERLANDS ANTILLES)#   ST. KITTS & NEVIS#  ST. LUCIA#  ST. MARTIN (NETHERLANDS ANTILLES)#  SAO TOME AND PRINCIPE#  ST. VINCENT & THE GRENADINES#   SUDAN#  SURINAME#   SLOVENIA, REPUBLIC OF#  SWAZILAND#  SWEDEN# SYRIAN ARAB REPUBLIC#   TAJIKISTAN REPUBLIC#    TURKS & CAICOS ISLANDS# THAILAND#   IS. TIMOR#  TOGO#   TOKELAU ISLANDS#    TONGA#  TRINIDAD AND TOBAGO#    U.S. TRUST TERRITORI#   TURKMENISTAN#   TUNISIA#    TURKEY# TUVALU# TAIWAN (FORMOSA)#   UNITED ARAB EMIRATES#   UGANDA# UK# UKRAINE#    TANZANIA UNITED REPUBLIC#   URUGUAY#    UNITED STATES OF AMERICA#   RUSSIAN FEDERATION# US VIRGIN ISLANDS#  UZBEKISTAN REPUBLIC#    VANUATU#    VENEZUELA#  VIET NAM#   VIRGIN ISLANDS (CODES: BVI - UVI)#  UNITED NATIONS WORLD FOOD COUNCIL#  WORLD FOOD DAY (CODE: GII)# WALLIS & FUTUNA IS.#    WEST IRIAN, (CODE: INS)#    WINDWARD AND LEEWARD#   YEMEN REPUBLIC OF#  YUGOSLAVIA# ZAIRE#  ZAMBIA
'# ZIMBABWE#   NORTHERN IRELAND#   Conferences UK# AUSTRIA#    ST. HELENA ISLAND#  SOMALIA#   NETHERLANDS#    SERBIA# ALGERIA#    MALTA#  IRELAND#    MALAYSIA#   NEW ZEALAND#    SWITZERLAND#"
End Sub

Function EdFuzzyMatch(s1 As String, s2 As String)
    s1 = Trim(s1)
    s2 = Trim(s2)
    If s1 = s2 Then
        EdFuzzyMatch = 100
        Exit Function
    End If
    'strLen = IIf(Len(s1) > Len(s2), s1, s2)  'whichever is longer.
    strLen = IIf(Len(s1) < Len(s2), s1, s2)  'whichever is shorter.
    Similarity = 0
    For c = 1 To strLen
        If InStr(1, s1, Mid(s2, c, 1), 1) Then
        Similarity = Similarity + 1
        'Str1 = Replace(Str1, Mid(Str2, c, 1), "*", 1, 1)
        End If
    Next c
Done:
    EdFuzzyMatch = 100 * Similarity / strLen
End Function

'"Application." vs "Application.Worksheetfunction."):
'    If error occurs, "Application.<function>" does not throws any errors, whereas
'    "Application.Worksheetfunction.<function>" throws RunTime error, hence requires you to provide
'    some error-handle mechanism (e.g. using: ON ERROR RESUME NEXT or GOTO <named-block>).
Sub MatchFunctionErrorHandlingTest()
    If IsError(Application.Match(38, Array(2, 3, 4, 6), 0)) Then    'No error
    'If IsError(Application.WorksheetFunction.Match(38, Array(2, 3, 4, 6), 0)) Then  'throws runtime-error if no match found.
        'x = Application.Match(Cells(2, "B"), "A2:A41", 0)
        'MsgBox TypeName(x)
        MsgBox "Error: No match found"
    Else
        MsgBox "no err: match found"
    End If
End Sub




'Follwing code collapses the table structured, left-align data with staggerred right-most columns removing any gaps/empty-cells.
'Slower-Execution-Version-1: uses Sheet-Navigation (using "Goto Cells().End(xlToLeft)"), instead of traversing each cell in (each) row.
Sub CollapseTableStructureLeftAglignData_byNavigating()
    Call ConfirmRun("CollapseTableStructureLeftAglignData_byNavigating")
    Dim sngStart, sngEnd, sngElapsed As Single
    sngStart = Timer                               ' Get start time.

    ScreenUpdating = False
    endRow = ActiveSheet.UsedRange.Rows.count
    'EndRow = Cells(Rows.Count, 1).End(xlUp).Row
    'EndRow = 260
    For r = endRow To 1 Step -1
        Application.GoTo Cells(r, Columns.count).End(xlToLeft)
        Do
            Selection.End(xlToLeft).Select    'Select last column
            If ActiveCell.Column = 1 Then
                Exit Do
            End If
            If IsEmpty(ActiveCell.Offset(0, -1)) Then
                ActiveCell.Offset(0, -1).Delete Shift:=xlToLeft
            End If
        Loop Until ActiveCell.Column = 1
    Next r
    ScreenUpdating = True
    
    sngEnd = Timer                                 ' Get end time.
    sngElapsed = Format(sngEnd - sngStart, "Fixed") ' Elapsed time.
    MsgBox ("Empty Columns deleted." & vbNewLine & vbNewLine & "The code took " & sngElapsed & " seconds to run.")

End Sub

'Follwing code collapses the table structured, left-align data with staggerred right-most columns removing any gaps/empty-cells.
'Faster-Execution-Version-2: traverses/loops each cell in (each) row (delete if empty), instead of using Sheet-Navigation (using "Goto Cells().End(xlToLeft)")
Sub CollapseTableStructureLeftAglignData()
    Call ConfirmRun("CollapseTableStructureLeftAglignData")
    Dim aCell As Range
    Set aCell = Rows(1).Find(What:="addressline", LookIn:=xlValues, MatchCase:=False, LookAt:=xlPart, SearchFormat:=False)
    If Not aCell Is Nothing Then
        aCell.EntireRow.Delete
    End If

    Dim sngStart, sngEnd, sngElapsed As Single
    sngStart = Timer                               ' Get start time.

    ScreenUpdating = False
    endRow = ActiveSheet.UsedRange.Rows.count
    'EndRow = Cells(Rows.Count, 1).End(xlUp).Row
    'EndRow = 260
    For r = endRow To 1 Step -1
        'For c = Cells(r, Columns.Count).End(xlToLeft).Offset(0, -1).Column To 1 Step -1 'code breaks if 1st-column has only white-space
        For c = Cells(r, Columns.count).End(xlToLeft).Column To 1 Step -1
            If IsEmpty(Cells(r, c)) Then
                Cells(r, c).Delete Shift:=xlToLeft
            End If
        Next c
    Next r
    ScreenUpdating = True
    
    sngEnd = Timer                                 ' Get end time.
    sngElapsed = Format(sngEnd - sngStart, "Fixed") ' Elapsed time.
    MsgBox ("Empty Columns/Cells Deleted, your data is no longer structured." & vbNewLine & vbNewLine & "The code took " & sngElapsed & " seconds to run.")

End Sub

'Pleace the cellpointer/cursor in the column where you want an additional column containing duplicate-row-counts to be inserted.
'Following code inserts an additional column at current cursor posion, then populates it with duplicate-row-counts.
'Note: this only looks/eveluates the columns on the left of the Current Cursor Position.
'Assumptions: Data is in tabular structure & has Top-Header-Row.
Sub DuplicateRowFinder()
    Dim col As Variant
    col = Application.InputBox(Prompt:="Enter column letter, where an additional column with duplicate-row-counts can be inserted. (e.g. C for third column)" _
        & vbNewLine & vbNewLine & "Note: only columns on the left are checked for duplications.", Title:="Column Letter")
    Debug.Print TypeName(col) & " - " & col
    If col = False Then  'user cancelled - clicking 'Cancel' button returns Boolean-False (NOT vbCancel).
        End
    ElseIf col = "" Then    'user clicked OK without inputing anything, returns empty-string "".
        MsgBox "No column letter entered, goodbye."
        End
    End If
    Dim lastCol, concatCol, sumProd, arg1, arg2 As String: lastCol = Range(col & "1").Column
    Dim lastRow, concatRow As String: lastRow = ActiveSheet.UsedRange.Rows.count
    Dim fillDest As Range
    Columns(lastCol).EntireColumn.Insert Shift:=xlToRight
    'ActiveCell.End(xlUp).Select
    'Range(col & "1").Select
'    ActiveCell.Value
    Range(col & "1") = "Duplicate-Row-Count"
    Range(col & "2").Select
    'ActiveCell.Offset(1, 0).Select
    Set fillDest = Range(Cells(2, lastCol), Cells(lastRow, lastCol))
    arg1 = "A" & ActiveCell.Row
    arg2 = "$A$2:$A$" & lastRow
    For i = 2 To lastCol - 1
        concatCol = Split(Cells(ActiveCell.Row, i).Address(True, False), "$")(0)
        concatRow = concatRow & ActiveCell.Row
        arg1 = arg1 & "&" & concatCol & ActiveCell.Row
        arg2 = arg2 & "&" & "$" & concatCol & "$2:$" & concatCol & "$" & lastRow
    Next i
    sumProd = "=SUMPRODUCT(--(" & arg1 & "=" & arg2 & "))"
    ActiveCell.Formula = sumProd
    ActiveCell.AutoFill Destination:=fillDest
    ActiveSheet.UsedRange.Sort Key1:=Cells(1, lastCol), Order1:=xlDescending, MatchCase:=False, Header:=xlYes
    fillDest.Interior.ColorIndex = 24
    'MsgBox "If the 'Duplicate-Row-Count' column contains value >= 2, then delete the duplicate row(s) until it shows '1'", vbInformation
    MsgBox "All duplicate rows will contain value >= 2 in the 'Duplicate-Row-Count' column (and unique rows will have value 1)", vbInformation
End Sub


'Following extracts unique values from all the rows in Column-A and insert them in Col-A replacing existing values:
'This is useful to remove duplicate and extract/get distinct values from each row, which is inserted in the last row.
Sub InsertUniqueValues_RemoveDupes()
Dim rng As Range, c As Range, s, i As Long
Application.ScreenUpdating = False
Set rng = Range("A1:A" & Range("A" & Rows.count).End(xlUp).Row)
With CreateObject("Scripting.Dictionary")
  For Each c In rng
    If c <> "" Then
      If InStr(c, ",") Then  'comma-separated.
      'If InStr(c, " ") Then  'space-separated.
        s = Split(Trim(c), ",")  'comma-separated.
        's = Split(Trim(c), " ")  'space-separated.
        For i = LBound(s) To UBound(s)
          If Not .Exists(s(i)) Then
            .Add s(i), s(i)
          End If
        Next i
        c.Value = Join(.Keys, ",")
      End If
    End If
  Next c
End With
Application.ScreenUpdating = True
End Sub


'Following code inserts a new sheet with list of account_nos that need to be marked as DoNotSendEmail in Trilogy.
Sub BouncedEmailListUpdate()
'MostRecentBounceReason
    Dim lastRow As Long
    Dim Sh As Worksheet
    Dim bounceReasonCol As Integer: bounceReasonCol = GetColumnNumberByHeaderName("MostRecentBounceReason")
    If bounceReasonCol = 0 Then
        MsgBox "MostRecentBounceReason column-header not found on current sheet, can't export Bournced email list."
        End
    End If
    ActiveSheet.UsedRange.AutoFilter Field:=bounceReasonCol, Criteria1:=Array("bad-configuration", _
        "bad-domain", "bad-mailbox", "blacklist-related", "Complaint", _
        "content-related", "inactive-mailbox", "invalid-sender", "policy-related", _
        "reputation-related", "spam-related", "Unknown"), Operator:=xlFilterValues
        
    Dim accNumCol As Integer: accNumCol = GetColumnNumberByHeaderName("Accountnumber")
    If accNumCol = 0 Then
        MsgBox "Accountnumber column-header not found on current sheet, can't export Bournced email list."
        End
    End If
    ActiveSheet.UsedRange.AutoFilter Field:=accNumCol, Criteria1:="<>"
    Set Sh = Sheets.Add(after:=ActiveSheet)
    [A1] = "AccountNo"
    [B1] = "DoNotEmail"
    [C1] = "EmailDeclined"
    Sheets(1).Select      'ActiveSheet.Next.Select
    Range(Cells(2, accNumCol), Cells(Rows.count, accNumCol).End(xlUp)).Copy Destination:=Sheets(2).Range("A2")
    Sheets(2).Select
'    Range("B2") = "Y"
'    Range("C2") = "Y"
    'LastRow = Cells(Rows.count, "A").End(xlUp).row
    lastRow = GetLastRow(Sh)
'    Range("B2").AutoFill Destination:=Sheets(2).Range("B2:B" & lastRow)
'    Range("C2").AutoFill Destination:=Sheets(2).Range("C2:C" & lastRow)
    Sheets(2).Range("B2:B" & lastRow) = "Y"
    Sheets(2).Range("C2:C" & lastRow) = "Y"
    Range("A1").Select
End Sub



'Following code inserts a new sheet with list of account_nos that need to be marked as DoNotSendEmail in Trilogy.
Sub UnsubscribedEmailListUpdate2()
'MostRecentBounceReason
    Dim targetCol As Integer: targetCol = GetColumnNumberByHeaderName("MostRecentUnsubscribeCampaign")
    If targetCol = 0 Then
        MsgBox "MostRecentUnsubscribeCampaign column-header not found in current sheet, can't export Unsubscribed list."
        End
    End If
    'ActiveSheet.UsedRange.AutoFilter Field:=targetCol, Criteria1:=Array("bad-domain", "bad-mailbox", "Unknown"), Operator:=xlFilterValues

    Dim accNumCol As Integer: accNumCol = GetColumnNumberByHeaderName("Accountnumber")
    If accNumCol = 0 Then
        MsgBox "Accountnumber column-header not found on current sheet, can't export Unsubscribed email list."
        End
    End If
    ActiveSheet.UsedRange.AutoFilter Field:=accNumCol, Criteria1:="<>"
    Sheets.Add after:=ActiveSheet
    [A1] = "AccountNo"
'    [B1] = "Keywords"
    [B1] = "DoNotEmail"
'    [C1] = "EmailDeclined"
    Sheets(1).Select      'ActiveSheet.Next.Select
    Range(Cells(2, accNumCol), Cells(Rows.count, accNumCol).End(xlUp)).Copy Destination:=Sheets(2).Range("A2")
    'If ActiveSheet.AutoFilterMode Then ActiveSheet.ShowAllData  'clear the AutoFilter
    If ActiveSheet.AutoFilterMode Then Cells.AutoFilter  'clear the AutoFilter
    Sheets(2).Select
    Range("B2") = "Y"
'    Range("C2") = "Y"
'    Sheets(2).Range("B2:B" & lastRow) = "unengaged_socialcare_Mar17"
    lastRow = Cells(Rows.count, "A").End(xlUp).Row
''    Range("B2").AutoFill Destination:=Sheets(2).Range("B2:B" & lastRow)
    Sheets(2).Range("B2:B" & lastRow) = "Y"
''    Range("C2").AutoFill Destination:=Sheets(2).Range("C2:C" & lastRow)
'    'Sheets(2).Range("C2:C" & lastRow) = "Y"

    Range("A1").Select
End Sub



'Following code inserts a new sheet with list of account_nos that need to be marked as DoNotSendEmail in Trilogy.
Sub UnsubscribedEmailListUpdate() '- Keywords update
'MostRecentBounceReason
    Dim targetCol As Integer: targetCol = GetColumnNumberByHeaderName("MostRecentUnsubscribeCampaign")
    If targetCol = 0 Then
        MsgBox "MostRecentUnsubscribeCampaign column-header not found in current sheet, can't export Unsubscribed list."
        End
    End If
    'ActiveSheet.UsedRange.AutoFilter Field:=targetCol, Criteria1:=Array("bad-domain", "bad-mailbox", "Unknown"), Operator:=xlFilterValues

    Dim accNumCol As Integer: accNumCol = GetColumnNumberByHeaderName("Accountnumber")
    If accNumCol = 0 Then
        MsgBox "Accountnumber column-header not found on current sheet, can't export Unsubscribed email list."
        End
    End If
    ActiveSheet.UsedRange.AutoFilter Field:=accNumCol, Criteria1:="<>"
    Sheets.Add after:=ActiveSheet
    [A1] = "AccountNo"
    [B1] = "Keywords"
'    [B1] = "DoNotEmail"
'    [C1] = "EmailDeclined"
    Sheets(1).Select      'ActiveSheet.Next.Select
    Range(Cells(2, accNumCol), Cells(Rows.count, accNumCol).End(xlUp)).Copy Destination:=Sheets(2).Range("A2")
    'If ActiveSheet.AutoFilterMode Then ActiveSheet.ShowAllData  'clear the AutoFilter
    If ActiveSheet.AutoFilterMode Then Cells.AutoFilter  'clear the AutoFilter
    Sheets(2).Select
'    Range("B2") = "Y"
'    Range("C2") = "Y"
    lastRow = Cells(Rows.count, "A").End(xlUp).Row
''    Range("B2").AutoFill Destination:=Sheets(2).Range("B2:B" & lastRow)
''    Range("C2").AutoFill Destination:=Sheets(2).Range("C2:C" & lastRow)
    Sheets(2).Range("B2:B" & lastRow) = "unengaged_fire_Mar17"  ' "unengaged_mh_Mar17" ' "unengaged_ld_Mar17"  ' "unengaged_elt_Mar17" ' "unengaged_socialcare_Mar17"
'    Sheets(2).Range("B2:B" & lastRow) = "Y"
'    'Sheets(2).Range("C2:C" & lastRow) = "Y"

    Range("A1").Select
End Sub



'Following code jumps / goes to current week.
'assumptions: code assumes that Column A contains dates,
'a series of first day of each week (e.g.: 08-Aug-2016, 15-Aug-2016, 22-Aug-2016 and so on.)
Sub goto_curr_week()
    Dim cell As Range, _
        wcf As Range
    Dim today As Long     'must compare dates in Long otherwise problematic.
    today = CLng(Date)    'converting current date in to Long.
    For Each cell In Range("A12:A9999")
        'Set wcf = Range("d1").Find(cell.Value)
        If today >= CLng(cell.Value) And today < CLng(cell.Offset(1, 0).Value) Then
            Set wcf = cell
            Exit For
        End If
    Next cell
    If Not wcf Is Nothing Then
        Debug.Print wcf.Value       'Ctrl+G to open Immediate window where Debug-stmts print.
        Application.GoTo wcf, True  'Application.Goto requires a range.
    Else
        MsgBox "Date range not found, Eddie to check Macro."
    End If
End Sub

'Following inserts the SubTotal() formula in current cell:
Sub InsertSubTotalOfVisibleOnlyCells()
    Dim currColLetter As String: currColLetter = ColumnNumberToLetter(ActiveCell.Column)
    Dim currRowNum As Long: currRowNum = ActiveCell.Row
    ActiveCell.Formula = "=SUBTOTAL(103," & currColLetter & "2:" & currColLetter & (currRowNum - 1) & ")"
End Sub

Sub DedupeRowsBy1PositionsPerOrganisation()
    Application.ScreenUpdating = False
    Dim r, lastRow As Long: lastRow = ActiveSheet.UsedRange.Rows.count 'Cells(Rows.Count, 1).End(xlUp).row
    Dim org, pos As String
    For r = lastRow To 2 Step -1 ' ActiveSheet.UsedRange.Rows.Count
        'D & K: If (Cells(r, "D") = Cells(r - 1, "D") And Cells(r, "D") <> "" And Cells(r, "K") = Cells(r - 1, "K") And Cells(r, "K") <> "") Then
        If Cells(r, "N") = Cells(r - 1, "N") Then
        'If (Cells(r, "G") = Cells(r - 1, "G") And Cells(r, "Q") = Cells(r - 1, "Q") And Cells(r, "H") <> "") Then
          If (Cells(r, "E") = Cells(r - 1, "E") And Cells(r, "E") <> "") Then
            'InStr(1, "Hello World", "Hello W") = 1
            'Cells(r, "A") = ""
            Rows(r).EntireRow.Delete
            'Rows(r).EntireRow.Hidden = True
          End If
        End If
    Next r
    Application.ScreenUpdating = True
    MsgBox "Done..."
End Sub

'Following sub dedupes the Subscribers' list - it eliminates duplication caused by Invoice & Delivery addresses (for accounts with 2 addresses.)
'Assumptions:
'1) Data must be sorted in Acc_No's ascending order.
'2) Columns must be in following order:
'Acc_No,Full_Name,Organisation,Postown,PostCode,Country,Country_Code,Address_Type_Id,Email,Subs_Rate_Type,Master_Acc,Start_Date,End_Date,Purchase_Price,Quantity,Revenue,Title,Subs-Type,ActiveSub,Renewal,Area
Sub DedupeSubscribersWithBothInvoiceAndDeliveryAddr()   'for Emma's Geo-Split:
    Application.ScreenUpdating = False
    Dim r, lastRow As Long: lastRow = ActiveSheet.UsedRange.Rows.count + 1 'Cells(Rows.Count, 1).End(xlUp).row
    Dim org, pos As String
    For r = lastRow To 2 Step -1 ' ActiveSheet.UsedRange.Rows.Count
        'Assuming Columns (in next line): A = acc_no, L & M are Start/End dates,  N = amount, and Q = title.
        If Cells(r, "A") = Cells(r - 1, "A") And Cells(r, "L") = Cells(r - 1, "L") And Cells(r, "M") = Cells(r - 1, "M") And Cells(r, "N") = Cells(r - 1, "N") And Cells(r, "Q") = Cells(r - 1, "Q") Then
            'removing Invoice Addr (keeping Delevery Addr) Assuming Delevery Addr is valid (should be valid as these are subscribers).
            If Cells(r, "H") = 3 Then  'AddrType: 3 is Invoice Addr.  4 is Delivery Addr.
                Rows(r).EntireRow.Delete
                'Rows(r).EntireRow.Hidden = True
            ElseIf Cells(r - 1, "H") = 3 Then
                Rows(r - 1).EntireRow.Delete
                'Rows(r - 1).EntireRow.Hidden = True
            End If
        End If
    Next r
    Application.ScreenUpdating = True
    MsgBox "Done..."
End Sub
'''Sub DedupeRowsBySimpleNextPreviousValueComparison()


'Following sub dedupes the Address data - it eliminates duplication caused by Invoice & Delivery addresses (for accounts with 2 addresses.)
    'it deletes invoice-address only if delivery-address exists for an account.
'Assumptions:
'1) Data must be sorted in Acc_No's ascending order.
'2) Columns must be in following order:
'Acc_No_email,Full_Name,position_,department,organisation,addressline1,addressline2,addressline3,locality,county,postown,postcode,country,Area,PositionType,Subject,Interest,record_date,address_type
Sub DedupeAddressInvoiceVsDeliveryV1() 'used for Catalogue2017
    Application.ScreenUpdating = False
    Dim r, lastRow As Long: lastRow = ActiveSheet.UsedRange.Rows.count 'Cells(Rows.Count, 1).End(xlUp).row
    Dim org, pos As String
    For r = lastRow To 2 Step -1 ' ActiveSheet.UsedRange.Rows.Count
        'In next line: A = acc_no:
        If Cells(r, "B") = Cells(r - 1, "B") Then
            If Cells(r, "R") = Cells(r - 1, "R") Then
                If Cells(r, "U") = "" And Cells(r - 1, "U") <> "" Then
                    Rows(r).EntireRow.Delete
                ElseIf Cells(r, "U") <> "" And Cells(r - 1, "U") = "" Then
                    Rows(r - 1).EntireRow.Delete
                ElseIf (Cells(r, "U") = "" And Cells(r - 1, "U") = "") Or (Cells(r, "U") <> "" And Cells(r - 1, "U") <> "") Then
                    If Cells(r, "AC") = "3" Then
                        Rows(r).EntireRow.Delete
                    ElseIf Cells(r - 1, "AC") = "3" Then
                        Rows(r - 1).EntireRow.Delete
                    Else
                        Rows(r).EntireRow.Delete
                    End If
                End If
            End If
        End If
    Next r
    Application.ScreenUpdating = True
    MsgBox "Done... Addresses Deduped."
End Sub

'''Sub DedupeRowsBySimpleNextPreviousValueComparison()
Sub DedupeAddressInvoiceVsDeliveryV2()
    Application.ScreenUpdating = False
    Dim r, lastRow As Long: lastRow = ActiveSheet.UsedRange.Rows.count + 1 'Cells(Rows.Count, 1).End(xlUp).row
    Dim org, pos As String
    For r = lastRow To 2 Step -1
        If Cells(r, "A") = Cells(r - 1, "A") And Cells(r, "B") = Cells(r - 1, "B") Then ' And Cells(r, "M") = Cells(r - 1, "M") Then
            'InStr(1, "Hello World", "Hello W") = 1 Then
            If Cells(r, "K") = "" And Cells(r, "E") = "" Then
                'Rows(r).EntireRow.Delete
                Rows(r).EntireRow.Hidden = True
            ElseIf Cells(r - 1, "K") = "" And Cells(r - 1, "E") = "" Then
                'Rows(r - 1).EntireRow.Delete
                Rows(r - 1).EntireRow.Hidden = True
            ElseIf Cells(r, "M") = "Invoice Address" Then
                'Rows(r).EntireRow.Delete
                Rows(r).EntireRow.Hidden = True
            ElseIf Cells(r - 1, "M") = "Invoice Address" Then
                'Rows(r - 1).EntireRow.Delete
                Rows(r - 1).EntireRow.Hidden = True
            End If
        End If
    Next r
    Application.ScreenUpdating = True
    MsgBox "Done..."
End Sub

'''Sub DedupeRowsBySimpleNextPreviousValueComparison()
Sub DedupeAddressInvoiceVsDeliveryV3()
    Application.ScreenUpdating = False
    Dim r, lastRow As Long: lastRow = ActiveSheet.UsedRange.Rows.count + 1 'Cells(Rows.Count, 1).End(xlUp).row
    Dim org, pos As String
    For r = lastRow To 2 Step -1
        If Cells(r, "A") = Cells(r - 1, "A") And Cells(r, "N") = Cells(r - 1, "B") Then ' And Cells(r, "M") = Cells(r - 1, "M") Then
            'InStr(1, "Hello World", "Hello W") = 1 Then
            If Cells(r, "K") = "" And Cells(r, "E") = "" Then
                'Rows(r).EntireRow.Delete
                Rows(r).EntireRow.Hidden = True
            ElseIf Cells(r - 1, "K") = "" And Cells(r - 1, "E") = "" Then
                'Rows(r - 1).EntireRow.Delete
                Rows(r - 1).EntireRow.Hidden = True
            ElseIf Cells(r, "M") = "Invoice Address" Then
                'Rows(r).EntireRow.Delete
                Rows(r).EntireRow.Hidden = True
            ElseIf Cells(r - 1, "M") = "Invoice Address" Then
                'Rows(r - 1).EntireRow.Delete
                Rows(r - 1).EntireRow.Hidden = True
            End If
        End If
    Next r
    Application.ScreenUpdating = True
    MsgBox "Done..."
End Sub


'To avoid "Out of subscription" error, use UBound(): (unfortunately VBA doesn't provide specific operator or function for this.)
Sub CheckIfArrayElementInitialized()
    Dim arr() As String
    arr() = Split("3-25", "-")
    If UBound(arr) = -1 Then    'array is not initialised - if not checked here Match() will return "Type mismatch" error.
    'OR
    If UBound(arr) >= 1 Then If arr(1) <> "" Then MsgBox (arr(1))  'Note: you can't use isEmpty() on array-elements.
End Function
'check if empty string: use isEmpty() to check if a variable has been assigned.
Sub CheckIfVariableAssigned()
    x = 0
    y = ""
    MsgBox "IsEmpty(x): " & IsEmpty(x) & "   IsEmpty(y): " & IsEmpty(y) 'both FALSE as they're initialised.
End Sub

Sub CheckEmptyString()
    x = ""
    If Len(Trim(x)) = 0 Then
    'If Len(Trim([A1])) = 0 Then
        MsgBox "x is empty string"
    End If
End Sub

'Following sub displays First & Last Rows of UsedRange:
Sub FirstAndLastRowsInUsedRange()
    firstRow = ActiveSheet.UsedRange.Rows        ' method-1
    firstRow = ActiveSheet.UsedRange.Rows(1).Row ' method-2
    lastRow = ActiveSheet.UsedRange.SpecialCells(11).Row
    MsgBox firstRow & " - " & lastRow
End Sub

Sub HideMultipleRowsAtOnce()
    Range("3:3, 6:6, 8:8").EntireRow.Hidden = True      'method-1
    Rows("2:15062").EntireRow.Hidden = True             'method-2
End Sub

Sub FindInUnionedRange()
    Dim aCell, rngCols As Range
    Set rngCols = Range("B1")
    Set rngCols = Union(rngCols, Range(Cells(3, "B"), Cells(5, "b")), Range(Cells(3, "D"), Cells(5, "D")))
    rngCols.Select
    Set aCell = rngCols.Find("d")
    If Not aCell Is Nothing Then
        MsgBox ":) Found at: " & aCell.Address
        End
    Else
        MsgBox "Not found  :("
    End If
End Sub

Sub SpecialCellsTest()
    'Dim r As Variant
    Dim r As Range
    On Error Resume Next
    Set r = Range("").SpecialCells(xlBlanks)
    MsgBox (r Is Nothing)
    MsgBox TypeName(r)
    On Error GoTo 0
End Sub



'Following functions returns ColorIndex values, it iterates amongst 11 bright colours.
'Important: Before calling this/NextColourIndex() function you must call InitBrightColourIndex() or InitDarkColourIndex() sub.
Function NextColourIndex()
    Dim cidx() As Variant
    If Len(Join(BrightColourIndexes)) > 0 Then
        cidx = BrightColourIndexes
    Else
        cidx = DarkColourIndexes
    End If
    If colourIdx = 0 Or colourIdx >= UBound(cidx) Then
        colourIdx = 0
    End If
    NextColourIndex = cidx(colourIdx)
    colourIdx = colourIdx + 1
End Function

Sub InitBrightColourIndex()
    Erase BrightColourIndexes
    Erase DarkColourIndexes
    BrightColourIndexes = Array(20, 44, 36, 38, 43, 34, 46, 3, 40, 17, 22)
    colourIdx = 0
End Sub
Sub InitDarkColourIndex()
    Erase BrightColourIndexes
    Erase DarkColourIndexes
    DarkColourIndexes = Array(9, 51, 1, 21, 30, 49, 11, 52, 53, 56)
    colourIdx = 0
    'MsgBox Len(Join(DarkColourIndexes))
End Sub



'Following code deletes specified Drawing-objects on ActiveSheet
'simply assign 'strDrawingObjName2Delete' variable the Control-name you want to delete, e.g. "Check Box", "DropDown" etc.
Sub DeleteDrawingObjects()
    On Error Resume Next
    Debug.Print TypeName(ActiveSheet.DrawingObjects(1)) & VarType(ActiveSheet.DrawingObjects(1))
    If Err.Description Like "*DrawingObjects property*" Then
        MsgBox "No drawing objects found on active-sheet (No Form Controls or ActiveX Controls)"
        Debug.Print Err.Number & " - " & Err.Description & " - " & Err.Source
        Err.Clear
        Exit Sub
    End If
    On Error GoTo 0
    Dim drobj As Object
    Dim strDrawingObjName2Delete As String: strDrawingObjName2Delete = "Check Box"
    Debug.Print "drawing Object Type-name: " & ActiveSheet.DrawingObjects(1).name
    Debug.Print "drawing Object Type-code: " & VarType(drobj)
    Debug.Print strDrawingObjName2Delete; " count - Before deleting " & ActiveSheet.DrawingObjects.count
    For Each drobj In ActiveSheet.DrawingObjects
        'If drobj.Name = "ComboBox1" Then    'if exact name is known.
        If InStr(1, drobj.name, strDrawingObjName2Delete, vbTextCompare) <> 0 Then
            drobj.Delete
        End If
    Next drobj
    Debug.Print strDrawingObjName2Delete; " count - After deleting " & ActiveSheet.DrawingObjects.count
End Sub



Sub StratifyByYears()
    Dim lastRow, lastCol, r, cidx  As Integer
    'Dim colourIdx:  colourIdx = Array(20, 44, 36, 38, 43, 34, 46, 3, 40, 17, 22)
    'Call InitDarkColourIndex  'NextColourIndex  is now active and will keey returning next DARK ColorIndex.
    Call InitBrightColourIndex  'NextColourIndex  is now active and will keey returning next BRIGHT ColorIndex.
    lastRow = Cells(Rows.count, 1).End(xlUp).Row
    lastCol = Cells(ActiveSheet.UsedRange.Row, Columns.count).End(xlToLeft).Column
    cidx = NextColourIndex
    For r = 2 To lastRow
        'Debug.Print NextColourIndex
        If Not IsEmpty(Cells(r, "C")) Then
            Range(Cells(r, 1), Cells(r, lastCol)).Interior.ColorIndex = cidx
            If Year(Cells(r, "C")) <> Year(Cells(r + 1, "C")) Then
                cidx = NextColourIndex
            End If
        End If
    Next r
End Sub


'Following VBA copies the values from a column into another whilst preserving values in 2nd/target column hence only writing to Blank cells (NOT overwrite any data in target column).
Sub CopyColumnValuesOnlyInEmptyCells()
    Application.ScreenUpdating = False
    MsgBox "You will be propmeted to enter 2 Columns next." & vbNewLine & vbNewLine & "First ""FROM"" column then ""TO"" column.", vbOKOnly & vbInformation, "Next Entries"
    Dim sourceCol As String: sourceCol = GetTargetColumn()
    Dim targetCol As String: targetCol = GetTargetColumn()
    
    Dim lastRow As Long, r As Long
    lastRow = Cells(Rows.count, sourceCol).End(xlUp).Row
    For r = 1 To lastRow
        If IsEmpty(Cells(r, targetCol)) Then
            Cells(r, targetCol) = Cells(r, sourceCol)
        End If
    Next r
    Application.ScreenUpdating = True
End Sub


'Find&Replace Keys with Values (Keys-Values-pair written in column A & B): if the Key is found in a Column-D then replace it with its (corresponding) value.
'In other words, match substrings in column-D with values in column-A, if matched, replace substring in column-D with value in Column-B (corresponding to column-A).
Sub FindAndReplaceKeysWithValues()
    Dim lastRow As Long, r As Long
    lastRow = Cells(Rows.count, "A").End(xlUp).Row
    For r = 2 To lastRow
        Range("D:D").Replace What:=Cells(r, "A"), Replacement:=Cells(r, "B"), LookAt:=xlPart, MatchCase:=False
        'Range("D:D").Replace What:=Cells(r, "A"), Replacement:=Cells(r, "B"), LookAt:=xlPart, SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:=False, ReplaceFormat:=False
    Next r
End Sub


Sub SplitJoinFunctionsCheck()
s = "9#this#is#test"
a = Split(s, "#")
MsgBox Join(a, "#")
MsgBox TypeName(a)
End Sub


'Copy the output (without Header) in a blank sheet then run this to format, then simply copy values to the Monthly Subs Report.
Sub MonthlyReportSortBySubs()
    Dim lastRow, r As Long: lastRow = ActiveSheet.UsedRange.Rows.count 'Cells(Rows.Count, 1).End(xlUp).row
    Dim s As String
    Dim arrSubs() As String
    [A1:C1].Copy Destination:=[F1]
    'arrSubs() = Split("British Journal of Family Medicine,Care Knowledge,English Teaching Professional,Fire Magazine,GM Journal,Journal of Family Health,Learning Disability Today,Mental Health Today Magazine,Modern English Teacher", ",")
    arrSubs() = Split("British Journal of Family Medicine,Care Knowledge,English Teaching Professional,Fire Magazine,GM Journal,Journal of Family Health,Modern English Teacher", ",")
    'arrSubs() = Split("British Journal of Family Medicine,English Teaching Professional,Fire Magazine,GM Journal,Journal of Family Health,Modern English Teacher", ",") 'without CK
    Cells(1, 6).Select
  'cind = 5
    For Each subName In arrSubs
        ActiveCell.Offset(1, 0).Select
        ActiveCell = subName
    'cind = cind + 1
        For r = 1 To lastRow
            'Cells(r, "A").Interior.ColorIndex = cind
            s = Cells(r, "A")
            If InStr(1, s, subName, 1) <> 0 Then
                ActiveCell.Offset(0, 1) = Cells(r, "B")
                ActiveCell.Offset(0, 2) = Cells(r, "C")
                Exit For
            Else
                ActiveCell.Offset(0, 1) = 0
                ActiveCell.Offset(0, 2) = 0
            End If
        Next r
    Next
    ActiveCell.Offset(1, 0).Select
    'ActiveCell.Interior.ColorIndex = 37
    ActiveCell = "Total (All Subs)"
    ActiveCell.Offset(0, 1).Select
    'ActiveCell.Interior.ColorIndex = 37
    'ActiveCell.Formula = "=sum(range(cells(1,activecells.column),cells(activecell.offset(-1,0),activecells.column) ))"
    'ActiveCell.Formula = "=sum(E2:E8)"
    ActiveCell.Offset(-1, 0).Select
    Set rng = Range(ActiveCell.Address & ":" & ActiveCell.End(xlUp).Offset(1, 0).Address)
    ActiveCell.Offset(1, 0) = Application.WorksheetFunction.Sum(rng)
    'ActiveCell.Formula = "=sum(E2:E8)"
    ActiveCell.Offset(0, 1).Select
    Set rng2 = Range(ActiveCell.Address & ":" & ActiveCell.End(xlUp).Offset(1, 0).Address)
    ActiveCell.Offset(1, 0) = Application.WorksheetFunction.Sum(rng2)
    rng.Select
    rng.Copy
End Sub


'Copy the output (without Header) in a blank sheet then run this to format, then simply copy values to the Monthly Subs Report.
Sub MonthlyReportSortBySubs_WithLDMH()
    Dim lastRow, r As Long: lastRow = ActiveSheet.UsedRange.Rows.count 'Cells(Rows.Count, 1).End(xlUp).row
    Dim s As String
    Dim arrSubs() As String
    arrSubs() = Split("British Journal of Family Medicine,Care Knowledge,English Teaching Professional,Fire Magazine,GM Journal,Journal of Family Health,Learning Disability Today,Mental Health Today Magazine,Modern English Teacher", ",")
    'arrSubs() = Split("British Journal of Family Medicine,Care Knowledge,English Teaching Professional,Fire Magazine,GM Journal,Journal of Family Health,Modern English Teacher", ",")
    Cells(1, 6).Select
  'cind = 5
    For Each subName In arrSubs
        ActiveCell.Offset(1, 0).Select
        ActiveCell = subName
    'cind = cind + 1
        For r = 1 To lastRow
            'Cells(r, "A").Interior.ColorIndex = cind
            s = Cells(r, "A")
            If InStr(1, s, subName, 1) <> 0 Then
                ActiveCell.Offset(0, 1) = Cells(r, "B")
                Exit For
            Else
                ActiveCell.Offset(0, 1) = 0
            End If
        Next r
    Next
    ActiveCell.Offset(1, 0).Select
    ActiveCell.Interior.ColorIndex = 37
    ActiveCell = "Total (All Subs)"
    ActiveCell.Offset(0, 1).Select
    ActiveCell.Interior.ColorIndex = 37
    'ActiveCell.Formula = "=sum(range(cells(1,activecells.column),cells(activecell.offset(-1,0),activecells.column) ))"
    ActiveCell.Formula = "=sum(G2:G10)"
    'ActiveCell.Formula = "=sum(G2:G8)"
    Range("G2:G10").Select
    Range("G2:G10").Copy
End Sub




'Following sub separates the Firstnames & LastNames
'requires: 2 emtpy columns on the right of "FullName" column.
Sub ExtractFirstName()
    Dim titles() As String
    Dim pos As Integer
    Dim firstName As String
    Dim surName As String
    Dim tmpArr() As String
    titles() = Split("Miss, Ms, Mr, Mrs, Sir, Lady, Madam, Dr, Lord, Miss., Ms., Mr., Mrs., Sir., Lady., Madam., Dr., Lord., FAO, FAO:", ", ")
    For r = 2 To 3568
        If Not IsEmpty(Cells(r, "C")) Then
            tmpArr() = Split(Cells(r, "C"), " ")
            
            'Firstname processing:
            firstName = tmpArr(0)
            If (IsInArray(firstName, titles)) Then
                firstName = Split(Cells(r, "C"), " ")(1)
            End If
            Cells(r, "D") = firstName
            
'            'Surname processing:
'            pos = InStr(Cells(r, "C"), firstName)
'            pos = pos + Len(firstName)
'            surName = Mid(Cells(r, "C"), pos)

            surName = tmpArr(UBound(tmpArr))
            Cells(r, "E") = surName
        End If
    Next r
End Sub



'Following reads strings in column-A, extracts all substrings enclosed within % and (if found) writes
'   in next column-B, if multiple instances are found splits/writes them into  B, C, D... columns.
'requires: data in column A
Sub ExtractSubstring()
    '=MID(A1,find("%"),1)
    Dim lastRow As Long: lastRow = ActiveSheet.UsedRange.Rows.count 'Cells(Rows.Count, 1).End(xlUp).row
    Dim pos, posEnd As Integer: pos = 0
    Dim count, c As Integer: count = 0
    Dim cVal, wrd As String
    For r = 1 To lastRow
        cVal = Cells(r, "A")
        If Not IsEmpty(cVal) Then
            c = 2
            Do
                count = count + 1
                pos = InStr(pos + 1, cVal, "%", 1)
                If pos = 0 Then Exit Do
                posEnd = InStr(pos + 1, cVal, "%", 1)
                wrd = Mid(cVal, pos + 1, (posEnd - (pos + 1)))
                Cells(r, c) = wrd
                c = c + 1
                pos = posEnd
            Loop While pos
        End If
    Next r
    MsgBox count
End Sub

'Following code sorts the UsedRange by font-colour.
Sub SortByColour()
    Application.FindFormat.Font.fontStyle = "Bold"
    lastRow = 9000
    Set aCell = Range("A2:A" & lastRow).Find(What:="", SearchFormat:=True)
    MsgBox aCell.Address
    If Not aCell Is Nothing Then
        aCell.Select
    End If
    ActiveSheet.Sort.SortFields.Clear
    'ActiveSheet.UsedRange.Sort Key1:=Cells(1, lastCol), Order1:=xlAscending, MatchCase:=False, Header:=xlYes
    lastCol = 8
    'ActiveSheet.UsedRange.Sort Key1:=ActiveCell.Font.ColorIndex, Order1:=xlAscending, MatchCase:=False, Header:=xlYes
    
ActiveSheet.Sort.SortFields.Add(ActiveCell, xlSortOnFontColor, xlAscending, xlSortTextAsNumbers).SortOnValue.Color = RGB(255, 0, 0)

    With ActiveSheet.Sort
        .SetRange ActiveSheet.UsedRange
        .Header = xlYes
        .MatchCase = False
        .Orientation = xlTopToBottom
        .SortMethod = xlPinYin
        .Apply
    End With

'ActiveSheet.Sort.SortFields.Add(Range("A987"), xlSortOnFontColor, xlAscending, , xlSortTextAsNumbers).SortOnValue.Color = RGB(255, 0, 0)
'    With ActiveWorkbook.Worksheets("Sheet1 (2)").Sort
'        .SetRange Range("A2:D12700")
'        .Header = xlNo
'        .MatchCase = False
'        .Orientation = xlTopToBottom
'        .SortMethod = xlPinYin
'        .Apply
'    End With
    Range("A1").Select
End Sub


'Following combines / concatenates the values in the passed range into one single String value and returns it.
'USAGE: =ColumnsToText(Range("A1:F1"))
'       =ColumnsToText(Range("A1:F1", "#"))  '2nd arg: to force "#" as delimiter.
Public Function ColumnsToText(rng As Range, Optional DelimitBy As String = " ") As String
    Dim var As Variant: var = rng
    Dim strArr() As String
    For c = 1 To UBound(var, 2) '2 = Second array dimension is columns.
        ReDim Preserve strArr(c) As String
        strArr(c) = CStr(var(1, c))
    Next c
    Debug.Print Join(strArr, DelimitBy)
    ColumnsToText = Join(strArr, DelimitBy)
End Function
'Following concatenates all left cells and inserts this concatenated string into ActiveCell:
Sub ConcatenateLeftCells()
    r = ActiveCell.Row
    c = ActiveCell.Column
    Dim rng As Range: Set rng = Range(Cells(r, 1), Cells(r, c))
    Dim delimiter As String: delimiter = ""
    ActiveCell = ColumnsToText(rng, delimiter)
End Sub

'Following concatenates all the left-cells-values into the ActiveCell by inserting Concatenate() formula, it also auto-fills the formula in all cells below the ActiveCell:
'It basically inserts the formula in the ActiveCell then does the same until the last row:
Sub ConcatenateLeftCellsTillLastRow()
    r = ActiveCell.Row
    c = ActiveCell.Column
    Dim strConcat As String: strConcat = "=Concatenate("
    For col = 1 To c - 1
        strConcat = strConcat & Chr(col + 64) & r & ","
    Next col
    strConcat = Left(strConcat, Len(strConcat) - 1) & ")"
    ActiveCell.Formula = strConcat
    Dim lastRow As Long: lastRow = ActiveSheet.UsedRange.Rows.count 'Cells(Rows.Count, 1).End(xlUp).row
    Dim fillDest As Range
    Set fillDest = Range(Cells(r, c), Cells(lastRow, c))
    ActiveCell.AutoFill Destination:=fillDest
End Sub



'Following combines a vertical column's values of CURRENT-COLUMN into one cell as comma separated values
    'which can be used in a WHERE clause of a SQL query.
Sub ColumnToCommaSeparatedValuesIntoNextColumn()
    Dim r As Integer: r = ActiveCell.Row()
    Dim c As Integer: c = ActiveCell.Column()
    'Dim colCount As Integer: colCount = ActiveSheet.Cells(r, Columns.count).End(xlToLeft).Column
    Dim rowCount As Integer: rowCount = Cells(Rows.count, 1).End(xlUp).Row
    
    If rowCount <= 1 Then
        MsgBox "No value found in that row." & vbNewLine & vbNewLine & "Ensure that cursor is placed on the same row (where columns values need to be compiled as Comma-separated-Values.", vbCritical, "Unexpected error"
        End
    End If

    Dim inQuotes As Boolean: inQuote = False
    Dim ans As String
    ans = MsgBox("Would you like values in quotes?", vbYesNoCancel, "Values in Quotes")
    If ans = vbYes Then
        inQuote = True
    ElseIf ans = vbNo Then
        inQuote = False
    Else
        End
    End If

    For i = 1 To rowCount
        cellValue = Cells(i, c).Value
        If Len(cellValue) > 0 Then
            If inQuote Then
                'strCommaList = strCommaList & ",''" & cellValue & "''"    'with ''Double-apostrophes'' (words enclosed within 'apostrophe')
                strCommaList = strCommaList & ",'" & cellValue & "'"    'with 'apostrophe' (words enclosed within 'apostrophe')
            Else
                strCommaList = strCommaList & "," & cellValue           'withOUT enclosing 'apostrophes'
            End If
            'Debug.Print strCommaList
            ''Join(arr, ",") join() doesn't work with 2d arrays.
        End If
    Next
    'strCommaList = "'" & Right(strCommaList, Len(strCommaList) - 1)
    Debug.Print strCommaList
    Cells(c, c + 2) = strCommaList
End Sub



'Following combines a vertical column's values into into one cell as comma separated values,
    'which can be used in a WHERE clause of a SQL query.
Sub ColumnToCommaSeparatedValuesIntoNextColumn__OLD()
    Dim inQuotes As Boolean: inQuote = False
    'Dim inQuotes As Boolean: inQuote = True
    Dim ans As String
    ans = MsgBox("Would you like values in quotes?", vbYesNo, "Values in Quotes")
    If ans = vbYes Then
        inQuote = True
    Else
        inQuote = False
    End If

    Dim strCol As String: strCol = GetTargetColumn()
    If strCol = "" Then
        strCol = "A"
        'End
    End If

    Dim firstRow As Double: firstRow = 1
    If IsEmpty(Cells(1, 1)) Then    'just in case if first few cells are empty.
        firstRow = Cells(1, strCol).End(xlDown).Row
    End If
    Dim lastRow As Double: lastRow = Cells(Rows.count, strCol).End(xlUp).Row
    Dim rng As Range: Set rng = Range(firstRow & ":" & lastRow)
    Dim arr As Variant: arr = rng
    Dim strCommaList As String
    'Dim intCol As Integer: intCol = Range(strCol & 1).Column
    For r = 1 To UBound(arr, 1) ' First array dimension is a row.
        If Len(arr(r, 1)) > 0 Then
            If inQuote Then
                strCommaList = strCommaList & ",'" & arr(r, 1) & "'"    'with apostrophe (words enclosed within 'apostrophe')
            Else
                strCommaList = strCommaList & "," & arr(r, 1)  'withOUT enclosing 'apostrophes'
            End If
            'Debug.Print strCommaList
            ''Join(arr, ",") join() doesn't work with 2d arrays.
        End If
    Next r
    strCommaList = "'" & Right(strCommaList, Len(strCommaList) - 1)
    Debug.Print strCommaList
    rng.Cells(1, 3) = strCommaList
    'arr = Application.Transpose(Application.Transpose(rng.Value))
    'Join(var, ",")
End Sub




Sub DeleteIfNextCellIsEmpty()
    Application.ScreenUpdating = False
    Dim r, lastRow As Long
    'lastRow = ActiveSheet.UsedRange.Rows.count 'Cells(Rows.Count, 1).End(xlUp).row
    lastRow = 4023
    Dim org, pos As String
    For r = lastRow To 2 Step -1
        'If (Cells(r, "G") = Cells(r - 1, "G") And Cells(r, "Q") = Cells(r - 1, "Q") And Cells(r, "H") <> "") Then
        If IsEmpty(Cells(r, "J")) Then
        'InStr(1, "Hello World", "Hello W") = 1
            Cells(r, "h") = ""
            'Rows(r).EntireRow.Delete
            'Rows(r).EntireRow.Hidden = True
        End If
    Next r
    Application.ScreenUpdating = True
    MsgBox "Done..."
End Sub


'Following is very useful for removing duplications from Address-Labels:
Sub DeleteIfNextCellHasValue()
    Application.ScreenUpdating = False
    Dim r, lastRow, cnt As Long
    Dim strCol As String: strCol = GetTargetColumn()
    strCol = Trim(strCol)
    If strCol = "" Then
        End
    End If
    If UCase(strCol) = "A" Then
        MsgBox "Column-A is the first column - there is no column on its left to compare it with." & vbNewLine & vbNewLine & "Enter a column-letter starting from Column-B onwards."
        End
    End If
    
    'lastRow = ActiveSheet.UsedRange.Rows.count 'Cells(Rows.Count, 1).End(xlUp).row
    lastRow = 4009
    Dim org, pos As String
    For r = lastRow To 2 Step -1
        'If (Cells(r, "G") = Cells(r - 1, "G") And Cells(r, "Q") = Cells(r - 1, "Q") And Cells(r, "H") <> "") Then
        'If IsEmpty(Cells(r, "J")) Then
        'InStr(1, "Hello World", "Hello W") = 1
        'If InStr(1, Cells(r, strCol), Cells(r, Asc(strCol) - 1)) = 1 Then
        'MsgBox ("*" & Cells(r, strCol) & "*" & Cells(r, Chr(Asc(strCol) - 1))) & " --- " & ("*" & Cells(r, strCol) & "*" Like Cells(r, Chr(Asc(strCol) - 1)))
        'MsgBox ("*" & Cells(r, strCol) & "*" & Cells(r, Chr(Asc(strCol) - 1))) & " --- " & (Cells(r, Chr(Asc(strCol) - 1)) Like "*" & Cells(r, strCol) & "*")
        'If (Cells(r, Chr(Asc(strCol) - 1)) Like "*" & Cells(r, strCol) & "*") Then ' Or (Cells(r, Chr(Asc(strCol) - 2)) Like "*" & Cells(r, strCol) & "*") Then ' Or (Cells(r, Chr(Asc(strCol) - 3)) Like "*" & Cells(r, strCol) & "*") Then ' Or (Cells(r, Chr(Asc(strCol) - 4)) Like "*" & Cells(r, strCol) & "*")) Then
        If Cells(r, strCol) <> "" And Cells(r, Chr(Asc(strCol) - 1)) = Cells(r, strCol) Then
'            MsgBox "Match found."
'            End
Debug.Print Cells(r, strCol) & " = " & Cells(r, Chr(Asc(strCol) - 1))
            Cells(r, strCol) = ""
            cnt = cnt + 1
            'Rows(r).EntireRow.Delete
            'Rows(r).EntireRow.Hidden = True
        End If
    Next r
    Application.ScreenUpdating = True
    MsgBox "Done... Total replacements: " & cnt
End Sub

Sub CopyXlsbFileIntoUsersAppDataFolder()
'usrDesktopFolder = CreateObject("WScript.Shell").specialfolders("Desktop")  'to get user's Desktop folder as String
'MsgBox usrDesktopFolder
'End
   'Creating folder (if doesn't exist):
    Dim fPath As String
    Set objWShell = CreateObject("WScript.Shell")
    appData = objWShell.expandEnvironmentStrings("%APPDATA%")
    'fPath = CurDir()   'CurDir() returns- C:\Users\eddie.shrivastava\Documents
    fPath = appData & "\Microsoft\Excel\XLSTART2"
    Dim fName As String:  fName = "XLSTART2"
    CreateFolderCheckIfNotExists (fPath)
    
   'Copying file:
    Dim fs As Object
    Dim sourcePathFile As String, targetPathFile As String
    'sourcePathFile = CurDir() & "\Doc2.docx"
    sourcePathFile = "\\slgpipav01\sgpdata\Data Reports\PavilionVBAUtilities\PavilionVBAUtilities.xlsb"
    targetPathFile = fPath & "\PavilionVBAUtilities.xlsb"
    Set fs = CreateObject("Scripting.FileSystemObject")
    fs.CopyFile sourcePathFile, targetPathFile
    Set fs = Nothing
End Sub

'following function takes full path+filename for a folder creation,
'if folder doesn't exists - it creates a new folder and returns True, otherwise returns False.
Function CreateFolderCheckIfNotExists(folderNameWithFullPath As String)
    'usrDesktopFolder = CreateObject("WScript.Shell").specialfolders("Desktop")  'to get user's Desktop folder as String
    'If Dir("C:\_Development\Deleteme", vbDirectory) = "" Then
    '    MkDir Path:="C:\_Development\Deleteme"

    If Dir(folderNameWithFullPath, vbDirectory) = "" Then
        MkDir Path:=folderNameWithFullPath
        CreateFolderCheckIfNotExists = True
    Else
        CreateFolderCheckIfNotExists = False
    End If
End Function

'Following code adds a column containing percentages of bounced emails.
'run this from within the "Weekly email summary.csv" file sent by Communigator.
'Assumptions: new column is added at column "F" assuming that preceding two columns (D & E) contain number of sent & undelivered emails.
Sub AddBouncedEmailsPercentColumn()
    If (ActiveWorkbook.name Like "Weekly email summary*" Or ActiveWorkbook.name Like "Monthly Report*") Then  ' ActiveSheet.name
        Dim lastRow As Double: lastRow = Cells(Rows.count, 1).End(xlUp).Row
        Columns("F:F").Insert Shift:=xlToRight
        Columns("F:F").NumberFormat = "0.00%"
        Range("F1") = "Undel%"
        Range("F2").FormulaR1C1 = "=RC[-1]/RC[-2]"
        Range("F2").AutoFill Destination:=Range("F2:F" & lastRow)
        ActiveSheet.UsedRange.Sort Key1:=Range("F2"), Order1:=xlAscending, MatchCase:=False, Header:=xlYes
    Else
        MsgBox "This macro must be executed from within the 'Weekly email summary.csv' file (sent by Communigator)."
    End If
    Call FormatEdTableWithHeader
    Call ApplyColourScaleOnColumn("F")
End Sub

'Following brings the value of last (staggered) column to the first column-A.
'in a non-structured data, this is very useful if you want PostCode written in right-most cells to one column.
Sub BringLastStaggeredColumnsValuesToFirstColumnA()
    Application.ScreenUpdating = False
    [A1].EntireColumn.Insert
    Dim lastRow, lastCol As Long, r As Long
    lastRow = Cells(Rows.count, 2).End(xlUp).Row
    For r = 1 To lastRow
        lastCol = Cells(r, Columns.count).End(xlToLeft).Column
        tVal = Trim(Cells(r, lastCol).Value)
        Cells(r, 1) = tVal
    Next r
    Application.ScreenUpdating = True
End Sub


'BEST WAY TO FIND LAST ROW & LAST COLUMN (recommended by MSDN):
'Usage: int LastRow = LastRow(ActiveSheet)
Function GetLastRow(Sh As Worksheet)
    On Error Resume Next
    GetLastRow = Sh.Cells.Find(What:="*", SearchDirection:=xlPrevious, SearchOrder:=xlByRows).Row
    'LastRow = sh.UsedRange.SpecialCells(xlCellTypeLastCell).row
    'LastRow = sh.Cells.Find(What:="*", _
                            After:=sh.Range("A1"), _
                            Lookat:=xlPart, _
                            LookIn:=xlFormulas, _
                            SearchOrder:=xlByRows, _
                            SearchDirection:=xlPrevious, _
                            MatchCase:=False).row
    On Error GoTo 0
End Function
'Usage: int LastCol = LastCol (<Sheet>)
Function GetLastCol(Sh As Worksheet)
    On Error Resume Next
    'LastCol = sh.UsedRange.SpecialCells(xlCellTypeLastCell).Column
    GetLastCol = Sh.Cells.Find(What:="*", SearchDirection:=xlPrevious, SearchOrder:=xlByColumns).Column
    'LastCol = sh.Cells.Find(What:="*", _
                            After:=sh.Range("A1"), _
                            Lookat:=xlPart, _
                            LookIn:=xlFormulas, _
                            SearchOrder:=xlByColumns, _
                            SearchDirection:=xlPrevious, _
                            MatchCase:=False).Column
    On Error GoTo 0
End Function

'Consolidating data from multiple sheets into one sheet:
Sub CopyRangeFromMultiWorksheets()
    Dim Sh As Worksheet
    Dim DestSh As Worksheet
    Dim Last As Long
    Dim CopyRng As Range
    With Application
        .ScreenUpdating = False
        .EnableEvents = False
    End With

    ' Delete the summary sheet if it exists.
    Application.DisplayAlerts = False
    On Error Resume Next
    ActiveWorkbook.Worksheets("MergeDataSheet").Delete
    On Error GoTo 0
    Application.DisplayAlerts = True
    ' Add a new summary worksheet.
    Set DestSh = ActiveWorkbook.Worksheets.Add
    DestSh.name = "MergedDataSheet"

    ' Loop through all worksheets and copy the data to the
    ' summary worksheet.
    For Each Sh In ActiveWorkbook.Worksheets
        If Sh.name <> DestSh.name Then
            ' Find the last row with data on the summary worksheet.
            Last = GetLastRow(DestSh)
            ' Specify the range to place the data.
            Set CopyRng = Sh.Range("A2:" & Sh.UsedRange.SpecialCells(xlCellTypeLastCell).Address)
        
            ' Test to see whether there are enough rows in the summary worksheet to copy all the data.
            If Last + CopyRng.Rows.count > DestSh.Rows.count Then
                MsgBox "There are not enough rows in the destitaion worksheet to place all the data."
                GoTo ExitTheSub
            End If

            ' This statement copy-pastes values from each worksheet:
            CopyRng.Copy Destination:=DestSh.Range("A" & (Last + 1))

            ' Optional: This statement will copy the sheet
            ' name in the H column.
            'DestSh.Cells(Last + 1, "H").Resize(CopyRng.Rows.Count).Value = sh.Name
        End If
    Next
ExitTheSub:
    Application.GoTo DestSh.Cells(1)
    ' AutoFit the column width in the summary sheet.
    DestSh.Columns.AutoFit
    With Application
        .ScreenUpdating = True
        .EnableEvents = True
    End With
End Sub


'following applies visualisation based on value-range on pased column (using Colour-Scale)
'Usage: ApplyColourScaleOnColumn ("D")
Sub ApplyColourScaleOnColumn(c As String)
    Dim rng As Range: Set rng = Columns(c & ":" & c)
    rng.FormatConditions.AddColorScale ColorScaleType:=3
    rng.FormatConditions(rng.FormatConditions.count).SetFirstPriority
    rng.FormatConditions(1).ColorScaleCriteria(1).Type = xlConditionValueLowestValue
    With rng.FormatConditions(1).ColorScaleCriteria(1).FormatColor
        .Color = 8109667
        .TintAndShade = 0
    End With
    rng.FormatConditions(1).ColorScaleCriteria(2).Type = xlConditionValuePercentile
    rng.FormatConditions(1).ColorScaleCriteria(2).Value = 50
    With rng.FormatConditions(1).ColorScaleCriteria(2).FormatColor
        .Color = 8711167
        .TintAndShade = 0
    End With
    rng.FormatConditions(1).ColorScaleCriteria(3).Type = xlConditionValueHighestValue
    With rng.FormatConditions(1).ColorScaleCriteria(3).FormatColor
        .Color = 7039480
        .TintAndShade = 0
    End With
    Range(c & "1").Select
End Sub



'Following splits the rows in group of 1000 rows and exports them in individual CSV files:
Sub SplitRowsIn1000sAndExportAsCSV()
  'CopyHeaderRowToNewSheet:
    Dim RowsRng As String
    Dim SourceSh, DestSh As Worksheet: Set SourceSh = ActiveSheet
    'Dim lastRow, r As Long: lastRow = SourceSh.UsedRange.Rows.count 'Cells(Rows.Count, 1).End(xlUp).row
    'ActiveSheet.UsedRange 'works if executed in the Immediate window to get rid of empty-range but not in VBA Sub.
    Dim lastRow, r As Long: lastRow = SourceSh.UsedRange.SpecialCells(11).Row
    Dim lastCol, c As Integer: lastCol = SourceSh.UsedRange.SpecialCells(11).Column
    c = SourceSh.Cells(1, SourceSh.Columns.count).End(xlToLeft).Column
    SourceSh.Columns(ColumnNumberToLetter(c + 1) & ":" & ColumnNumberToLetter(lastCol + 1)).Delete
'    MsgBox SourceSh.Rows(lastRow + 1 & ":" & lastRow + 1000).Address
'    SourceSh.Rows(lastRow + 1 & ":" & lastRow + 1000).Delete 'instead-accurately formed the RowsRng below using: ElseIf (r + 999) >= lastRow

    For r = 1 To (lastRow) Step 1000
        Set DestSh = ActiveWorkbook.Worksheets.Add
        SourceSh.Rows(1).Copy Destination:=DestSh.Rows(1)
        If r = 1 Then
            RowsRng = "2:1000"
            'Set RowsRng = Range(Rows(2), Rows(1000))
        ElseIf (r + 999) >= lastRow Then
            RowsRng = r & ":" & lastRow
        Else
            RowsRng = r & ":" & (r + 999)
            'Set RowsRng = Range(Rows(r), Rows(r + 999))
        End If
        Debug.Print DestSh.name & "   -   " & r & "   -   " & RowsRng
        SourceSh.Range(RowsRng).Copy Destination:=DestSh.Range("A2")
        DestSh.Select
        Call ExportCurrSheetAsCSV_WithoutPrompt(ActiveWorkbook.Path)
    Next r
    SourceSh.Select
    Call DeleteAllButActiveSheet
End Sub

'Following code saves the current worksheet as CSV file with same name as current sheet-name in the current folder.
Sub ExportCurrSheetAsCSV_WithoutPrompt(wBookPath As String)
     wBookName = ActiveWorkbook.name & " - " & Replace(ActiveSheet.name, "Sheet", "Batch") & ".csv"
     ActiveSheet.Copy
     ActiveWorkbook.SaveAs fileName:=wBookPath & "\" & wBookName, FileFormat:=xlCSV, CreateBackup:=False
     ActiveWorkbook.Close False
     'ThisWorkbook.Activate
     'MsgBox "Sheet exported as: """ & wBookName & """" & vbNewLine & vbNewLine & "Saved under: " & wBookPath
End Sub


Sub SimpleRangeCheck()
    For r = 1 To 4005 Step 1000
        If r > 2000 Then End
        Debug.Print r & "   -   " & r + 999
    Next r
End Sub

'Following simply selects a range of rows:
Sub Select1000Rows()
    'Range(Rows(2), Rows(1000)).Select
    'Range(Rows(1001), Rows(2000)).Select
    'Range(Rows(2001), Rows(3000)).Select
    'Range(Rows(3001), Rows(4000)).Select
    'Range(Rows(4001), Rows(5000)).Select
    Range(Rows(5001), Rows(6000)).Select
    'Range(Rows(6001), Rows(7000)).Select
    'Range(Rows(7001), Rows(8000)).Select
    'Range(Rows(8001), Rows(9200)).Select
    'Range(Rows(9201), Rows(10495)).Select
    'Call Split1000RecordsExportAsCSV
End Sub

'Formats the Spec Spreadsheet to make it more readable - for temporary use (don't save it)
Sub SpecFormatTmp()
    'Application.DisplayAlerts = False  'resets & sets the Text-Wrap to True - spoiling manual row-height.
    Dim found As Boolean: found = False
    Dim filterRow As Long
    For Each ws In Worksheets
      If InStr(1, ws.name, "Data Mapping", vbTextCompare) Then
        found = True
      End If
    Next ws
    'If (ActiveWorkbook.name Like "Weekly email summary*" Or ActiveWorkbook.name Like "Monthly Report*") Then  ' ActiveSheet.name
    If Not found Then
        MsgBox "Wrong workbook.  This macro must be executed from within a Spec - containing a sheet called ""Data Mapping""."
        End
    End If

    Sheets("Data Mapping").Select
    Call CreateCopyOfCurrentSheet
    'ActiveWindow.SmallScroll Down:=-30
    'Range("DataMapping[[#Headers],[Target Table]]").Select
    
    'Removing AutoFilter Dropdowns:
    Cells(Rows.count, 1).End(xlUp).Select   'need to be within the filtered-range, otherwise ListObjects(1) will be null.
    If (ActiveSheet.ListObjects(1).ShowAutoFilter = True) Then
       'ActiveSheet.ListObjects(1).ShowAutoFilter = False
       'ActiveSheet.AutoFilter.Range.SpecialCells(xlCellTypeLastCell).AutoFilter    'Same as above removes AutoFilter.
       'iif(ActiveSheet.AutoFilter.Range.Rows(1).row<=0, filterRow=1, (filterRow = ActiveSheet.AutoFilter.Range.Rows(1).row))
        If (ActiveSheet.AutoFilter.Range.Rows(1).Row <= 0) Then
            filterRow = 1
        Else
            filterRow = ActiveSheet.AutoFilter.Range.Rows(1).Row
        End If
        'filterRow = ActiveSheet.UsedRange.Rows(1).row
        Cells(filterRow, 1).AutoFilter
        If filterRow > 1 Then
           Rows(1 & ":" & (filterRow - 1)).Hidden = True
        End If
    End If
    
    'Cells.WrapText = False
    Columns("B:B").ColumnWidth = 14
    Columns("C:C").ColumnWidth = 2
    Columns("G:G").ColumnWidth = 2
    With ActiveWindow
        .FreezePanes = False
        .SplitColumn = 0
        .SplitRow = 1
        '.SplitRow = filterRow
        .FreezePanes = True
    End With
    Cells(Rows.count, ActiveSheet.UsedRange.SpecialCells(11).Column).End(xlUp).AutoFilter  'turning on AutoFilter on visible cells.
    Cells(2, ActiveSheet.UsedRange.SpecialCells(11).Column).End(xlDown).Offset(0, -2).Select
    'Cells(filterRow, Columns.count).End(xlToLeft).Offset(0, -2).Select
    'turning on the AutoFilter:
    'ActiveSheet.UsedRange.SpecialCells(xlCellTypeVisible).AutoFilter  'turning on AutoFilter on visible cells.
    'CommandBars.ExecuteMso "HideRibbon"    'use DisplayFullScreen instead.
    'Application.DisplayAlerts = True  'resets & sets the Text-Wrap to True - spoiling manual row-height.
    SetFullScreen (True)
End Sub


'Looks up and populates (if found) the Use-Case numbers into a newly inserted column 'B' within 'DDL Script' sheet:
Sub GetSpecId()
    'Application.DisplayAlerts = False  'resets & sets the Text-Wrap to True - spoiling manual row-height.
    Dim found1 As Boolean: found1 = False
    Dim found2 As Boolean: found2 = False
    For Each ws In Worksheets
      If InStr(1, ws.name, "DDL Scripts", vbTextCompare) Then
        found1 = True
      End If
      If InStr(1, ws.name, "Detailed Use Cases", vbTextCompare) Then
        found2 = True
      End If
    Next ws
    If Not (found1 And found2) Then
        MsgBox "Wrong workbook.  This macro must be executed from within a Spec - containing a 2 sheets called ""DDL Scripts"" & ""Detailed Use Cases""."
        End
    End If

    Sheets("DDL Scripts").Select
    Call CreateCopyOfCurrentSheet
    
    Dim lastRow As Long: lastRow = Cells(Rows.count, 1).End(xlUp).Row
    [B1].EntireColumn.Insert
    [B1].Value = "Spec-ID"
    With Sheets("Detailed Use Cases")
        Set rng = .Range("B1:B" & .Cells(Rows.count, 2).End(xlUp).Row)
    End With
    For r = 2 To lastRow
        If Not IsError(Application.Match(Cells(r, "A"), rng, 0)) Then
            TargetRow = Application.Match(Cells(r, "A"), rng, 0)
            Cells(r, "B") = Sheets("Detailed Use Cases").Cells(TargetRow, "C").Value
        End If
    Next r
    '[A1].Select
    'Application.DisplayAlerts = True  'resets & sets the Text-Wrap to True - spoiling manual row-height.
    SetFullScreen (True)
End Sub




'******************************************************************************************
'Written by:    Eddie Kumar Shrivastava
'Main Method(s):Sub NonStandardCharactersCheckInSheet(), NonStandardCharactersCheckInSpecifiedColumn()
'Description:   Utility to check (highlight & list) the cells containing any non-standard characters in sheet.
'Date Created:  20-10-2017
'Date Modified: 23-10-2017
'Version:   1.1
'Version info/Change Log:
'   - Added feature to highlight individual character (alogn with the entire cell-backgroud)
'******************************************************************************************

'Following sub finds & lists the cells containing any non-standard (often problematic) characters such as "TeX's Roman Fonts"
    'in the current worksheet, the code can also highlight and bring these cells on top (of sheet) if required.
Sub NonStandardCharactersCheckInSheet()  'Sub SpotDodgyCharsInSheet()
    answerHighlight = MsgBox("Would you like to highlight cells containing non-standard characters?", vbYesNoCancel + vbQuestion, "Create Copy of Sheet & Highlight Content?")
    If answerHighlight = vbCancel Then
        End
    ElseIf answerHighlight = vbYes Then
        Call CreateCopyOfCurrentSheet
    End If
    TurnOffOtherExcelActivities (True)
    Application.DisplayStatusBar = True
    Application.ScreenUpdating = True
    GetExecutionDuration_TurnOnTimer (True)
    On Error GoTo 0
    DoEvents
    Dim re As Object: Set re = CreateObject("VBScript.RegExp")
    re.Pattern = "[^\w\s\.\,\*\+\-\?\\\|\(\)\[\]\{\}\^\$""#~@;:!`¬<>-€“”‘'’†‡•¦&£%/]+"
    Dim lineNums As String: lineNums = ""
    Dim found As Boolean: found = False
    endCol = ActiveSheet.UsedRange.SpecialCells(xlCellTypeLastCell).Column
    For c = 1 To endCol
        Application.StatusBar = "Checking " & c & " of " & endCol & " | " & "PLEASE WAIT!!!    " & Int(c * 100 / endCol) & "% Completed"
        strCol = ColumnNumberToLetter(c)
        lastRow = ActiveSheet.UsedRange.SpecialCells(11).Row
        For r = 1 To lastRow
            If re.test(Cells(r, strCol)) Then
                found = True
                lineNums = lineNums & strCol & r & ", "
                If answerHighlight = vbYes Then
                    Cells(r, strCol).Interior.Color = RGB(240, 230, 255)    'BG highlight
                    'Cells(r, strCol).Font.ColorIndex = 3                   'FG highlight
                    For i = 1 To Len(Cells(r, strCol))                      'Pin-point char(s)
                        With Cells(r, strCol).Characters(i, 1)
                          If re.test(.text) Then
                              .Font.ColorIndex = 3     'redCIndex
                              .Font.fontStyle = "Bold"
                          End If
                        End With
                    Next i
                End If
            End If
        Next r
    Next c
    Application.StatusBar = False          'Handing over statusbar's display-control back to Excel.
    TurnOffOtherExcelActivities (False)
    If found Then
        Debug.Print lineNums
        MsgBox "Following cells contain one or more non-standard characters:" _
            & vbNewLine & vbNewLine & Left(lineNums, Len(lineNums) - 2) & "." _
            & vbNewLine & vbNewLine & "The program execution took " _
            & GetExecutionDuration_TurnOnTimer(False) & " seconds.", vbOK + vbInformation, "Cell-Addresses For Content Review"
    Else
        MsgBox ("No non-standard characters found in this worksheet." _
            & vbNewLine & vbNewLine & "The program execution took " _
            & GetExecutionDuration_TurnOnTimer(False) & " seconds.")
    End If
End Sub

'Following sub finds (in specified column) and highlights (+brings on top of sheet) all possible non-standard characters such as "TeX's Roman Fonts"
Sub NonStandardCharactersCheckInSpecifiedColumn()     'SpotDodgyCharsInSpecCol()
    Dim strCol As String: strCol = GetTargetColumn()
    If strCol = "" Then
        End
    End If

    answerHighlight = MsgBox("Would you like to highlight cells containing non-standard characters & bring them on top (of sheet)?", vbYesNoCancel + vbQuestion, "Create Copy of Sheet & Highlight Content?")
    If answerHighlight = vbCancel Then
        End
    ElseIf answerHighlight = vbYes Then
        Call CreateCopyOfCurrentSheet
    End If

    TurnOffOtherExcelActivities (True)
    GetExecutionDuration_TurnOnTimer (True)
    On Error GoTo 0
    Dim re As Object: Set re = CreateObject("VBScript.RegExp")
    re.Pattern = "[^\w\s\.\,\*\+\-\?\\\|\(\)\[\]\{\}\^\$""#~@;:!`¬<>-€“”‘'’†‡•¦&£%/]+"
    Dim lineNums As String: lineNums = ""
    lastRow = ActiveSheet.UsedRange.SpecialCells(11).Row
'    Columns(strCol).Interior.ColorIndex = 2
'    Columns(strCol).Font.ColorIndex = 1
    Dim found As Boolean: found = False
    For r = 1 To lastRow
        If re.test(Cells(r, strCol)) Then
            found = True
            lineNums = lineNums & (r) & ", "
            If answerHighlight = vbYes Then
                Cells(r, strCol).Interior.Color = RGB(240, 230, 255)    'BG highlight
                'Cells(r, strCol).Font.ColorIndex = 3                   'FG highlight
                For i = 1 To Len(Cells(r, strCol))                      'Pin-point char(s)
                    With Cells(r, strCol).Characters(i, 1)
                      If re.test(.text) Then
                          .Font.ColorIndex = 3     'redCIndex
                          .Font.fontStyle = "Bold"
                      End If
                    End With
                Next i
            End If
        End If
    Next r
    TurnOffOtherExcelActivities (False)
    
    If found Then
        Debug.Print lineNums
        If answerHighlight = vbNo Then
            MsgBox ("Following row-numbers contain one or more non-standard characters:" _
                & vbNewLine & vbNewLine & Left(lineNums, Len(lineNums) - 2) & "." _
                & vbNewLine & vbNewLine & "The program execution took " _
                & GetExecutionDuration_TurnOnTimer(False) & " seconds.")
        Else
            ''Following section brings the highlighted cells to top of sheet:
            ActiveSheet.Sort.SortFields.Clear
            Range(strCol & "1").Select
            ActiveSheet.Sort.SortFields.Add(ActiveCell, xlSortOnFontColor, xlAscending, xlSortTextAsNumbers).SortOnValue.ColorIndex = 3
            With ActiveSheet.Sort
                .SetRange ActiveSheet.UsedRange
                .Header = xlNo 'xlYes
                .MatchCase = False
                .Orientation = xlTopToBottom
                .SortMethod = xlPinYin
                .Apply
            End With
            Set re = Nothing
            MsgBox ("Cells containing any non-standard characterset highlighted (in the selected column) & brought to top of sheet for review." & vbNewLine & vbNewLine & "The program execution took " & GetExecutionDuration_TurnOnTimer(False) & " seconds.")
        End If
    Else
        MsgBox ("No non-standard characters found in this worksheet." _
            & vbNewLine & vbNewLine & "The program execution took " _
            & GetExecutionDuration_TurnOnTimer(False) & " seconds.")
    End If
End Sub

Sub SpotDodgyCharPositionInTargetCell()
    Dim TargetCell As Range: Set TargetCell = [C16]
    Dim re As Object: Set re = CreateObject("VBScript.RegExp")
    re.Pattern = "[^\w\s\.\,\*\+\-\?\\\|\(\)\[\]\{\}\^\$""#~@;:!`¬<>-€“”‘'’†‡•¦&£%/]+"
    Dim charFound As Boolean: charFound = False
    Dim charPositions As String: charPositions = ""

  For i = 1 To Len(TargetCell)
      With TargetCell.Characters(i, 1)
        If re.test(.text) Then
            charFound = True
            charPositions = charPositions & i & ","
            .Font.ColorIndex = 3     'redCIndex
            .Font.fontStyle = "Bold"
        End If
      End With
  Next i
      If charFound Then
        Debug.Print "Dodgy characters fount at the following position(s): " & vbCrLf & Left(charPositions, Len(charPositions) - 1)
    End If
End Sub

Sub SpotDodgyCharPositionInTargetCell_CheckOnly()
    Dim TargetCell As Range: Set TargetCell = [C16]
    Dim re As Object: Set re = CreateObject("VBScript.RegExp")
    re.Pattern = "[^\w\s\.\,\*\+\-\?\\\|\(\)\[\]\{\}\^\$""#~@;:!`¬<>-€“”‘'’†‡•¦&£%/]+"
    Dim found As Boolean: found = False
    If re.test(TargetCell) Then
        found = True
    End If
    If found Then
       MsgBox "FOUND"
    Else
       MsgBox "NOT Found"
    End If
End Sub


'using Application.StatusBar to show progress of execution (instead of progress-bar)
Sub StatusBarExample()
    DoEvents 'allowing other processes (no deallock on Excel)
    'Disable Screen Updating and Events
    Application.EnableEvents = False
    Application.ScreenUpdating = False
    MsgBox ("Ready: Look at Excel's Statusbar")
    
    Dim maxLoops As Long: maxLoops = 10
    For i = 1 To maxLoops
        'Altering the Statusbar Property
        Application.StatusBar = "Executing " & i & " of " & maxLoops & " | " & "BE PATIENT!!! " & i
            'Application.Wait (Now + TimeValue("00:00:00"))  'minimum is 1 second wait - use sleep() for < 1 sec wait (e.g. for half second: Sleep(500)).
            'Application.Wait Now + 1 / (24 * 60 * 60) 'doesn't work - Overflow error.
            Sleep (200) 'sleeping for 0.2 seconds
            'Sleep() requires: Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal milliseconds As LongPtr) 'for Office 64 Bit
    Next i
    
    Application.StatusBar = False          'Handing over statusbar's display-control back to Excel.
    'Enable ScreenUpdating and Events
    Application.ScreenUpdating = True
    Application.EnableEvents = True
End Sub

'Following sub uses the "EdProgressBar VB-Form" and requires the next Sub ProgressBarUsingForm():
Sub ShowProgressBar()
    EdProgressBar.Show
End Sub
'to run the following sub (ProgressBarUsingForm) run the Sub ShowProgressBar() above.
Sub ProgressBarUsingForm()
    Dim maxLoops As Long: maxLoops = 10
    For i = 1 To maxLoops
        EdProgressBar.Label1.Caption = (i * 10) & "% Completed"
        EdProgressBar.bar.Width = i * 20
        Sleep (200) 'sleeping for 0.2 seconds
        'Sleep() requires: Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal milliseconds As LongPtr) 'for Office 64 Bit
        DoEvents
    Next i
    MsgBox ("All done...")
    EdProgressBar.Hide
End Sub


'Following 3xSubs deal with printing array-elements into Sheet:
'To print all elements in **one cell** as a single-string, convert your array into  a string (using the Join() function), then print it in a cell:
Sub PrintArrayIn1Cell(myArr As Variant, cell As Range)
    cell = Join(myArr, ",")
End Sub
Sub PrintArrayInRange(myArr As Variant, cell As Range)
    cell.Resize(UBound(myArr, 1), UBound(myArr, 2)) = myArr
End Sub
Sub TestPrintArrayIntoSheet()  '2dArrayToSheet
    Dim arr As Variant
    arr = Split("a  b  c", "  ")

    'Printing in ONE-CELL: To print all array-elements as a single string separated by comma (a,b,c):
    PrintArrayIn1Cell arr, [A1]
    
    'Printing in SEPARATE-CELLS: To print array-elements in separate cells:
    Dim arr2D As Variant
    arr2D = Application.WorksheetFunction.Transpose(arr) 'convert a 1D array into 2D array
    PrintArrayInRange arr2D, Range("B1:B3")
End Sub


Sub UnassignedVsEmptyVsNull()
    Dim MyVar                     'defaults to a Variant type
    Debug.Print IsEmpty(MyVar)    ' => True, Variant never gets auto-initialised / no default init value for Variant.
    Debug.Print IsEmpty(undeclared)    ' => True, treatead as Variant.
    
    MyVar = Null    ' Assign Null.
    Debug.Print IsEmpty(MyVar)    ' => False.
    Debug.Print IsNull(MyVar)     ' => True.
    
    MyVar = Empty                 ' explicitly assigned EMPTY.
    Debug.Print IsEmpty(MyVar)    ' => True.

    Dim i As Integer
    Debug.Print IsEmpty(i) & ", default init: " & i           ' => False, default init: 0.

    Dim str As String
    Debug.Print IsEmpty(str) & ", default init: " & str       ' => False, default init: "".

    Dim boo As Boolean
    Debug.Print IsEmpty(boo) & ", default init: " & boo       ' => False, default init: False.

End Sub

'Following (if occurred) catches specific error and gracefully handles it by printing appropriate message:
Sub ErrHandlerSimple()
    On Error GoTo ErrHandlerSimple:
    n = 1 / 0    ' cause an error
    Debug.Print "This line will never reach / NOT print as 'using On Error GoTo 0'  (instead of 'On Error GoTo Next' or 'Resume Next')"
ErrHandlerSimple:
    If Err.Number = 11 Then  'error# 11 = divide by zero
        Debug.Print "Err#11: Division by zero - error occurred."
        MsgBox "Err#11: 'Division by zero' error occurred. Application will now terminate.", vbCritical, "Unexpected error"
    Else
        MsgBox "Unexpected error occurred, see error-details in Immediate windowpane. Please contact Eddie Shrivastava to resolve.", vbCritical, "Unexpected error"
        Debug.Print Err.Number
        Debug.Print Err.Description
    End If
    On Error GoTo 0
End Sub



'Check error in Loop / Skip iteration if error occurs:
'Following handles the error in loop, if error occurs prints its details then skips the iteration.
Sub ErrHandlerInLoop()
    On Error Resume Next
    For i = 1 To 3
      Err.Clear
      n = 1 / 0    ' cause an error
      Debug.Print "all hunky dory..."
      If Err.Number = 0 Then
         'do work on success
         Debug.Print "no error"
      Else
         Debug.Print Err.Number
         Debug.Print Err.Description
         Exit For
      End If
    Next
End Sub
'Remember: you canNOT use On Error GoTo <Lable> to skip the iteration in a loop, as when you Resume, execution goes back to the line where error had occurred.
'Following depicts multiple error-occurrances and exiting error handler using Resume or Resume Next:
Sub ErrHandler()
    On Error GoTo ErrHandler1:
    n = 1 / 0    ' cause an error
    Debug.Print "all hunky dory..."
ErrHandler1:
    ' error handling code
    Debug.Print "1.error occurred"
    Debug.Print Err.Number
    Debug.Print Err.Description
    Resume Next
    
    On Error GoTo ErrHandler2:   'this will not work without resuming normal execution using "Resume Next"
    n = 1 / 0    ' cause an error AGAIN
    Debug.Print "all hunky dory..."
ErrHandler2:
    ' error handling code
    Debug.Print "2.error occurred"
    If Err.Number = 11 Then
        Debug.Print "Err#11: Division by zero - error occurred."
    Else
        MsgBox "Unexpected error occurred see error-details in Immediate windowpane.", vbCritical, "Unexpected error"
        Debug.Print Err.Number
        Debug.Print Err.Description
    End If
    On Error GoTo 0
End Sub


''Pausing Code Execution using: VBA's Wait() OR Windows' Sleep():
'Syntaxes:
'    Application.Wait (<Specific_Clock_Time>)   'available natively - no declaration required.
'    Sleep (<milliseconds>)     '   requires global declaration of Windows "kernel32" library.

'Wait(): pauses code execution until a certain time is reached. It's available natively with 1 second as minimum interval.

'Sleep(): Windows  "kernel32" has Sleep() function that pauses the entire process for a certain delay specified in milliseconds. NOTE: Freezes the entire process i.e. Excel does not respond - There is no way to stop the Sleep function.
'Unlike Application.Wait(), the Sleep() function is not available by default in VBA, and has to be imported from the kernel32 library. 'For 64 Bit procedures make sure to append the 'PtrSafe' statement.
'    #If VBA7 Then
'        Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal milliseconds As LongPtr) 'MS Office 64 Bit
'    #Else
'        Public Declare Sub Sleep Lib "kernel32" (ByVal milliseconds As Long) 'MS Office 32 Bit
'    #End If
    Sub SleepTest()
        GetExecutionDuration_TurnOnTimer (True) 'turn on stop-watch.
        Sleep 500                         'pause for HALF second
        MsgBox ("SleepVsWaitTest" & vbNewLine & vbNewLine & "The code took " & GetExecutionDuration_TurnOnTimer(False) & " seconds to run.") 'turn off stop-watch + return seconds.
    End Sub
    Sub WaitTest()
        GetExecutionDuration_TurnOnTimer (True) 'turn on stop-watch.
        'Application.Wait "16:30:00"                    'pause unti 4:30 pm
        Application.Wait (Now + TimeValue("0:00:01"))  'for 1 second from now
        'Application.Wait (DateAdd("s", 3, Now))        'for 3 seconds from now
        MsgBox ("SleepVsWaitTest" & vbNewLine & vbNewLine & "The code took " & GetExecutionDuration_TurnOnTimer(False) & " seconds to run.")
    End Sub
    Sub WaitTest2()
        Application.Wait (Now + #12:00:02 AM#)
        MsgBox "Waited 2 seconds"
        ''OR
        Application.Wait (Now + TimeValue("00:00:02"))
        MsgBox "Waited another 2 seconds"
    End Sub


'Displays the path of current workbook file and copies it to the clipboard:
Sub getPathOfCurrentFile()
    If Len(ActiveWorkbook.Path) <= 0 Then
        MsgBox "Unsaved Document!" & vbNewLine & vbNewLine & "File path/name doesn't exist as not saved yet."
    Else
        filePath = ActiveWorkbook.Path
        fileFullName = filePath & "\" & ActiveWorkbook.name
        WriteToClipboard fileFullName
        MsgBox fileFullName & vbNewLine & vbNewLine & vbNewLine & "Also copied to the Clipboard."
    End If
    'MsgBox "_" & Format(Now, "dd-MM-yyyy_hhnnss")
End Sub



'------------ x -------------



''''''''''''''''''''''''''''''
'TextDiff - Excel VBA Utility for character-by-character differences between two paragraphs/code snippets.
''''''''''''''''''''''''''''''

'******************************************************************************************
'Written by:    Eddie Kumar Shrivastava
'Main method:   Sub CompareAndHighlightDiffs() - this is the main/parent function that calls relevant dependencies/sub-routines.
'Description:   Utility to quickly find character-by-character differences between two paragraphs/code snippets in Cells [A1] & [B1].
'Usage:         In Excel spreadsheet copy-paste the two paragraphs/code snippets (that need to be compared) in Cells [A1] & [B1], then run the CompareAndHighlightDiffs() function.
'Date Created:  29-09-2016
'Date Modified: 23-10-2017
'Version:   1.2
'Version info/Change Log:
'   - Added feature to span/merge multiple cells, when text overflows out of first cell.
'   - Fixed issue with recognition of the repeating words.
'******************************************************************************************

'Following (CompareAndHighlightDiffs) is the main/parent function that calls relevant dependencies/sub-routines.
'TEXT COMPARISON: Following sub compares text and highlights the differences between cells A1 against B1:
Sub CompareAndHighlightDiffs()
    Call BasicEqualityCheck([A1], [B1], False)
'TRIMMING WHITE-SPACE:
  'Trimming Left & Right BLANKS:
    [A1].Value = Trim([A1].Value)
    [B1].Value = Trim([B1].Value)
  'Trimming Left & Right NEWLINE-CHARS:
    [A1].Value = TrimNewLineCharsFromEdges([A1].Value)
    [B1].Value = TrimNewLineCharsFromEdges([B1].Value)
    'Call SortThenCopyA1B1ValuesToA3B3
    Call CompareAndHighlightPassedValues([A1], [B1])
    'Call CompareAndHighlightPassedValues([A3], [B3])
    
    Call FixRowHeightLimit
End Sub
Sub CompareAndHighlightPassedValues(CellA As Range, CellB As Range)
'------------------------
'CHAR-BY-CHAR Comparison:
'------------------------
    Dim posA, posRevA As Integer
    Dim posB, posRevB As Integer
    'Dim s1, s2 As String
    's1 = CellA
    's2 = CellB
    
    s1 = CellA.Value
    s2 = CellB.Value
    s1 = ReplaceWhiteSpaces(s1)
    s2 = ReplaceWhiteSpaces(s2)
    trimmedTxt1 = Replace(s1, "¬", "")
    trimmedTxt2 = Replace(s2, "¬", "")
    Call BasicEqualityCheck(trimmedTxt1, trimmedTxt2, True)
    
    'Hightlighting entire text of both cells:
    Call HighlightCellText(CellA)
    Call HighlightCellText(CellB)
    
    posA = GetFirstDiffNonWhitespaceCharPosition(s1, s2)
    posB = GetFirstDiffNonWhitespaceCharPosition(s2, s1)
    s1 = StrReverse(s1)
    s2 = StrReverse(s2)
'    Debug.Print s1
    posRevA = GetFirstDiffNonWhitespaceCharPosition(s1, s2)
    posRevB = GetFirstDiffNonWhitespaceCharPosition(s2, s1)
'CellA Formatting:
    'With CellA.Characters(posA, Len(s1) - ((posRevA + posA) - 2)).Font 'character in the middle.
    With CellA.Characters(1, posA - 1).Font
        .ColorIndex = 0  'blackCIndex
        .fontStyle = "Regular"
    End With
'    If Len(s1) > (posA + posRevA) - 2 Then
        With CellA.Characters(Len(s1) - (posRevA - 2), Len(s1)).Font
            .ColorIndex = 0  'blackCIndex
            .fontStyle = "Regular"
        End With
'    End If
    
'CellB Formatting:
    'With CellB.Characters(posB, Len(s1) - ((posRevB + pos) - 2)).Font 'characters in the middle.
    With CellB.Characters(1, posB - 1).Font
        .ColorIndex = 0  'black Index
        .fontStyle = "Regular"
    End With
'    If Len(s2) > (posB + posRevB) - 2 Then
        With CellB.Characters(Len(s2) - (posRevB - 2), Len(s2)).Font
            .ColorIndex = 0  'blackCIndex
            .fontStyle = "Regular"
        End With
'    End If
'
'------------------------
'WORD-BY-WORD Comparison (for unmatched words):
'------------------------
'    MsgBox Mid(CellA, pos, Len(s1) - ((posRevA + pos) - 2)) 'characters in the middle.
'    MsgBox Mid(CellB, pos, Len(s2) - ((posRevA + pos) - 2)) 'characters in the middle.
    'Starting positions of Unmatch-text:
    Dim unmatPosA, unmatPosB As Integer
    unmatPosA = posA    'Start comparing from posA - where first diff-char was found.
    unmatPosB = posB
    
    'Initialisation of the variable holding Unmatched-text:
    Dim unmatchedA, unmatchedB As String  'unmatched characters in the middle.
    If Len(s1) > ((posRevA + posA) - 2) Then
        unmatchedA = WorksheetFunction.Trim(Mid(CellA, posA, Len(s1) - ((posRevA + posA) - 2)))
    Else
        unmatchedA = ""
    End If
    If Len(s2) > ((posRevB + posB) - 2) Then
        unmatchedB = WorksheetFunction.Trim(Mid(CellB, posB, Len(s2) - ((posRevB + posB) - 2)))
    Else
        unmatchedB = ""
    End If
    Dim cIndex As Integer: cIndex = 0
    Dim fontStyle As String: fontStyle = "Regular"
    'Comparing word-by-word using Arrays:
WordByWord:
    Dim arr1() As String
    Dim arr2() As String
    unmatchedA = Replace(unmatchedA, Chr(10), " ")  'Soft enter
    'unmatchedA = Replace(unmatchedA, Chr(13), " ") 'Hard enter - doesn't exist within Cell.
    arr1() = Split(unmatchedA, Chr(32)) 'split by blank-spaces
    'MsgBox Join(arr1, ", ")
    unmatchedB = Replace(unmatchedB, Chr(10), " ")
    'unmatchedB = Replace(unmatchedB, Chr(13), " ")
    arr2() = Split(unmatchedB, Chr(32))
    For Each elem In arr2
        If elem <> "" Then
            If IsInArrayInt(elem, arr1) <> -1 Then
                unmatPosA = InStr(unmatPosA, CellA, elem, vbTextCompare)
                With CellA.Characters(unmatPosA, Len(elem)).Font
                    .ColorIndex = 0  'blackCIndex
                    .fontStyle = "Regular"
                End With
                unmatPosA = unmatPosA + Len(elem)
            End If
        Else
            If (InStr(unmatPosA, Mid(CellA, unmatPosA, 1), " ", vbTextCompare) Or InStr(unmatPosA, Mid(CellA, unmatPosA, 1), Chr(10), vbTextCompare)) Then
                unmatPosA = unmatPosA + 1
            End If
        End If
    Next
    For Each elem In arr1
        If elem <> "" Then
            If IsInArrayInt(elem, arr2) <> -1 Then
                unmatPosB = InStr(unmatPosB, CellB, elem, vbTextCompare)
                With CellB.Characters(unmatPosB, Len(elem)).Font
                    .ColorIndex = 0  'blackCIndex
                    .fontStyle = "Regular"
                End With
                unmatPosB = unmatPosB + Len(elem)
            End If
        Else
            If (InStr(unmatPosB, Mid(CellB, unmatPosB, 1), " ", vbTextCompare) Or InStr(unmatPosB, Mid(CellB, unmatPosB, 1), Chr(10), vbTextCompare)) Then
                unmatPosB = unmatPosB + 1
            End If
        End If
    Next
    On Error GoTo HighlightSuccessful
    If CellA.Font.ColorIndex < 1 And CellB.Font.ColorIndex < 1 Then
'        unmatchedA = Application.WorksheetFunction.Trim(CellA)
'        unmatchedB = Application.WorksheetFunction.Trim(CellB)
        unmatchedA = CellA
        unmatchedB = CellB
        unmatPosA = 1   'Start comparing from begining i.e. entire text.
        unmatPosB = 1
        Call HighlightCellText(CellA)
        Call HighlightCellText(CellB)
        GoTo WordByWord
    End If
HighlightSuccessful:
    'All done - resetting error handling:
    On Error GoTo 0
End Sub
'Following hightlights the text of passed cell-range:
Sub HighlightCellText(rng As Range)
    With rng.Characters(1, Len(s1)).Font
        .ColorIndex = 3     'redCIndex
        .fontStyle = "Bold"
    End With
End Sub
'Following function removes the new line character chr(10) from both edges of the passed string then returns it.
Function TrimNewLineCharsFromEdges(ByVal s As String)
    While Right(s, 1) = Chr(10)
        s = Mid(s, 1, Len(s) - 1)
    Wend
    While Left(s, 1) = Chr(10)
        s = Mid(s, 2, Len(s))
    Wend
    TrimNewLineCharsFromEdges = s
End Function

Sub CheckWhiteSpaceInString()
    FindWhiteSpaceInString ("Hello " & Chr(10))
End Sub
'Following finds (and prints to Immediate window) any White-Spaces in passed string:
Sub FindWhiteSpaceInString(ByVal s As String)
    For i = 9 To 11
        If InStr(s, Chr(i)) <> 0 Then
            Debug.Print "Chr(" & i & ") found in string"
        End If
    Next i
    'Debug.Print InStr(1, [A1], Chr(13), vbTextCompare)
    'MsgBox InStr(1, [B1], Chr(13), vbTextCompare)
End Sub
'Following function replaces blank spaces and CrLf (with ¬ sign) from the passed string.
'Usage: cleanString = ReplaceWhiteSpaces(string_with_whitespaces)
Function ReplaceWhiteSpaces(ByVal s As String)
    s = Replace(s, Chr(10), "¬")
    s = Replace(s, Chr(13), "¬")
    s = Replace(s, Chr(32), "¬")
    ReplaceWhiteSpaces = s
End Function
'Following function compares two passed strings and return the postion of the first different character.
'Returns: 0 if no difference found.
'Usage: diffCharPosition = GetDiffCharPosition(string1, string2)
Function GetFirstDiffCharPosition(ByVal s1 As String, ByVal s2 As String)
    If StrComp(s1, s2, vbTextCompare) = 0 Then
        GetFirstDiffCharPosition = 0
    Else
        For i = 1 To Len(s1)
            If StrComp(Mid(s1, i, 1), Mid(s2, i, 1), vbTextCompare) <> 0 Then
                GetFirstDiffCharPosition = i
                Exit Function
            End If
        Next i
    End If
End Function
'Following function compares two passed strings and return the postion of the first different character.
'Returns: 0 if no difference found.
'Usage: diffLetterPosition = GetFirstDiffNonWhitespaceCharPosition(string1, string2)
Function GetFirstDiffNonWhitespaceCharPosition(ByVal s1 As String, ByVal s2 As String)
    If StrComp(s1, s2, vbTextCompare) = 0 Then
        GetFirstDiffNonWhitespaceCharPosition = 0
    Else
        i = 1: j = 1
        Do
            If StrComp(Mid(s1, i, 1), Mid(s2, j, 1), vbTextCompare) <> 0 Then
                Exit Do
            End If
            If Mid(s1, i, 1) = "¬" Then
                While Mid(s1, i, 1) = "¬": i = i + 1: Wend
            Else
                i = i + 1
            End If
            If Mid(s2, j, 1) = "¬" Then
                While Mid(s2, j, 1) = "¬": j = j + 1: Wend
            Else
                j = j + 1
            End If
        Loop While (i <= Len(s2)) Or (j <= Len(s2))
        GetFirstDiffNonWhitespaceCharPosition = i
    End If
End Function
'Following inserts a new sheet and copies the ActiveCell into new sheet's [A1] & [B1]:
Sub Copy2xCellsToNewSheet()
    Dim aCell As Range: Set aCell = ActiveCell
    Dim bCell As Range: Set bCell = ActiveCell.Offset(0, 1)
'    Set wbNew = Workbooks.Add
    Dim originalSheet As Worksheet: Set originalSheet = ActiveSheet
    Dim NewSh As Worksheet
    Set NewSh = Sheets.Add(after:=ActiveSheet)
    aCell.Copy
    With NewSh.[A1]
        .PasteSpecial xlPasteColumnWidths
        .PasteSpecial xlPasteFormats
        .PasteSpecial xlPasteValues
    End With
    bCell.Copy
    With NewSh.[B1]
        .PasteSpecial xlPasteColumnWidths
        .PasteSpecial xlPasteFormats
        .PasteSpecial xlPasteValues
    End With
    
    'Trimming Left & Right whitespace:
    [A1].Value = Trim([A1].Value)
    [B1].Value = Trim([B1].Value)
End Sub
'Following inserts a new temporary workbook and copies the ActiveCell to new sheet's [A1]:
Sub CurrCellToNewTempBook()
    Dim aCell As Range: Set aCell = ActiveCell
'    Dim bCell As Range: Set bCell = ActiveCell.Offset(0, 1)
    Dim NewWb As Workbook
    Set NewWb = Workbooks.Add
    Dim originalSheet As Worksheet: Set originalSheet = ActiveSheet
'    Dim NewSh As Worksheet
'    Set NewSh = Sheets.Add(after:=ActiveSheet)
    aCell.Copy
    With NewWb.Sheets(1).[A1]
        .PasteSpecial xlPasteColumnWidths
        .PasteSpecial xlPasteFormats
        .PasteSpecial xlPasteValues
    End With
    'bCell.Copy
    With NewWb.Sheets(1).[B1]
        .PasteSpecial xlPasteColumnWidths
'        .PasteSpecial xlPasteFormats
'        .PasteSpecial xlPasteValues
    End With
    
    'Trimming Left & Right whitespace:
    [A1].Value = Trim([A1].Value)
    '[B1].Value = Trim([B1].Value)
    'Checking Row-Height, if needed, increase it beyond Excel limits (for better readability):
    Call FixRowHeightLimit
End Sub
'Following sub merges two cells to increase/overcome the max Row-Height (409.5):
Sub FixRowHeightLimit()
    ActiveSheet.Cells.VerticalAlignment = xlTop
    Debug.Print [A1].RowHeight
    If [A1].RowHeight > 380 Then
        [A2].RowHeight = 409.5
        Range("A1:A2").Merge
        Range("B1:B2").Merge
    End If
    If [A3].RowHeight > 380 Then
        [A4].RowHeight = 409.5
        Range("A3:A4").Merge
        Range("B3:B4").Merge
    End If
    [B2].Select
End Sub
'Following sub checks the basic equality of the passed parameters and shows MsgBox accordingly:
Sub BasicEqualityCheck(ByVal txt1 As String, ByVal txt2 As String, trimmedTxt As Boolean)
    Dim trmMsg As String: trmMsg = ""
    If trimmedTxt Then
        trmMsg = ", however there are some variations in white-spaces."
    End If
    If txt1 = txt2 Then
        MsgBox "No difference found in text" + trmMsg, vbOKOnly & vbInformation, "Text-Diff Result"
        End
    End If
End Sub

'Following sorts the lines/values in [A1] & [B1] cells, then copies into [A3] & [B3] cells respectively:
Sub SortThenCopyA1B1ValuesToA3B3()
    Dim arr1() As String
    Dim arr2() As Variant
    'Creating a array list:
    Set arrList = CreateObject("System.Collections.ArrayList")
    
  '[A1] -> [A3]:
    'arr1() = Split([A1], Chr(10)) 'split by Soft-Enters / new-lines
    arr1() = Split([A1], vbLf) 'split by Soft-Enters / new-lines
    
    arrList.Clear   'Clearing Array-List
    ' adding each lines in the array_list:
    For Each element In arr1()
        arrList.Add element
    Next
    arrList.Sort    'sorting arrList
    arr2() = arrList.toarray  'Array-List elements into array.
    'MsgBox Join(arr2, vbLf)
    [A3] = Join(arr2, vbLf)
    
  '[B1] -> [B3]:
    arr1() = Split([B1], vbLf) 'split by Soft-Enters / new-lines
    ' adding each lines in the array_list:
    arrList.Clear   'Clearing Array-List
    For Each element In arr1()
        arrList.Add element
    Next
    arrList.Sort    'sorting arrList
    arr2() = arrList.toarray  'Array-List elements into array.
    [B3] = Join(arr2, vbLf)
End Sub

''Usage: IsInArrayInt("item1", arrName) -> this will check if passed arg1 is in the array-2nd-arg. Cast-INsensitive for string-comparisons.
''Returns: Position As Integer (not index number), returns -1 if element not found in array.
''e.g. If (IsInArrayInt(Cells(1, c), arr)) Then
'Function IsInArrayInt(ByVal stringToBeFound As Variant, ByVal arr As Variant) As Integer
'    If UBound(arr) = -1 Then    'array is not initialised - if not checked here Match() will return "Type mismatch" error.
'        IsInArrayInt = -1
'        Exit Function
'    Else
'        If Not IsError(Application.Match(stringToBeFound, arr, 0)) Then
'            IsInArrayInt = Application.Match(stringToBeFound, arr, 0)
'        Else
'            IsInArrayInt = -1
'        End If
'    End If
'End Function
'Sub IsInArrayIntTest()
'    arr = Split("A B C D E F", " ")
'    'MsgBox UBound(a) 'a(0)
'    MsgBox IsInArrayInt("a", arr) '=> 1 as "a" is at 1st position (finds regardless of lower-case).
'End Sub


'------------x------------