[Libreoffice-commits] core.git: wizards/source
Jean-Pierre Ledure (via logerrit)
logerrit at kemper.freedesktop.org
Thu Nov 5 15:02:53 UTC 2020
wizards/source/scriptforge/SF_Array.xba | 2549 +++++++++++++++++++++
wizards/source/scriptforge/SF_Dictionary.xba | 952 +++++++
wizards/source/scriptforge/SF_Exception.xba | 1107 +++++++++
wizards/source/scriptforge/SF_FileSystem.xba | 2084 +++++++++++++++++
wizards/source/scriptforge/SF_L10N.xba | 696 +++++
wizards/source/scriptforge/SF_Platform.xba | 281 ++
wizards/source/scriptforge/SF_Root.xba | 822 ++++++
wizards/source/scriptforge/SF_Services.xba | 607 +++++
wizards/source/scriptforge/SF_Session.xba | 918 +++++++
wizards/source/scriptforge/SF_String.xba | 2642 ++++++++++++++++++++++
wizards/source/scriptforge/SF_TextStream.xba | 701 +++++
wizards/source/scriptforge/SF_Timer.xba | 463 +++
wizards/source/scriptforge/SF_UI.xba | 1175 +++++++++
wizards/source/scriptforge/SF_Utils.xba | 967 ++++++++
wizards/source/scriptforge/_CodingConventions.xba | 100
wizards/source/scriptforge/_ModuleModel.xba | 221 +
wizards/source/scriptforge/__License.xba | 25
wizards/source/scriptforge/dialog.xlb | 6
wizards/source/scriptforge/dlgConsole.xdl | 14
wizards/source/scriptforge/dlgProgress.xdl | 11
wizards/source/scriptforge/script.xlb | 21
21 files changed, 16362 insertions(+)
New commits:
commit 09c1bee1f91315fd7901af1804e028f6574228a6
Author: Jean-Pierre Ledure <jp at ledure.be>
AuthorDate: Thu Nov 5 15:55:39 2020 +0100
Commit: Jean-Pierre Ledure <jp at ledure.be>
CommitDate: Thu Nov 5 15:55:39 2020 +0100
ScriptForge - core library
Additional "LibreOffice Macros & Dialogs" library
Change-Id: I7380cf3f9ee56b73cfcf7b9e33d0cf50ecb40429
diff --git a/wizards/source/scriptforge/SF_Array.xba b/wizards/source/scriptforge/SF_Array.xba
new file mode 100644
index 000000000000..914f42269867
--- /dev/null
+++ b/wizards/source/scriptforge/SF_Array.xba
@@ -0,0 +1,2549 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
+<script:module xmlns:script="http://openoffice.org/2000/script" script:name="SF_Array" script:language="StarBasic" script:moduleType="normal">REM =======================================================================================================================
+REM === The ScriptForge library and its associated libraries are part of the LibreOffice project. ===
+REM === Full documentation is available on https://help.libreoffice.org/ ===
+REM =======================================================================================================================
+
+Option Compatible
+Option Explicit
+
+'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+''' SF_Array
+''' ========
+''' Singleton class implementing the "ScriptForge.Array" service
+''' Implemented as a usual Basic module
+''' Only 1D or 2D arrays are considered. Arrays with more than 2 dimensions are rejected
+''' With the noticeable exception of the CountDims method (>2 dims allowed)
+''' The first argument of almost every method is the array to consider
+''' It is always passed by reference and left unchanged
+'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+REM ================================================================== EXCEPTIONS
+
+Const ARRAYSEQUENCEERROR = "ARRAYSEQUENCEERROR" ' Incoherent arguments
+Const ARRAYINSERTERROR = "ARRAYINSERTERROR" ' Matrix and vector have incompatible sizes
+Const ARRAYINDEX1ERROR = "ARRAYINDEX1ERROR" ' Given index does not fit in array bounds
+Const ARRAYINDEX2ERROR = "ARRAYINDEX2ERROR" ' Given indexes do not fit in array bounds
+Const CSVPARSINGERROR = "CSVPARSINGERROR" ' Parsing error detected while parsing a csv file
+Const CSVOVERFLOWWARNING = "CSVOVERFLOWWARNING" ' Array becoming too big, import process of csv file is interrupted
+
+REM ============================================================ MODULE CONSTANTS
+
+Const MAXREPR = 50 ' Maximum length to represent an array in the console
+
+REM ===================================================== CONSTRUCTOR/DESCTRUCTOR
+
+REM -----------------------------------------------------------------------------
+Public Function Dispose() As Variant
+ Set Dispose = Nothing
+End Function ' ScriptForge.SF_Array Explicit destructor
+
+REM ================================================================== PROPERTIES
+
+REM -----------------------------------------------------------------------------
+Property Get ObjectType As String
+''' Only to enable object representation
+ ObjectType = "SF_Array"
+End Property ' ScriptForge.SF_Array.ObjectType
+
+REM -----------------------------------------------------------------------------
+Property Get ServiceName As String
+''' Internal use
+ ServiceName = "ScriptForge.Array"
+End Property ' ScriptForge.SF_Array.ServiceName
+
+REM ============================================================== PUBLIC METHODS
+
+REM -----------------------------------------------------------------------------
+Public Function Append(Optional ByRef Array_1D As Variant _
+ , ParamArray pvArgs() As Variant _
+ ) As Variant
+''' Append at the end of the input array the items listed as arguments
+''' Arguments are appended blindly
+''' each of them might be a scalar of any type or a subarray
+''' Args
+''' Array_1D: the pre-existing array, may be empty
+''' pvArgs: a list of items to append to Array_1D
+''' Return:
+''' the new extended array. Its LBound is identical to that of Array_1D
+''' Examples:
+''' SF_Array.Append(Array(1, 2, 3), 4, 5) returns (1, 2, 3, 4, 5)
+
+Dim vAppend As Variant ' Return value
+Dim lNbArgs As Long ' Number of elements to append
+Dim lMax As Long ' UBound of input array
+Dim i As Long
+Const cstThisSub = "Array.Append"
+Const cstSubArgs = "Array_1D, arg0[, arg1] ..."
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vAppend = Array()
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_1D, "Array_1D", 1) Then GoTo Finally
+ End If
+
+Try:
+ lMax = UBound(Array_1D)
+ lNbArgs = UBound(pvArgs) + 1 ' pvArgs is always zero-based
+ If lMax < LBound(Array_1D) Then ' Initial array is empty
+ If lNbArgs > 0 Then
+ ReDim vAppend(0 To lNbArgs - 1)
+ End If
+ Else
+ vAppend() = Array_1D()
+ If lNbArgs > 0 Then
+ ReDim Preserve vAppend(LBound(Array_1D) To lMax + lNbArgs)
+ End If
+ End If
+ For i = 1 To lNbArgs
+ vAppend(lMax + i) = pvArgs(i - 1)
+ Next i
+
+Finally:
+ Append = vAppend()
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.Append
+
+REM -----------------------------------------------------------------------------
+Public Function AppendColumn(Optional ByRef Array_2D As Variant _
+ , Optional ByRef Column As Variant _
+ ) As Variant
+''' AppendColumn appends to the right side of a 2D array a new Column
+''' Args
+''' Array_2D: the pre-existing array, may be empty
+''' If the array has 1 dimension, it is considered as the 1st Column of the resulting 2D array
+''' Column: a 1D array with as many items as there are rows in Array_2D
+''' Returns:
+''' the new extended array. Its LBounds are identical to that of Array_2D
+''' Exceptions:
+''' ARRAYINSERTERROR
+''' Examples:
+''' SF_Array.AppendColumn(Array(1, 2, 3), Array(4, 5, 6)) returns ((1, 4), (2, 5), (3, 6))
+''' x = SF_Array.AppendColumn(Array(), Array(1, 2, 3)) => ∀ i ∈ {0 ≤ i ≤ 2} : x(0, i) ≡ i
+
+Dim vAppendColumn As Variant ' Return value
+Dim iDims As Integer ' Dimensions of Array_2D
+Dim lMin1 As Long ' LBound1 of input array
+Dim lMax1 As Long ' UBound1 of input array
+Dim lMin2 As Long ' LBound2 of input array
+Dim lMax2 As Long ' UBound2 of input array
+Dim lMin As Long ' LBound of Column array
+Dim lMax As Long ' UBound of Column array
+Dim i As Long
+Dim j As Long
+Const cstThisSub = "Array.AppendColumn"
+Const cstSubArgs = "Array_2D, Column"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vAppendColumn = Array()
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_2D, "Array_2D") Then GoTo Finally 'Initial check: not missing and array
+ If Not SF_Utils._ValidateArray(Column, "Column", 1) Then GoTo Finally
+ End If
+ iDims = SF_Array.CountDims(Array_2D)
+ If iDims > 2 Then
+ If Not SF_Utils._ValidateArray(Array_2D, "Array_2D", 2) Then GoTo Finally '2nd check to manage error
+ End If
+
+Try:
+ lMin = LBound(Column)
+ lMax = UBound(Column)
+
+ ' Compute future dimensions of output array
+ Select Case iDims
+ Case 0 : lMin1 = lMin : lMax1 = lMax
+ lMin2 = 0 : lMax2 = -1
+ Case 1 : lMin1 = LBound(Array_2D, 1) : lMax1 = UBound(Array_2D, 1)
+ lMin2 = 0 : lMax2 = 0
+ Case 2 : lMin1 = LBound(Array_2D, 1) : lMax1 = UBound(Array_2D, 1)
+ lMin2 = LBound(Array_2D, 2) : lMax2 = UBound(Array_2D, 2)
+ End Select
+ If iDims > 0 And lMax - lMin <> lMax1 - lMin1 Then GoTo CatchColumn
+ ReDim vAppendColumn(lMin1 To lMax1, lMin2 To lMax2 + 1)
+
+ ' Copy input array to output array
+ For i = lMin1 To lMax1
+ For j = lMin2 To lMax2
+ If iDims = 2 Then vAppendColumn(i, j) = Array_2D(i, j) Else vAppendColumn(i, j) = Array_2D(i)
+ Next j
+ Next i
+ ' Copy new Column
+ For i = lMin1 To lMax1
+ vAppendColumn(i, lMax2 + 1) = Column(i)
+ Next i
+
+Finally:
+ AppendColumn = vAppendColumn()
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchColumn:
+ SF_Exception.RaiseFatal(ARRAYINSERTERROR, "Column", SF_Array._Repr(Array_2D), SF_Utils._Repr(Column, MAXREPR))
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.AppendColumn
+
+REM -----------------------------------------------------------------------------
+Public Function AppendRow(Optional ByRef Array_2D As Variant _
+ , Optional ByRef Row As Variant _
+ ) As Variant
+''' AppendRow appends below a 2D array a new row
+''' Args
+''' Array_2D: the pre-existing array, may be empty
+''' If the array has 1 dimension, it is considered as the 1st row of the resulting 2D array
+''' Row: a 1D array with as many items as there are columns in Array_2D
+''' Returns:
+''' the new extended array. Its LBounds are identical to that of Array_2D
+''' Exceptions:
+''' ARRAYINSERTERROR
+''' Examples:
+''' SF_Array.AppendRow(Array(1, 2, 3), Array(4, 5, 6)) returns ((1, 2, 3), (4, 5, 6))
+''' x = SF_Array.AppendRow(Array(), Array(1, 2, 3)) => ∀ i ∈ {0 ≤ i ≤ 2} : x(i, 0) ≡ i
+
+Dim vAppendRow As Variant ' Return value
+Dim iDims As Integer ' Dimensions of Array_2D
+Dim lMin1 As Long ' LBound1 of input array
+Dim lMax1 As Long ' UBound1 of input array
+Dim lMin2 As Long ' LBound2 of input array
+Dim lMax2 As Long ' UBound2 of input array
+Dim lMin As Long ' LBound of row array
+Dim lMax As Long ' UBound of row array
+Dim i As Long
+Dim j As Long
+Const cstThisSub = "Array.AppendRow"
+Const cstSubArgs = "Array_2D, Row"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vAppendRow = Array()
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_2D, "Array_2D") Then GoTo Finally 'Initial check: not missing and array
+ If Not SF_Utils._ValidateArray(Row, "Row", 1) Then GoTo Finally
+ End If
+ iDims = SF_Array.CountDims(Array_2D)
+ If iDims > 2 Then
+ If Not SF_Utils._ValidateArray(Array_2D, "Array_2D", 2) Then GoTo Finally '2nd check to manage error
+ End If
+
+Try:
+ lMin = LBound(Row)
+ lMax = UBound(Row)
+
+ ' Compute future dimensions of output array
+ Select Case iDims
+ Case 0 : lMin1 = 0 : lMax1 = -1
+ lMin2 = lMin : lMax2 = lMax
+ Case 1 : lMin1 = 0 : lMax1 = 0
+ lMin2 = LBound(Array_2D, 1) : lMax2 = UBound(Array_2D, 1)
+ Case 2 : lMin1 = LBound(Array_2D, 1) : lMax1 = UBound(Array_2D, 1)
+ lMin2 = LBound(Array_2D, 2) : lMax2 = UBound(Array_2D, 2)
+ End Select
+ If iDims > 0 And lMax - lMin <> lMax2 - lMin2 Then GoTo CatchRow
+ ReDim vAppendRow(lMin1 To lMax1 + 1, lMin2 To lMax2)
+
+ ' Copy input array to output array
+ For i = lMin1 To lMax1
+ For j = lMin2 To lMax2
+ If iDims = 2 Then vAppendRow(i, j) = Array_2D(i, j) Else vAppendRow(i, j) = Array_2D(j)
+ Next j
+ Next i
+ ' Copy new row
+ For j = lMin2 To lMax2
+ vAppendRow(lMax1 + 1, j) = Row(j)
+ Next j
+
+Finally:
+ AppendRow = vAppendRow()
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchRow:
+ SF_Exception.RaiseFatal(ARRAYINSERTERROR, "Row", SF_Array._Repr(Array_2D), SF_Utils._Repr(Row, MAXREPR))
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.AppendRow
+
+REM -----------------------------------------------------------------------------
+Public Function Contains(Optional ByRef Array_1D As Variant _
+ , Optional ByVal ToFind As Variant _
+ , Optional ByVal CaseSensitive As Variant _
+ , Optional ByVal SortOrder As Variant _
+ ) As Boolean
+''' Check if a 1D array contains the ToFind number, string or date
+''' The comparison between strings can be done case-sensitive or not
+''' If the array is sorted then
+''' the array must be filled homogeneously, i.e. all items must be of the same type
+''' Empty and Null items are forbidden
+''' a binary search is done
+''' Otherwise the array is scanned from top. Null or Empty items are simply ignored
+''' Args:
+''' Array_1D: the array to scan
+''' ToFind: a number, a date or a string to find
+''' CaseSensitive: Only for string comparisons, default = False
+''' SortOrder: "ASC", "DESC" or "" (= not sorted, default)
+''' Return: True when found
+''' Result is unpredictable when array is announced sorted and is in reality not
+''' Examples:
+''' SF_Array.Contains(Array("A","B","c","D"), "C", SortOrder := "ASC") returns True
+''' SF_Array.Contains(Array("A","B","c","D"), "C", CaseSensitive := True) returns False
+
+Dim bContains As Boolean ' Return value
+Dim iToFindType As Integer ' VarType of ToFind
+Const cstThisSub = "Array.Contains"
+Const cstSubArgs = "Array_1D, ToFind, [CaseSensitive=False], [SortOrder=""""|""ASC""|""DESC""]"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+ bContains = False
+
+Check:
+ If IsMissing(CaseSensitive) Or IsEmpty(CaseSensitive) Then CaseSensitive = False
+ If IsMissing(SortOrder) Or IsEmpty(SortOrder) Then SortOrder = ""
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._Validate(SortOrder, "SortOrder", V_STRING, Array("ASC", "DESC", "")) Then GoTo Finally
+ If Not SF_Utils._Validate(ToFind, "ToFind", Array(V_STRING, V_DATE, V_NUMERIC)) Then GoTo Finally
+ iToFindType = SF_Utils._VarTypeExt(ToFind)
+ If SortOrder <> "" Then
+ If Not SF_Utils._ValidateArray(Array_1D, "Array_1D", 1, iToFindType) Then GoTo Finally
+ Else
+ If Not SF_Utils._ValidateArray(Array_1D, "Array_1D", 1) Then GoTo Finally
+ End If
+ If Not SF_Utils._Validate(CaseSensitive, "CaseSensitive", V_BOOLEAN) Then GoTo Finally
+ End If
+
+Try:
+ bContains = SF_Array._FindItem(Array_1D, ToFind, CaseSensitive, SortOrder)(0)
+
+Finally:
+ Contains = bContains
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.Contains
+
+REM -----------------------------------------------------------------------------
+Public Function ConvertToDictionary(Optional ByRef Array_2D As Variant) As Variant
+''' Store the content of a 2-columns array into a dictionary
+''' Key found in 1st column, Item found in 2nd
+''' Args:
+''' Array_2D: 1st column must contain exclusively non zero-length strings
+''' 1st column may not be sorted
+''' Returns:
+''' a ScriptForge dictionary object
+''' Examples:
+'''
+
+Dim oDict As Variant ' Return value
+Dim i As Long
+Const cstThisSub = "Dictionary.ConvertToArray"
+Const cstSubArgs = "Array_2D"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_2D, "Array_2D", 2, V_STRING, True) Then GoTo Finally
+ End If
+
+Try:
+ Set oDict = SF_Services.CreateScriptService("Dictionary")
+ For i = LBound(Array_2D, 1) To UBound(Array_2D, 1)
+ oDict.Add(Array_2D(i, 0), Array_2D(i, 1))
+ Next i
+
+ ConvertToDictionary = oDict
+
+Finally:
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.ConvertToDictionary
+
+REM -----------------------------------------------------------------------------
+Public Function CountDims(Optional ByRef Array_ND As Variant) As Integer
+''' Count the number of dimensions of an array - may be > 2
+''' Args:
+''' Array_ND: the array to be examined
+''' Return: the number of dimensions: -1 = not array, 0 = unitialized array, else >= 1
+''' Examples:
+''' Dim a(1 To 10, -3 To 12, 5)
+''' CountDims(a) returns 3
+
+Dim iDims As Integer ' Return value
+Dim lMax As Long ' Storage for UBound of each dimension
+Const cstThisSub = "Array.CountDims"
+Const cstSubArgs = "Array_ND"
+
+Check:
+ iDims = -1
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If IsMissing(Array_ND) Then ' To have missing exception processed
+ If Not SF_Utils._ValidateArray(Array_ND, "Array_ND") Then GoTo Finally
+ End If
+ End If
+
+Try:
+ On Local Error Goto ErrHandler
+ ' Loop, increasing the dimension index (i) until an error occurs.
+ ' An error will occur when i exceeds the number of dimensions in the array. Returns i - 1.
+ iDims = 0
+ If Not IsArray(Array_ND) Then
+ Else
+ Do
+ iDims = iDims + 1
+ lMax = UBound(Array_ND, iDims)
+ Loop Until (Err <> 0)
+ End If
+
+ ErrHandler:
+ On Local Error GoTo 0
+
+ iDims = iDims - 1
+ If iDims = 1 Then
+ If LBound(Array_ND, 1) > UBound(Array_ND, 1) Then iDims = 0
+ End If
+
+Finally:
+ CountDims = iDims
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+End Function ' ScriptForge.SF_Array.CountDims
+
+REM -----------------------------------------------------------------------------
+Public Function Difference(Optional ByRef Array1_1D As Variant _
+ , Optional ByRef Array2_1D As Variant _
+ , Optional ByVal CaseSensitive As Variant _
+ ) As Variant
+''' Build a set being the Difference of the two input arrays, i.e. items are contained in 1st array and NOT in 2nd
+''' both input arrays must be filled homogeneously, i.e. all items must be of the same type
+''' Empty and Null items are forbidden
+''' The comparison between strings is case sensitive or not
+''' Args:
+''' Array1_1D: a 1st input array
+''' Array2_1D: a 2nd input array
+''' CaseSensitive: default = False
+''' Returns: a zero-based array containing unique items from the 1st array not present in the 2nd
+''' The output array is sorted in ascending order
+''' Examples:
+''' SF_Array.Difference(Array("A", "C", "A", "b", "B"), Array("C", "Z", "b"), True) returns ("A", "B")
+
+Dim vDifference() As Variant ' Return value
+Dim vSorted() As Variant ' The 2nd input array after sort
+Dim iType As Integer ' VarType of elements in input arrays
+Dim lMin1 As Long ' LBound of 1st input array
+Dim lMax1 As Long ' UBound of 1st input array
+Dim lMin2 As Long ' LBound of 2nd input array
+Dim lMax2 As Long ' UBound of 2nd input array
+Dim lSize As Long ' Number of Difference items
+Dim vItem As Variant ' One single item in the array
+Dim i As Long
+Const cstThisSub = "Array.Difference"
+Const cstSubArgs = "Array1_1D, Array2_1D, [CaseSensitive=False]"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vDifference = Array()
+
+Check:
+ If IsMissing(CaseSensitive) Then CaseSensitive = False
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array1_1D, "Array1_1D", 1, 0, True) Then GoTo Finally
+ iType = SF_Utils._VarTypeExt(Array1_1D(LBound(Array1_1D)))
+ If Not SF_Utils._ValidateArray(Array2_1D, "Array2_1D", 1, iType, True) Then GoTo Finally
+ If Not SF_Utils._Validate(CaseSensitive, "CaseSensitive", V_BOOLEAN) Then GoTo Finally
+ End If
+
+Try:
+ lMin1 = LBound(Array1_1D) : lMax1 = UBound(Array1_1D)
+ lMin2 = LBound(Array2_1D) : lMax2 = UBound(Array2_1D)
+
+ ' If 1st array is empty, do nothing
+ If lMax1 < lMin1 Then
+ ElseIf lMax2 < lMin2 Then ' only 2nd array is empty
+ vUnion = SF_Array.Unique(Array1_1D, CaseSensitive)
+ Else
+
+ ' First sort the 2nd array
+ vSorted = SF_Array.Sort(Array2_1D, "ASC", CaseSensitive)
+
+ ' Resize the output array to the size of the 1st array
+ ReDim vDifference(0 To (lMax1 - lMin1))
+ lSize = -1
+
+ ' Fill vDifference one by one with items present only in 1st set
+ For i = lMin1 To lMax1
+ vItem = Array1_1D(i)
+ If Not SF_Array.Contains(vSorted, vItem, CaseSensitive, "ASC") Then
+ lSize = lSize + 1
+ vDifference(lSize) = vItem
+ End If
+ Next i
+
+ ' Remove unfilled entries and duplicates
+ If lSize >= 0 Then
+ ReDim Preserve vDifference(0 To lSize)
+ vDifference() = SF_Array.Unique(vDifference, CaseSensitive)
+ Else
+ vDifference = Array()
+ End If
+ End If
+
+Finally:
+ Difference = vDifference()
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.Difference
+
+REM -----------------------------------------------------------------------------
+Public Function ExportToTextFile(Optional ByRef Array_1D As Variant _
+ , Optional ByVal FileName As Variant _
+ , Optional ByVal Encoding As Variant _
+ ) As Boolean
+''' Write all items of the array sequentially to a text file
+''' If the file exists already, it will be overwritten without warning
+''' Args:
+''' Array_1D: the array to export
+''' FileName: the full name (path + file) in SF_FileSystem.FileNaming notation
+''' Encoding: The character set that should be used
+''' Use one of the Names listed in https://www.iana.org/assignments/character-sets/character-sets.xhtml
+''' Note that LibreOffice does not implement all existing sets
+''' Default = UTF-8
+''' Returns:
+''' True if successful
+''' Examples:
+''' SF_Array.ExportToTextFile(Array("A","B","C","D"), "C:\Temp\A short file.txt")
+
+Dim bExport As Boolean ' Return value
+Dim oFile As Object ' Output file handler
+Dim sLine As String ' A single line
+Const cstThisSub = "Array.ExportToTextFile"
+Const cstSubArgs = "Array_1D, FileName"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bExport = False
+
+Check:
+ If IsMissing(Encoding) Or IsEmpty(Encoding) Then Encoding = "UTF-8"
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_1D, "Array_1D", 1, V_STRING, True) Then GoTo Finally
+ If Not SF_Utils._ValidateFile(FileName, "FileName") Then GoTo Finally
+ If Not SF_Utils._Validate(Encoding, "Encoding", V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ Set oFile = SF_FileSystem.CreateTextFile(FileName, Overwrite := True, Encoding := Encoding)
+ If Not IsNull(oFile) Then
+ With oFile
+ For Each sLine In Array_1D
+ .WriteLine(sLine)
+ Next sLine
+ .CloseFile()
+ End With
+ End If
+
+ bExport = True
+
+Finally:
+ If Not IsNull(oFile) Then Set oFile = oFile.Dispose()
+ ExportToTextFile = bExport
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.ExportToTextFile
+
+REM -----------------------------------------------------------------------------
+Public Function ExtractColumn(Optional ByRef Array_2D As Variant _
+ , Optional ByVal ColumnIndex As Variant _
+ ) As Variant
+''' ExtractColumn extracts from a 2D array a specific column
+''' Args
+''' Array_2D: the array from which to extract
+''' ColumnIndex: the column to extract - must be in the interval [LBound, UBound]
+''' Returns:
+''' the extracted column. Its LBound and UBound are identical to that of the 1st dimension of Array_2D
+''' Exceptions:
+''' ARRAYINDEX1ERROR
+''' Examples:
+''' |1, 2, 3|
+''' SF_Array.ExtractColumn( |4, 5, 6|, 2) returns (3, 6, 9)
+''' |7, 8, 9|
+
+Dim vExtractColumn As Variant ' Return value
+Dim lMin1 As Long ' LBound1 of input array
+Dim lMax1 As Long ' UBound1 of input array
+Dim lMin2 As Long ' LBound1 of input array
+Dim lMax2 As Long ' UBound1 of input array
+Dim i As Long
+Const cstThisSub = "Array.ExtractColumn"
+Const cstSubArgs = "Array_2D, ColumnIndex"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vExtractColumn = Array()
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_2D, "Array_2D", 2) Then GoTo Finally
+ If Not SF_Utils._Validate(ColumnIndex, "ColumnIndex", V_NUMERIC) Then GoTo Finally
+ End If
+
+Try:
+ ' Compute future dimensions of output array
+ lMin2 = LBound(Array_2D, 2) : lMax2 = UBound(Array_2D, 2)
+ If ColumnIndex < lMin2 Or ColumnIndex > lMax2 Then GoTo CatchIndex
+ lMin1 = LBound(Array_2D, 1) : lMax1 = UBound(Array_2D, 1)
+ ReDim vExtractColumn(lMin1 To lMax1)
+
+ ' Copy Column of input array to output array
+ For i = lMin1 To lMax1
+ vExtractColumn(i) = Array_2D(i, ColumnIndex)
+ Next i
+
+Finally:
+ ExtractColumn = vExtractColumn()
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchIndex:
+ SF_Exception.RaiseFatal(ARRAYINDEX1ERROR, "ColumnIndex", SF_Array._Repr(Array_2D), ColumnIndex)
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.ExtractColumn
+
+REM -----------------------------------------------------------------------------
+Public Function ExtractRow(Optional ByRef Array_2D As Variant _
+ , Optional ByVal RowIndex As Variant _
+ ) As Variant
+''' ExtractRow extracts from a 2D array a specific row
+''' Args
+''' Array_2D: the array from which to extract
+''' RowIndex: the row to extract - must be in the interval [LBound, UBound]
+''' Returns:
+''' the extracted row. Its LBound and UBound are identical to that of the 2nd dimension of Array_2D
+''' Exceptions:
+''' ARRAYINDEX1ERROR
+''' Examples:
+''' |1, 2, 3|
+''' SF_Array.ExtractRow(|4, 5, 6|, 2) returns (7, 8, 9)
+''' |7, 8, 9|
+
+Dim vExtractRow As Variant ' Return value
+Dim lMin1 As Long ' LBound1 of input array
+Dim lMax1 As Long ' UBound1 of input array
+Dim lMin2 As Long ' LBound1 of input array
+Dim lMax2 As Long ' UBound1 of input array
+Dim i As Long
+Const cstThisSub = "Array.ExtractRow"
+Const cstSubArgs = "Array_2D, RowIndex"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vExtractRow = Array()
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_2D, "Array_2D", 2) Then GoTo Finally
+ If Not SF_Utils._Validate(RowIndex, "RowIndex", V_NUMERIC) Then GoTo Finally
+ End If
+
+Try:
+ ' Compute future dimensions of output array
+ lMin1 = LBound(Array_2D, 1) : lMax1 = UBound(Array_2D, 1)
+ If RowIndex < lMin1 Or RowIndex > lMax1 Then GoTo CatchIndex
+ lMin2 = LBound(Array_2D, 2) : lMax2 = UBound(Array_2D, 2)
+ ReDim vExtractRow(lMin2 To lMax2)
+
+ ' Copy row of input array to output array
+ For i = lMin2 To lMax2
+ vExtractRow(i) = Array_2D(RowIndex, i)
+ Next i
+
+Finally:
+ ExtractRow = vExtractRow()
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchIndex:
+ SF_Exception.RaiseFatal(ARRAYINDEX1ERROR, "RowIndex", SF_Array._Repr(Array_2D), RowIndex)
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.ExtractRow
+
+REM -----------------------------------------------------------------------------
+Public Function Flatten(Optional ByRef Array_1D As Variant) As Variant
+''' Stack all items and all items in subarrays into one array without subarrays
+''' Args
+''' Array_1D: the pre-existing array, may be empty
+''' Return:
+''' The new flattened array. Its LBound is identical to that of Array_1D
+''' If one of the subarrays has a number of dimensions > 1 Then that subarray is left unchanged
+''' Examples:
+''' SF_Array.Flatten(Array(1, 2, Array(3, 4, 5)) returns (1, 2, 3, 4, 5)
+
+Dim vFlatten As Variant ' Return value
+Dim lMin As Long ' LBound of input array
+Dim lMax As Long ' UBound of input array
+Dim lIndex As Long ' Index in output array
+Dim vItem As Variant ' Array single item
+Dim iDims As Integer ' Array number of dimensions
+Dim lEmpty As Long ' Number of empty subarrays
+Dim i As Long
+Dim j As Long
+Const cstThisSub = "Array.Flatten"
+Const cstSubArgs = "Array_1D"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vFlatten = Array()
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_1D, "Array_1D", 1) Then GoTo Finally
+ End If
+
+Try:
+ If UBound(Array_1D) >= LBound(Array_1D) Then
+ lMin = LBound(Array_1D) : lMax = UBound(Array_1D)
+ ReDim vFlatten(lMin To lMax) ' Initial minimal sizing
+ lEmpty = 0
+ lIndex = lMin - 1
+ For i = lMin To lMax
+ vItem = Array_1D(i)
+ If IsArray(vItem) Then
+ iDims = SF_Array.CountDims(vItem)
+ Select Case iDims
+ Case 0 ' Empty arrays are ignored
+ lEmpty = lEmpty + 1
+ Case 1 ' Only 1D subarrays are flattened
+ ReDim Preserve vFlatten(lMin To UBound(vFlatten) + UBound(vItem) - LBound(vItem))
+ For j = LBound(vItem) To UBound(vItem)
+ lIndex = lIndex + 1
+ vFlatten(lIndex) = vItem(j)
+ Next j
+ Case > 1 ' Other arrays are left unchanged
+ lIndex = lIndex + 1
+ vFlatten(lIndex) = vItem
+ End Select
+ Else
+ lIndex = lIndex + 1
+ vFlatten(lIndex) = vItem
+ End If
+ Next i
+ End If
+ ' Reduce size of output if Array_1D is populated with some empty arrays
+ If lEmpty > 0 Then
+ If lIndex - lEmpty < lMin Then
+ vFlatten = Array()
+ Else
+ ReDim Preserve vFlatten(lMin To UBound(vFlatten) - lEmpty)
+ End If
+ End If
+
+Finally:
+ Flatten = vFlatten()
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.Flatten
+
+REM -----------------------------------------------------------------------------
+Public Function GetProperty(Optional ByVal PropertyName As Variant) As Variant
+''' Return the actual value of the given property
+''' Args:
+''' PropertyName: the name of the property as a string
+''' Returns:
+''' The actual value of the property
+''' Exceptions
+''' ARGUMENTERROR The property does not exist
+
+Const cstThisSub = "Array.GetProperty"
+Const cstSubArgs = "PropertyName"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ GetProperty = Null
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._Validate(PropertyName, "PropertyName", V_STRING, Properties()) Then GoTo Catch
+ End If
+
+Try:
+ Select Case UCase(PropertyName)
+ Case Else
+ End Select
+
+Finally:
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.GetProperty
+
+REM -----------------------------------------------------------------------------
+Public Function ImportFromCSVFile(Optional ByRef FileName As Variant _
+ , Optional ByVal Delimiter As Variant _
+ , Optional ByVal DateFormat As Variant _
+ ) As Variant
+''' Import the data contained in a comma-separated values (CSV) file
+''' The comma may be replaced by any character
+''' Each line in the file contains a full record
+''' Line splitting is not allowed)
+''' However sequences like \n, \t, ... are left unchanged. Use SF_String.Unescape() to manage them
+''' A special mechanism is implemented to load dates
+''' The applicable CSV format is described in https://tools.ietf.org/html/rfc4180
+''' Args:
+''' FileName: the name of the text file containing the data expressed as given by the current FileNaming
+''' property of the SF_FileSystem service. Default = both URL format or native format
+''' Delimiter: Default = ",". Other usual options are ";" and the tab character
+''' DateFormat: either YYYY-MM-DD, DD-MM-YYYY or MM-DD-YYYY
+''' The dash (-) may be replaced by a dot (.), a slash (/) or a space
+''' Other date formats will be ignored
+''' If "" (default), dates will be considered as strings
+''' Returns:
+''' A 2D-array with each row corresponding with a single record read in the file
+''' and each column corresponding with a field of the record
+''' No check is made about the coherence of the field types across columns
+''' A best guess will be made to identify numeric and date types
+''' If a line contains less or more fields than the first line in the file,
+''' an exception will be raised. Empty lines however are simply ignored
+''' If the size of the file exceeds the number of items limit, a warning is raised
+''' and the array is truncated
+''' Exceptions:
+''' CSVPARSINGERROR Given file is not formatted as a csv file
+''' CSVOVERFLOWWARNING Maximum number of allowed items exceeded
+
+Dim vArray As Variant ' Returned array
+Dim lCol As Long ' Index of last column of vArray
+Dim lRow As Long ' Index of current row of vArray
+Dim lFileSize As Long ' Number of records found in the file
+Dim vCsv As Object ' CSV file handler
+Dim sLine As String ' Last read line
+Dim vLine As Variant ' Array of fields of last read line
+Dim sItem As String ' Individual item in the file
+Dim vItem As Variant ' Individual item in the output array
+Dim iPosition As Integer ' Date position in individual item
+Dim iYear As Integer, iMonth As Integer, iDay As Integer
+ ' Date components
+Dim i As Long
+Const cstItemsLimit = 250000 ' Maximum number of admitted items
+Const cstThisSub = "Array.ImportFromCSVFile"
+Const cstSubArgs = "FileName, [Delimiter="",""], [DateFormat=""""]"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vArray = Array()
+
+Check:
+ If IsMissing(Delimiter) Or IsEmpty(Delimiter) Then Delimiter = ","
+ If IsMissing(DateFormat) Or IsEmpty(DateFormat) Then DateFormat = ""
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateFile(FileName, "FileName") Then GoTo Finally
+ If Not SF_Utils._Validate(Delimiter, "Delimiter", V_STRING) Then GoTo Finally
+ If Not SF_Utils._Validate(DateFormat, "DateFormat", V_STRING) Then GoTo Finally
+ End If
+ If Len(Delimiter) = 0 Then Delimiter = ","
+
+Try:
+ ' Counts the lines present in the file to size the final array
+ ' Very beneficial for large files, better than multiple ReDims
+ ' Small overhead for small files
+ lFileSize = SF_FileSystem._CountTextLines(FileName, False)
+ If lFileSize <= 0 Then GoTo Finally
+
+ ' Reread file line by line
+ Set vCsv = SF_FileSystem.OpenTextFile(FileName, IOMode := SF_FileSystem.ForReading)
+ If IsNull(vCsv) Then GoTo Finally ' Open error
+ lRow = -1
+ With vCsv
+ Do While Not .AtEndOfStream
+ sLine = .ReadLine()
+ If Len(sLine) > 0 Then ' Ignore empty lines
+ If InStr(sLine, """") > 0 Then vLine = SF_String.SplitNotQuoted(sLine, Delimiter) Else vLine = Split(sLine, Delimiter) ' Simple split when relevant
+ lRow = lRow + 1
+ If lRow = 0 Then ' Initial sizing of output array
+ lCol = UBound(vLine)
+ ReDim vArray(0 To lFileSize - 1, 0 To lCol)
+ ElseIf UBound(vLine) <> lCol Then
+ GoTo CatchCSVFormat
+ End If
+ ' Check type and copy all items of the line
+ For i = 0 To lCol
+ If Left(vLine(i), 1) = """" Then sItem = SF_String.Unquote(vLine(i)) Else sItem = vLine(i) ' Unquote only when useful
+ ' Interprete the individual line item
+ Select Case True
+ Case IsNumeric(sItem)
+ If InStr(sItem, ".") + InStr(1, sItem, "e", 1) > 0 Then vItem = Val(sItem) Else vItem = CLng(sItem)
+ Case DateFormat <> "" And Len(sItem) = Len(DateFormat)
+ If SF_String.IsADate(sItem, DateFormat) Then
+ iPosition = InStr(DateFormat, "YYYY") : iYear = CInt(Mid(sItem, iPosition, 4))
+ iPosition = InStr(DateFormat, "MM") : iMonth = CInt(Mid(sItem, iPosition, 2))
+ iPosition = InStr(DateFormat, "DD") : iDay = CInt(Mid(sItem, iPosition, 2))
+ vItem = DateSerial(iYear, iMonth, iDay)
+ Else
+ vItem = sItem
+ End If
+ Case Else : vItem = sItem
+ End Select
+ vArray(lRow, i) = vItem
+ Next i
+ End If
+ ' Provision to avoid very large arrays and their sometimes erratic behaviour
+ If (lRow + 2) * (lCol + 1) > cstItemsLimit Then
+ ReDim Preserve vArray(0 To lRow, 0 To lCol)
+ GoTo CatchOverflow
+ End If
+ Loop
+ End With
+
+Finally:
+ If Not IsNull(vCsv) Then
+ vCsv.CloseFile()
+ Set vCsv = vCsv.Dispose()
+ End If
+ ImportFromCSVFile = vArray
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchCSVFormat:
+ SF_Exception.RaiseFatal(CSVPARSINGERROR, FileName, vCsv.Line, sLine)
+ GoTo Finally
+CatchOverflow:
+ 'TODO SF_Exception.RaiseWarning(SF_Exception.CSVOVERFLOWWARNING, cstThisSub)
+ 'MsgBox "TOO MUCH LINES !!"
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.ImportFromCSVFile
+
+REM -----------------------------------------------------------------------------
+Public Function IndexOf(Optional ByRef Array_1D As Variant _
+ , Optional ByVal ToFind As Variant _
+ , Optional ByVal CaseSensitive As Variant _
+ , Optional ByVal SortOrder As Variant _
+ ) As Long
+''' Finds in a 1D array the ToFind number, string or date
+''' ToFind must exist within the array.
+''' The comparison between strings can be done case-sensitively or not
+''' If the array is sorted then
+''' the array must be filled homogeneously, i.e. all items must be of the same type
+''' Empty and Null items are forbidden
+''' a binary search is done
+''' Otherwise the array is scanned from top. Null or Empty items are simply ignored
+''' Args:
+''' Array_1D: the array to scan
+''' ToFind: a number, a date or a string to find
+''' CaseSensitive: Only for string comparisons, default = False
+''' SortOrder: "ASC", "DESC" or "" (= not sorted, default)
+''' Return: the index of the found item, LBound - 1 if not found
+''' Result is unpredictable when array is announced sorted and is in reality not
+''' Examples:
+''' SF_Array.IndexOf(Array("A","B","c","D"), "C", SortOrder := "ASC") returns 2
+''' SF_Array.IndexOf(Array("A","B","c","D"), "C", CaseSensitive := True) returns -1
+
+Dim vFindItem() As Variant ' 2-items array (0) = True if found, (1) = Index where found
+Dim lIndex As Long ' Return value
+Dim iToFindType As Integer ' VarType of ToFind
+Const cstThisSub = "Array.IndexOf"
+Const cstSubArgs = "Array_1D, ToFind, [CaseSensitive=False], [SortOrder=""""|""ASC""|""DESC""]"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+ lIndex = -1
+
+Check:
+ If IsMissing(CaseSensitive) Or IsEmpty(CaseSensitive) Then CaseSensitive = False
+ If IsMissing(SortOrder) Or IsEmpty(SortOrder) Then SortOrder = ""
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._Validate(SortOrder, "SortOrder", V_STRING, Array("ASC", "DESC", "")) Then GoTo Finally
+ If Not SF_Utils._Validate(ToFind, "ToFind", Array(V_STRING, V_DATE, V_NUMERIC)) Then GoTo Finally
+ iToFindType = SF_Utils._VarTypeExt(ToFind)
+ If SortOrder <> "" Then
+ If Not SF_Utils._ValidateArray(Array_1D, "Array", 1, iToFindType) Then GoTo Finally
+ Else
+ If Not SF_Utils._ValidateArray(Array_1D, "Array", 1) Then GoTo Finally
+ End If
+ If Not SF_Utils._Validate(CaseSensitive, "CaseSensitive", V_BOOLEAN) Then GoTo Finally
+ End If
+
+Try:
+ vFindItem = SF_Array._FindItem(Array_1D, ToFind, CaseSensitive, SortOrder)
+ If vFindItem(0) = True Then lIndex = vFindItem(1) Else lIndex = LBound(Array_1D) - 1
+
+Finally:
+ IndexOf = lIndex
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.IndexOf
+
+REM -----------------------------------------------------------------------------
+Public Function Insert(Optional ByRef Array_1D As Variant _
+ , Optional ByVal Before As Variant _
+ , ParamArray pvArgs() As Variant _
+ ) As Variant
+''' Insert before the index Before of the input array the items listed as arguments
+''' Arguments are inserted blindly
+''' each of them might be a scalar of any type or a subarray
+''' Args
+''' Array_1D: the pre-existing array, may be empty
+''' Before: the index before which to insert; must be in the interval [LBound, UBound + 1]
+''' pvArgs: a list of items to Insert inside Array_1D
+''' Returns:
+''' the new rxtended array. Its LBound is identical to that of Array_1D
+''' Exceptions:
+''' ARRAYINSERTERROR
+''' Examples:
+''' SF_Array.Insert(Array(1, 2, 3), 2, 4, 5) returns (1, 2, 4, 5, 3)
+
+Dim vInsert As Variant ' Return value
+Dim lNbArgs As Long ' Number of elements to Insert
+Dim lMin As Long ' LBound of input array
+Dim lMax As Long ' UBound of input array
+Dim i As Long
+Const cstThisSub = "Array.Insert"
+Const cstSubArgs = "Array_1D, Before, arg0[, arg1] ..."
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vInsert = Array()
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_1D, "Array_1D", 1) Then GoTo Finally
+ If Not SF_Utils._Validate(Before, "Before", V_NUMERIC) Then GoTo Finally
+ If Before < LBound(Array_1D) Or Before > UBound(Array_1D) + 1 Then GoTo CatchArgument
+ End If
+
+Try:
+ lNbArgs = UBound(pvArgs) + 1 ' pvArgs is always zero-based
+ lMin = LBound(Array_1D) ' = LBound(vInsert)
+ lMax = UBound(Array_1D) ' <> UBound(vInsert)
+ If lNbArgs > 0 Then
+ ReDim vInsert(lMin To lMax + lNbArgs)
+ For i = lMin To UBound(vInsert)
+ If i < Before Then
+ vInsert(i) = Array_1D(i)
+ ElseIf i < Before + lNbArgs Then
+ vInsert(i) = pvArgs(i - Before)
+ Else
+ vInsert(i) = Array_1D(i - lNbArgs)
+ End If
+ Next i
+ Else
+ vInsert() = Array_1D()
+ End If
+
+Finally:
+ Insert = vInsert()
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchArgument:
+ 'TODO SF_Exception.RaiseFatal(ARRAYINSERTERROR, cstThisSub)
+ MsgBox "INVALID ARGUMENT VALUE !!"
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.Insert
+
+REM -----------------------------------------------------------------------------
+Public Function InsertSorted(Optional ByRef Array_1D As Variant _
+ , Optional ByVal Item As Variant _
+ , Optional ByVal SortOrder As Variant _
+ , Optional ByVal CaseSensitive As Variant _
+ ) As Variant
+''' Insert in a sorted array a new item on its place
+''' the array must be filled homogeneously, i.e. all items must be of the same type
+''' Empty and Null items are forbidden
+''' Args:
+''' Array_1D: the array to sort
+''' Item: the scalar value to insert, same type as the existing array items
+''' SortOrder: "ASC" (default) or "DESC"
+''' CaseSensitive: Default = False
+''' Returns: the extended sorted array with same LBound as input array
+''' Examples:
+''' InsertSorted(Array("A", "C", "a", "b"), "B", CaseSensitive := True) returns ("A", "B", "C", "a", "b")
+
+Dim vSorted() As Variant ' Return value
+Dim iType As Integer ' VarType of elements in input array
+Dim lMin As Long ' LBound of input array
+Dim lMax As Long ' UBound of input array
+Dim lIndex As Long ' Place where to insert new item
+Const cstThisSub = "Array.InsertSorted"
+Const cstSubArgs = "Array_1D, Item, [SortOrder=""ASC""|""DESC""], [CaseSensitive=False]"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vSorted = Array()
+
+Check:
+ If IsMissing(SortOrder) Or IsEmpty(SortOrder) Then SortOrder = "ASC"
+ If IsMissing(CaseSensitive) Or IsEmpty(CaseSensitive) Then CaseSensitive = False
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_1D, "Array_1D", 1, 0) Then GoTo Finally
+ If LBound(Array_1D) <= UBound(Array_1D) Then
+ iType = SF_Utils._VarTypeExt(Array_1D(LBound(Array_1D)))
+ If Not SF_Utils._Validate(Item, "Item", iType) Then GoTo Finally
+ Else
+ If Not SF_Utils._Validate(Item, "Item", Array(V_STRING, V_DATE, V_NUMERIC)) Then GoTo Finally
+ End If
+ If Not SF_Utils._Validate(SortOrder, "SortOrder", V_STRING, Array("ASC","DESC")) Then GoTo Finally
+ If Not SF_Utils._Validate(CaseSensitive, "CaseSensitive", V_BOOLEAN) Then GoTo Finally
+ End If
+
+Try:
+ lMin = LBound(Array_1D)
+ lMax = UBound(Array_1D)
+ lIndex = SF_Array._FindItem(Array_1D, Item, CaseSensitive, SortOrder)(1)
+ vSorted = SF_Array.Insert(Array_1D, lIndex, Item)
+
+Finally:
+ InsertSorted = vSorted()
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.InsertSorted
+
+REM -----------------------------------------------------------------------------
+Public Function Intersection(Optional ByRef Array1_1D As Variant _
+ , Optional ByRef Array2_1D As Variant _
+ , Optional ByVal CaseSensitive As Variant _
+ ) As Variant
+''' Build a set being the intersection of the two input arrays, i.e. items are contained in both arrays
+''' both input arrays must be filled homogeneously, i.e. all items must be of the same type
+''' Empty and Null items are forbidden
+''' The comparison between strings is case sensitive or not
+''' Args:
+''' Array1_1D: a 1st input array
+''' Array2_1D: a 2nd input array
+''' CaseSensitive: default = False
+''' Returns: a zero-based array containing unique items stored in both input arrays
+''' The output array is sorted in ascending order
+''' Examples:
+''' Intersection(Array("A", "C", "A", "b", "B"), Array("C", "Z", "b"), True) returns ("C", "b")
+
+Dim vIntersection() As Variant ' Return value
+Dim vSorted() As Variant ' The shortest input array after sort
+Dim iType As Integer ' VarType of elements in input arrays
+Dim lMin1 As Long ' LBound of 1st input array
+Dim lMax1 As Long ' UBound of 1st input array
+Dim lMin2 As Long ' LBound of 2nd input array
+Dim lMax2 As Long ' UBound of 2nd input array
+Dim lMin As Long ' LBound of unsorted array
+Dim lMax As Long ' UBound of unsorted array
+Dim iShortest As Integer ' 1 or 2 depending on shortest input array
+Dim lSize As Long ' Number of Intersection items
+Dim vItem As Variant ' One single item in the array
+Dim i As Long
+Const cstThisSub = "Array.Intersection"
+Const cstSubArgs = "Array1_1D, Array2_1D, [CaseSensitive=False]"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vIntersection = Array()
+
+Check:
+ If IsMissing(CaseSensitive) Then CaseSensitive = False
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array1_1D, "Array1_1D", 1, 0, True) Then GoTo Finally
+ iType = SF_Utils._VarTypeExt(Array1_1D(LBound(Array1_1D)))
+ If Not SF_Utils._ValidateArray(Array2_1D, "Array2_1D", 1, iType, True) Then GoTo Finally
+ If Not SF_Utils._Validate(CaseSensitive, "CaseSensitive", V_BOOLEAN) Then GoTo Finally
+ End If
+
+Try:
+ lMin1 = LBound(Array1_1D) : lMax1 = UBound(Array1_1D)
+ lMin2 = LBound(Array2_1D) : lMax2 = UBound(Array2_1D)
+
+ ' If one of both arrays is empty, do nothing
+ If lMax1 >= lMin1 And lMax2 >= lMin2 Then
+
+ ' First sort the shortest array
+ If lMax1 - lMin1 <= lMax2 - lMin2 Then
+ iShortest = 1
+ vSorted = SF_Array.Sort(Array1_1D, "ASC", CaseSensitive)
+ lMin = lMin2 : lMax = lMax2 ' Bounds of unsorted array
+ Else
+ iShortest = 2
+ vSorted = SF_Array.Sort(Array2_1D, "ASC", CaseSensitive)
+ lMin = lMin1 : lMax = lMax1 ' Bounds of unsorted array
+ End If
+
+ ' Resize the output array to the size of the shortest array
+ ReDim vIntersection(0 To (lMax - lMin))
+ lSize = -1
+
+ ' Fill vIntersection one by one only with items present in both sets
+ For i = lMin To lMax
+ If iShortest = 1 Then vItem = Array2_1D(i) Else vItem = Array1_1D(i) ' Pick in unsorted array
+ If SF_Array.Contains(vSorted, vItem, CaseSensitive, "ASC") Then
+ lSize = lSize + 1
+ vIntersection(lSize) = vItem
+ End If
+ Next i
+
+ ' Remove unfilled entries and duplicates
+ If lSize >= 0 Then
+ ReDim Preserve vIntersection(0 To lSize)
+ vIntersection() = SF_Array.Unique(vIntersection, CaseSensitive)
+ Else
+ vIntersection = Array()
+ End If
+ End If
+
+Finally:
+ Intersection = vIntersection()
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.Intersection
+
+REM -----------------------------------------------------------------------------
+Public Function Join2D(Optional ByRef Array_2D As Variant _
+ , Optional ByVal ColumnDelimiter As Variant _
+ , Optional ByVal RowDelimiter As Variant _
+ , Optional ByVal Quote As Variant _
+ ) As String
+''' Join a two-dimensional array with two delimiters, one for columns, one for rows
+''' Args:
+''' Array_2D: each item must be either a String, a number, a Date or a Boolean
+''' ColumnDelimiter: delimits each column (default = Tab/Chr(9))
+''' RowDelimiter: delimits each row (default = LineFeed/Chr(10))
+''' Quote: if True, protect strings with double quotes (default = False)
+''' Return:
+''' A string after conversion of numbers and dates
+''' Invalid items are replaced by a zero-length string
+''' Examples:
+''' | 1, 2, "A", [2020-02-29], 5 |
+''' SF_Array.Join_2D( | 6, 7, "this is a string", 9, 10 | , ",", "/")
+''' ' "1,2,A,2020-02-29 00:00:00,5/6,7,this is a string,9,10"
+
+Dim sJoin As String ' The return value
+Dim sItem As String ' The string representation of a single item
+Dim vItem As Variant ' Single item
+Dim lMin1 As Long ' LBound1 of input array
+Dim lMax1 As Long ' UBound1 of input array
+Dim lMin2 As Long ' LBound2 of input array
+Dim lMax2 As Long ' UBound2 of input array
+Dim i As Long
+Dim j As Long
+Const cstThisSub = "Array.Join2D"
+Const cstSubArgs = "Array_2D, [ColumnDelimiter=Chr(9)], [RowDelimiter=Chr(10)], [Quote=False]"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sJoin = ""
+
+Check:
+ If IsMissing(ColumnDelimiter) Or IsEmpty(ColumnDelimiter) Then ColumnDelimiter = Chr(9)
+ If IsMissing(RowDelimiter) Or IsEmpty(RowDelimiter) Then RowDelimiter = Chr(10)
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_2D, "Array_2D", 2) Then GoTo Finally
+ If Not SF_Utils._Validate(ColumnDelimiter, "ColumnDelimiter", V_STRING) Then GoTo Finally
+ If Not SF_Utils._Validate(RowDelimiter, "RowDelimiter", V_STRING) Then GoTo Finally
+ If Not SF_Utils._Validate(Quote, "Quote", V_BOOLEAN) Then GoTo Finally
+ End If
+
+Try:
+ lMin1 = LBound(Array_2D, 1) : lMax1 = UBound(Array_2D, 1)
+ lMin2 = LBound(Array_2D, 2) : lMax2 = UBound(Array_2D, 2)
+ If lMin1 <= lMax1 Then
+ For i = lMin1 To lMax1
+ For j = lMin2 To lMax2
+ vItem = Array_2D(i, j)
+ Select Case SF_Utils._VarTypeExt(vItem)
+ Case V_STRING : If Quote Then sItem = SF_String.Quote(vItem) Else sItem = vItem
+ Case V_NUMERIC, V_DATE : sItem = SF_Utils._Repr(vItem)
+ Case V_BOOLEAN : sItem = Iif(vItem, "True", "False") 'TODO: L10N
+ Case Else : sItem = ""
+ End Select
+ sJoin = sJoin & sItem & Iif(j < lMax2, ColumnDelimiter, "")
+ Next j
+ sJoin = sJoin & Iif(i < lMax1, RowDelimiter, "")
+ Next i
+ End If
+
+Finally:
+ Join2D = sJoin
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.Join2D
+
+REM -----------------------------------------------------------------------------
+Public Function Methods() As Variant
+''' Return the list of public methods of the Array service as an array
+
+ Methods = Array( _
+ "Append" _
+ , "AppendColumn" _
+ , "AppendRow" _
+ , "Contains" _
+ , "ConvertToDictionary" _
+ , "CountDims" _
+ , "Difference" _
+ , "ExportToTextFile" _
+ , "ExtractColumn" _
+ , "ExtractRow" _
+ , "Flatten" _
+ , "ImportFromCSVFile" _
+ , "IndexOf" _
+ , "Insert" _
+ , "InsertSorted" _
+ , "Intersection" _
+ , "Join2D" _
+ , "Prepend" _
+ , "PrependColumn" _
+ , "PrependRow" _
+ , "RangeInit" _
+ , "Reverse" _
+ , "Shuffle" _
+ , "Sort" _
+ , "SortColumns" _
+ , "SortRows" _
+ , "Transpose" _
+ , "TrimArray" _
+ , "Union" _
+ , "Unique" _
+ )
+
+End Function ' ScriptForge.SF_Array.Methods
+
+REM -----------------------------------------------------------------------------
+Public Function Prepend(Optional ByRef Array_1D As Variant _
+ , ParamArray pvArgs() As Variant _
+ ) As Variant
+''' Prepend at the beginning of the input array the items listed as arguments
+''' Arguments are Prepended blindly
+''' each of them might be a scalar of any type or a subarray
+''' Args
+''' Array_1D: the pre-existing array, may be empty
+''' pvArgs: a list of items to Prepend to Array_1D
+''' Return: the new rxtended array. Its LBound is identical to that of Array_1D
+''' Examples:
+''' SF_Array.Prepend(Array(1, 2, 3), 4, 5) returns (4, 5, 1, 2, 3)
+
+Dim vPrepend As Variant ' Return value
+Dim lNbArgs As Long ' Number of elements to Prepend
+Dim lMin As Long ' LBound of input array
+Dim lMax As Long ' UBound of input array
+Dim i As Long
+Const cstThisSub = "Array.Prepend"
+Const cstSubArgs = "Array_1D, arg0[, arg1] ..."
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vPrepend = Array()
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_1D, "Array_1D", 1) Then GoTo Finally
+ End If
+
+Try:
+ lNbArgs = UBound(pvArgs) + 1 ' pvArgs is always zero-based
+ lMin = LBound(Array_1D) ' = LBound(vPrepend)
+ lMax = UBound(Array_1D) ' <> UBound(vPrepend)
+ If lMax < LBound(Array_1D) And lNbArgs > 0 Then ' Initial array is empty
+ ReDim vPrepend(0 To lNbArgs - 1)
+ Else
+ ReDim vPrepend(lMin To lMax + lNbArgs)
+ End If
+ For i = lMin To UBound(vPrepend)
+ If i < lMin + lNbArgs Then vPrepend(i) = pvArgs(i - lMin) Else vPrepend(i) = Array_1D(i - lNbArgs)
+ Next i
+
+Finally:
+ Prepend = vPrepend
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.Prepend
+
+REM -----------------------------------------------------------------------------
+Public Function PrependColumn(Optional ByRef Array_2D As Variant _
+ , Optional ByRef Column As Variant _
+ ) As Variant
+''' PrependColumn prepends to the left side of a 2D array a new Column
+''' Args
+''' Array_2D: the pre-existing array, may be empty
+''' If the array has 1 dimension, it is considered as the last Column of the resulting 2D array
+''' Column: a 1D array with as many items as there are rows in Array_2D
+''' Returns:
+''' the new rxtended array. Its LBounds are identical to that of Array_2D
+''' Exceptions:
+''' ARRAYINSERTERROR
+''' Examples:
+''' SF_Array.PrependColumn(Array(1, 2, 3), Array(4, 5, 6)) returns ((4, 1), (5, 2), (6, 3))
+''' x = SF_Array.PrependColumn(Array(), Array(1, 2, 3)) => ∀ i ∈ {0 ≤ i ≤ 2} : x(0, i) ≡ i
+
+Dim vPrependColumn As Variant ' Return value
+Dim iDims As Integer ' Dimensions of Array_2D
+Dim lMin1 As Long ' LBound1 of input array
+Dim lMax1 As Long ' UBound1 of input array
+Dim lMin2 As Long ' LBound2 of input array
+Dim lMax2 As Long ' UBound2 of input array
+Dim lMin As Long ' LBound of Column array
+Dim lMax As Long ' UBound of Column array
+Dim i As Long
+Dim j As Long
+Const cstThisSub = "Array.PrependColumn"
+Const cstSubArgs = "Array_2D, Column"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vPrependColumn = Array()
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_2D, "Array_2D") Then GoTo Finally 'Initial check: not missing and array
+ If Not SF_Utils._ValidateArray(Column, "Column", 1) Then GoTo Finally
+ End If
+ iDims = SF_Array.CountDims(Array_2D)
+ If iDims > 2 Then
+ If Not SF_Utils._ValidateArray(Array_2D, "Array_2D", 2) Then GoTo Finally '2nd check to manage error
+ End If
+
+Try:
+ lMin = LBound(Column)
+ lMax = UBound(Column)
+
+ ' Compute future dimensions of output array
+ Select Case iDims
+ Case 0 : lMin1 = lMin : lMax1 = lMax
+ lMin2 = 0 : lMax2 = -1
+ Case 1 : lMin1 = LBound(Array_2D, 1) : lMax1 = UBound(Array_2D, 1)
+ lMin2 = 0 : lMax2 = 0
+ Case 2 : lMin1 = LBound(Array_2D, 1) : lMax1 = UBound(Array_2D, 1)
+ lMin2 = LBound(Array_2D, 2) : lMax2 = UBound(Array_2D, 2)
+ End Select
+ If iDims > 0 And lMax - lMin <> lMax1 - lMin1 Then GoTo CatchColumn
+ ReDim vPrependColumn(lMin1 To lMax1, lMin2 To lMax2 + 1)
+
+ ' Copy input array to output array
+ For i = lMin1 To lMax1
+ For j = lMin2 + 1 To lMax2 + 1
+ If iDims = 2 Then vPrependColumn(i, j) = Array_2D(i, j - 1) Else vPrependColumn(i, j) = Array_2D(i)
+ Next j
+ Next i
+ ' Copy new Column
+ For i = lMin1 To lMax1
+ vPrependColumn(i, lMin2) = Column(i)
+ Next i
+
+Finally:
+ PrependColumn = vPrependColumn()
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchColumn:
+ SF_Exception.RaiseFatal(ARRAYINSERTERROR, "Column", SF_Array._Repr(Array_2D), SF_Utils._Repr(Column, MAXREPR))
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.PrependColumn
+
+REM -----------------------------------------------------------------------------
+Public Function PrependRow(Optional ByRef Array_2D As Variant _
+ , Optional ByRef Row As Variant _
+ ) As Variant
+''' PrependRow prepends on top of a 2D array a new row
+''' Args
+''' Array_2D: the pre-existing array, may be empty
+''' If the array has 1 dimension, it is considered as the last row of the resulting 2D array
+''' Row: a 1D array with as many items as there are columns in Array_2D
+''' Returns:
+''' the new rxtended array. Its LBounds are identical to that of Array_2D
+''' Exceptions:
+''' ARRAYINSERTERROR
+''' Examples:
+''' SF_Array.PrependRow(Array(1, 2, 3), Array(4, 5, 6)) returns ((4, 5, 6), (1, 2, 3))
+''' x = SF_Array.PrependColumn(Array(), Array(1, 2, 3) => ∀ i ∈ {0 ≤ i ≤ 2} : x(i, 0) ≡ i
+
+Dim vPrependRow As Variant ' Return value
+Dim iDims As Integer ' Dimensions of Array_2D
+Dim lMin1 As Long ' LBound1 of input array
+Dim lMax1 As Long ' UBound1 of input array
+Dim lMin2 As Long ' LBound2 of input array
+Dim lMax2 As Long ' UBound2 of input array
+Dim lMin As Long ' LBound of row array
+Dim lMax As Long ' UBound of row array
+Dim i As Long
+Dim j As Long
+Const cstThisSub = "Array.PrependRow"
+Const cstSubArgs = "Array_2D, Row"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vPrependRow = Array()
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_2D, "Array_2D") Then GoTo Finally 'Initial check: not missing and array
+ If Not SF_Utils._ValidateArray(Row, "Row", 1) Then GoTo Finally
+ End If
+ iDims = SF_Array.CountDims(Array_2D)
+ If iDims > 2 Then
+ If Not SF_Utils._ValidateArray(Array_2D, "Array_2D", 2) Then GoTo Finally '2nd check to manage error
+ End If
+
+Try:
+ lMin = LBound(Row)
+ lMax = UBound(Row)
+
+ ' Compute future dimensions of output array
+ Select Case iDims
+ Case 0 : lMin1 = 0 : lMax1 = -1
+ lMin2 = lMin : lMax2 = lMax
+ Case 1 : lMin1 = 0 : lMax1 = 0
+ lMin2 = LBound(Array_2D, 1) : lMax2 = UBound(Array_2D, 1)
+ Case 2 : lMin1 = LBound(Array_2D, 1) : lMax1 = UBound(Array_2D, 1)
+ lMin2 = LBound(Array_2D, 2) : lMax2 = UBound(Array_2D, 2)
+ End Select
+ If iDims > 0 And lMax - lMin <> lMax2 - lMin2 Then GoTo CatchRow
+ ReDim vPrependRow(lMin1 To lMax1 + 1, lMin2 To lMax2)
+
+ ' Copy input array to output array
+ For i = lMin1 + 1 To lMax1 + 1
+ For j = lMin2 To lMax2
+ If iDims = 2 Then vPrependRow(i, j) = Array_2D(i - 1, j) Else vPrependRow(i, j) = Array_2D(j)
+ Next j
+ Next i
+ ' Copy new row
+ For j = lMin2 To lMax2
+ vPrependRow(lMin1, j) = Row(j)
+ Next j
+
+Finally:
+ PrependRow = vPrependRow()
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchRow:
+ SF_Exception.RaiseFatal(ARRAYINSERTERROR, "Row", SF_Array._Repr(Array_2D), SF_Utils._Repr(Row, MAXREPR))
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.PrependRow
+
+REM -----------------------------------------------------------------------------
+Public Function Properties() As Variant
+''' Return the list or properties as an array
+
+ Properties = Array( _
+ )
+
+End Function ' ScriptForge.SF_Array.Properties
+
+REM -----------------------------------------------------------------------------
+Public Function RangeInit(Optional ByVal From As Variant _
+ , Optional ByVal UpTo As Variant _
+ , Optional ByVal ByStep As Variant _
+ ) As Variant
+''' Initialize a new zero-based array with numeric values
+''' Args: all numeric
+''' From: value of first item
+''' UpTo: last item should not exceed UpTo
+''' ByStep: difference between 2 successive items
+''' Return: the new array
+''' Exceptions:
+''' ARRAYSEQUENCEERROR Wrong arguments, f.i. UpTo < From with ByStep > 0
+''' Examples:
+''' SF_Array.RangeInit(10, 1, -1) returns (10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
+
+Dim lIndex As Long ' Index of array
+Dim lSize As Long ' UBound of resulting array
+Dim vCurrentItem As Variant ' Last stored item
+Dim vArray() ' The return value
+Const cstThisSub = "Array.RangeInit"
+Const cstSubArgs = "From, UpTo, [ByStep = 1]"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vArray = Array()
+
+Check:
+ If IsMissing(ByStep) Or IsEmpty(ByStep) Then ByStep = 1
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._Validate(From, "From", V_NUMERIC) Then GoTo Finally
+ If Not SF_Utils._Validate(UpTo, "UpTo", V_NUMERIC) Then GoTo Finally
+ If Not SF_Utils._Validate(ByStep, "ByStep", V_NUMERIC) Then GoTo Finally
+ End If
+ If (From < UpTo And ByStep <= 0) Or (From > UpTo And ByStep >= 0) Then GoTo CatchSequence
+
+Try:
+ lSize = CLng(Abs((UpTo - From) / ByStep))
+ ReDim vArray(0 To lSize)
+ For lIndex = 0 To lSize
+ vArray(lIndex) = From + lIndex * ByStep
+ Next lIndex
+
+Finally:
+ RangeInit = vArray
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchSequence:
+ SF_Exception.RaiseFatal(ARRAYSEQUENCEERROR, From, UpTo, ByStep)
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.RangeInit
+
+REM -----------------------------------------------------------------------------
+Public Function Reverse(Optional ByRef Array_1D As Variant) As Variant
+''' Return the reversed 1D input array
+''' Args:
+''' Array_1D: the array to reverse
+''' Returns: the reversed array
+''' Examples:
+''' SF_Array.Reverse(Array(1, 2, 3, 4)) returns (4, 3, 2, 1)
+
+Dim vReverse() As Variant ' Return value
+Dim lHalf As Long ' Middle of array
+Dim lMin As Long ' LBound of input array
+Dim lMax As Long ' UBound of input array
+Dim i As Long, j As Long
+Const cstThisSub = "Array.Reverse"
+Const cstSubArgs = "Array_1D"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vReverse = Array()
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_1D, "Array_1D", 1) Then GoTo Finally
+ End If
+
+Try:
+ lMin = LBound(Array_1D)
+ lMax = UBound(Array_1D)
+ ReDim vReverse(lMin To lMax)
+ lHalf = Int((lMax + lMin) / 2)
+ j = lMax
+ For i = lMin To lHalf
+ vReverse(i) = Array_1D(j)
+ vReverse(j) = Array_1D(i)
+ j = j - 1
+ Next i
+ ' Odd number of items
+ If IsEmpty(vReverse(lHalf + 1)) Then vReverse(lHalf + 1) = Array_1D(lHalf + 1)
+
+Finally:
+ Reverse = vReverse()
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.Reverse
+
+REM -----------------------------------------------------------------------------
+Public Function SetProperty(Optional ByVal PropertyName As Variant _
+ , Optional ByRef Value As Variant _
+ ) As Boolean
+''' Set a new value to the given property
+''' Args:
+''' PropertyName: the name of the property as a string
+''' Value: its new value
+''' Exceptions
+''' ARGUMENTERROR The property does not exist
+
+Const cstThisSub = "Array.SetProperty"
+Const cstSubArgs = "PropertyName, Value"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ SetProperty = False
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._Validate(PropertyName, "PropertyName", V_STRING, Properties()) Then GoTo Catch
+ End If
+
+Try:
+ Select Case UCase(PropertyName)
+ Case Else
+ End Select
+
+Finally:
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.SetProperty
+
+REM -----------------------------------------------------------------------------
+Public Function Shuffle(Optional ByRef Array_1D As Variant) As Variant
+''' Returns a random permutation of a 1D array
+''' https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
+''' Args:
+''' Array_1D: the array to shuffle
+''' Returns: the shuffled array
+
+Dim vShuffle() As Variant ' Return value
+Dim vSwapValue As Variant ' Intermediate value during swap
+Dim lMin As Long ' LBound of Array_1D
+Dim lCurrentIndex As Long ' Decremented from UBount to LBound
+Dim lRandomIndex As Long ' Random between LBound and lCurrentIndex
+Dim i As Long
+Const cstThisSub = "Array.Shuffle"
+Const cstSubArgs = "Array_1D"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vShuffle = Array()
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_1D, "Array_1D", 1) Then GoTo Finally
+ End If
+
+Try:
+ lMin = LBound(Array_1D)
+ lCurrentIndex = UBound(array_1D)
+ ' Initialize the output array
+ ReDim vShuffle(lMin To lCurrentIndex)
+ For i = lMin To lCurrentIndex
+ vShuffle(i) = Array_1D(i)
+ Next i
+ ' Now ... shuffle !
+ Do While lCurrentIndex > lMin
+ lRandomIndex = Int(Rnd * (lCurrentIndex - lMin)) + lMin
+ vSwapValue = vShuffle(lCurrentIndex)
+ vShuffle(lCurrentIndex) = vShuffle(lRandomIndex)
+ vShuffle(lRandomIndex) = vSwapValue
+ lCurrentIndex = lCurrentIndex - 1
+ Loop
+
+Finally:
+ Shuffle = vShuffle()
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.Shuffle
+
+REM -----------------------------------------------------------------------------
+Public Function Slice(Optional ByRef Array_1D As Variant _
+ , Optional ByVal From As Variant _
+ , Optional ByVal UpTo As Variant _
+ ) As Variant
+''' Returns a subset of a 1D array
+''' Args:
+''' Array_1D: the array to slice
+''' From: the lower index of the subarray to extract (included)
+''' UpTo: the upper index of the subarray to extract (included). Default = the last item of Array_1D
+''' Returns:
+''' The selected subarray with the same LBound as the input array.
+''' If UpTo < From then the returned array is empty
+''' Exceptions:
+''' ARRAYINDEX2ERROR Wrong values for From and/or UpTo
+''' Example:
+''' SF_Array.Slice(Array(1, 2, 3, 4, 5), 1, 3) returns (2, 3, 4)
+
+Dim vSlice() As Variant ' Return value
+Dim lMin As Long ' LBound of Array_1D
+Dim lIndex As Long ' Current index in output array
+Dim i As Long
+Const cstThisSub = "Array.Slice"
+Const cstSubArgs = "Array_1D, From, [UpTo = UBound(Array_1D)]"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vSlice = Array()
+
+Check:
+ If IsMissing(UpTo) Or IsEmpty(UpTo) Then UpTo = -1
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_1D, "Array_1D", 1) Then GoTo Finally
+ If Not SF_Utils._Validate(From, "From", V_NUMERIC) Then GoTo Finally
+ If Not SF_Utils._Validate(UpTo, "UpTo", V_NUMERIC) Then GoTo Finally
+ End If
+ If UpTo = -1 Then UpTo = UBound(Array_1D)
+ If From < LBound(Array_1D) Or From > UBound(Array_1D) _
+ Or From > UpTo Or UpTo > UBound(Array_1D) Then GoTo CatchIndex
+
+Try:
+ If UpTo >= From Then
+ lMin = LBound(Array_1D)
+ ' Initialize the output array
+ ReDim vSlice(lMin To lMin + UpTo - From)
+ lIndex = lMin - 1
+ For i = From To UpTo
+ lIndex = lIndex + 1
+ vSlice(lIndex) = Array_1D(i)
+ Next i
+ End If
+
+Finally:
+ Slice = vSlice()
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchIndex:
+ SF_Exception.RaiseFatal(ARRAYINDEX2ERROR, SF_Array._Repr(Array_1D), From, UpTo)
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.Slice
+
+REM -----------------------------------------------------------------------------
+Public Function Sort(Optional ByRef Array_1D As Variant _
+ , Optional ByVal SortOrder As Variant _
+ , Optional ByVal CaseSensitive As Variant _
+ ) As Variant
+''' Sort a 1D array in ascending or descending order. String comparisons can be case-sensitive or not
+''' Args:
+''' Array_1D: the array to sort
+''' must be filled homogeneously by either strings, dates or numbers
+''' Null and Empty values are allowed
+''' SortOrder: "ASC" (default) or "DESC"
+''' CaseSensitive: Default = False
+''' Returns: the sorted array
+''' Examples:
+''' Sort(Array("a", "A", "b", "B", "C"), CaseSensitive := True) returns ("A", "B", "C", "a", "b")
+
+Dim vSort() As Variant ' Return value
+Dim vIndexes() As Variant ' Indexes of sorted items
+Dim lMin As Long ' LBound of input array
+Dim lMax As Long ' UBound of input array
+Dim i As Long
+Const cstThisSub = "Array.Sort"
+Const cstSubArgs = "Array_1D, [SortOrder=""""|""ASC""|""DESC""], [CaseSensitive=False]"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vSort = Array()
+
+Check:
+ If IsMissing(SortOrder) Or IsEmpty(SortOrder) Then SortOrder = "ASC"
+ If IsMissing(CaseSensitive) Or IsEmpty(CaseSensitive) Then CaseSensitive = False
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_1D, "Array_1D", 1, 0) Then GoTo Finally
+ If Not SF_Utils._Validate(SortOrder, "SortOrder", V_STRING, Array("ASC","DESC")) Then GoTo Finally
+ If Not SF_Utils._Validate(CaseSensitive, "CaseSensitive", V_BOOLEAN) Then GoTo Finally
+ End If
+
+Try:
+ lMin = LBound(Array_1D)
+ lMax = UBound(Array_1D)
+ vIndexes() = SF_Array._HeapSort(Array_1D, ( SortOrder = "ASC" ), CaseSensitive)
+
+ ' Load output array
+ ReDim vSort(lMin To lMax)
+ For i = lMin To lMax
+ vSort(i) = Array_1D(vIndexes(i))
+ Next i
+
+Finally:
+ Sort = vSort()
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.Sort
+
+REM -----------------------------------------------------------------------------
+Public Function SortColumns(Optional ByRef Array_2D As Variant _
+ , Optional ByVal RowIndex As Variant _
+ , Optional ByVal SortOrder As Variant _
+ , Optional ByVal CaseSensitive As Variant _
+ ) As Variant
+''' Returns a permutation of the columns of a 2D array, sorted on the values of a given row
+''' Args:
+''' Array_2D: the input array
+''' RowIndex: the index of the row to sort the columns on
+''' the row must be filled homogeneously by either strings, dates or numbers
+''' Null and Empty values are allowed
+''' SortOrder: "ASC" (default) or "DESC"
+''' CaseSensitive: Default = False
+''' Returns:
+''' the array with permuted columns, LBounds and UBounds are unchanged
+''' Exceptions:
+''' ARRAYINDEXERROR
+''' Examples:
+''' | 5, 7, 3 | | 7, 5, 3 |
+''' SF_Array.SortColumns( | 1, 9, 5 |, 2, "ASC") returns | 9, 1, 5 |
+''' | 6, 1, 8 | | 1, 6, 8 |
+
+Dim vSort() As Variant ' Return value
+Dim vRow() As Variant ' The row on which to sort the array
+Dim vIndexes() As Variant ' Indexes of sorted row
+Dim lMin1 As Long ' LBound1 of input array
+Dim lMax1 As Long ' UBound1 of input array
+Dim lMin2 As Long ' LBound2 of input array
+Dim lMax2 As Long ' UBound2 of input array
+Dim i As Long, j As Long
+Const cstThisSub = "Array.SortColumn"
+Const cstSubArgs = "Array_2D, RowIndex, [SortOrder=""""|""ASC""|""DESC""], [CaseSensitive=False]"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vSort = Array()
+
+Check:
+ If IsMissing(SortOrder) Or IsEmpty(SortOrder) Then SortOrder = "ASC"
+ If IsMissing(CaseSensitive) Or IsEmpty(CaseSensitive) Then CaseSensitive = False
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_2D, "Array_2D", 2) Then GoTo Finally
+ If Not SF_Utils._Validate(RowIndex, "RowIndex", V_NUMERIC) Then GoTo Finally
+ If Not SF_Utils._Validate(SortOrder, "SortOrder", V_STRING, Array("ASC","DESC")) Then GoTo Finally
+ If Not SF_Utils._Validate(CaseSensitive, "CaseSensitive", V_BOOLEAN) Then GoTo Finally
+ End If
+
+Try:
+ lMin1 = LBound(Array_2D, 1) : lMax1 = UBound(Array_2D, 1)
+ If RowIndex < lMin1 Or RowIndex > lMax1 Then GoTo CatchIndex
+ lMin2 = LBound(Array_2D, 2) : lMax2 = UBound(Array_2D, 2)
+
+ ' Extract and sort the RowIndex-th row
+ vRow = SF_Array.ExtractRow(Array_2D, RowIndex)
+ If Not SF_Utils._ValidateArray(vRow, "Row #" & CStr(RowIndex), 1, 0) Then GoTo Finally
+ vIndexes() = SF_Array._HeapSort(vRow, ( SortOrder = "ASC" ), CaseSensitive)
+
+ ' Load output array
+ ReDim vSort(lMin1 To lMax1, lMin2 To lMax2)
+ For i = lMin1 To lMax1
+ For j = lMin2 To lMax2
+ vSort(i, j) = Array_2D(i, vIndexes(j))
+ Next j
+ Next i
+
+Finally:
+ SortColumns = vSort()
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchIndex:
+ 'TODO SF_Exception.RaiseFatal(ARRAYINDEXERROR, cstThisSub)
+ MsgBox "INVALID INDEX VALUE !!"
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.SortColumns
+
+REM -----------------------------------------------------------------------------
+Public Function SortRows(Optional ByRef Array_2D As Variant _
+ , Optional ByVal ColumnIndex As Variant _
+ , Optional ByVal SortOrder As Variant _
+ , Optional ByVal CaseSensitive As Variant _
+ ) As Variant
+''' Returns a permutation of the rows of a 2D array, sorted on the values of a given column
+''' Args:
+''' Array_2D: the input array
+''' ColumnIndex: the index of the column to sort the rows on
+''' the column must be filled homogeneously by either strings, dates or numbers
+''' Null and Empty values are allowed
+''' SortOrder: "ASC" (default) or "DESC"
+''' CaseSensitive: Default = False
+''' Returns:
+''' the array with permuted Rows, LBounds and UBounds are unchanged
+''' Exceptions:
+''' ARRAYINDEXERROR
+''' Examples:
+''' | 5, 7, 3 | | 1, 9, 5 |
+''' SF_Array.SortRows( | 1, 9, 5 |, 0, "ASC") returns | 5, 7, 3 |
+''' | 6, 1, 8 | | 6, 1, 8 |
+
+Dim vSort() As Variant ' Return value
+Dim vCol() As Variant ' The column on which to sort the array
+Dim vIndexes() As Variant ' Indexes of sorted row
+Dim lMin1 As Long ' LBound1 of input array
+Dim lMax1 As Long ' UBound1 of input array
+Dim lMin2 As Long ' LBound2 of input array
+Dim lMax2 As Long ' UBound2 of input array
+Dim i As Long, j As Long
+Const cstThisSub = "Array.SortRow"
+Const cstSubArgs = "Array_2D, ColumnIndex, [SortOrder=""""|""ASC""|""DESC""], [CaseSensitive=False]"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vSort = Array()
+
+Check:
+ If IsMissing(SortOrder) Or IsEmpty(SortOrder) Then SortOrder = "ASC"
+ If IsMissing(CaseSensitive) Or IsEmpty(CaseSensitive) Then CaseSensitive = False
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_2D, "Array_2D", 2) Then GoTo Finally
+ If Not SF_Utils._Validate(ColumnIndex, "ColumnIndex", V_NUMERIC) Then GoTo Finally
+ If Not SF_Utils._Validate(SortOrder, "SortOrder", V_STRING, Array("ASC","DESC")) Then GoTo Finally
+ If Not SF_Utils._Validate(CaseSensitive, "CaseSensitive", V_BOOLEAN) Then GoTo Finally
+ End If
+
+Try:
+ lMin2 = LBound(Array_2D, 2) : lMax2 = UBound(Array_2D, 2)
+ If ColumnIndex < lMin2 Or ColumnIndex > lMax2 Then GoTo CatchIndex
+ lMin1 = LBound(Array_2D, 1) : lMax1 = UBound(Array_2D, 1)
+
+ ' Extract and sort the ColumnIndex-th column
+ vCol = SF_Array.ExtractColumn(Array_2D, ColumnIndex)
+ If Not SF_Utils._ValidateArray(vCol, "Column #" & CStr(ColumnIndex), 1, 0) Then GoTo Finally
+ vIndexes() = SF_Array._HeapSort(vCol, ( SortOrder = "ASC" ), CaseSensitive)
+
+ ' Load output array
+ ReDim vSort(lMin1 To lMax1, lMin2 To lMax2)
+ For i = lMin1 To lMax1
+ For j = lMin2 To lMax2
+ vSort(i, j) = Array_2D(vIndexes(i), j)
+ Next j
+ Next i
+
+Finally:
+ SortRows = vSort()
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchIndex:
+ 'TODO SF_Exception.RaiseFatal(ARRAYINDEXERROR, cstThisSub)
+ MsgBox "INVALID INDEX VALUE !!"
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.SortRows
+
+REM -----------------------------------------------------------------------------
+Public Function Transpose(Optional ByRef Array_2D As Variant) As Variant
+''' Swaps rows and columns in a 2D array
+''' Args:
+''' Array_2D: the array to transpose
+''' Returns:
+''' The transposed array
+''' Examples:
+''' | 1, 2 | | 1, 3, 5 |
+''' SF_Array.Transpose( | 3, 4 | ) returns | 2, 4, 6 |
+''' | 5, 6 |
+
+Dim vTranspose As Variant ' Return value
+Dim lIndex As Long ' vTranspose index
+Dim lMin1 As Long ' LBound1 of input array
+Dim lMax1 As Long ' UBound1 of input array
+Dim lMin2 As Long ' LBound2 of input array
+Dim lMax2 As Long ' UBound2 of input array
+Dim i As Long, j As Long
+Const cstThisSub = "Array.Transpose"
+Const cstSubArgs = "Array_2D"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vTranspose = Array()
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_2D, "Array_2D", 2) Then GoTo Finally
+ End If
+
+Try:
+ ' Resize the output array
+ lMin1 = LBound(Array_2D, 1) : lMax1 = UBound(Array_2D, 1)
+ lMin2 = LBound(Array_2D, 2) : lMax2 = UBound(Array_2D, 2)
+ If lMin1 <= lMax1 Then
+ ReDim vTranspose(lMin2 To lMax2, lMin1 To lMax1)
+ End If
+
+ ' Transpose items
+ For i = lMin1 To lMax1
+ For j = lMin2 To lMax2
+ vTranspose(j, i) = Array_2D(i, j)
+ Next j
+ Next i
+
+Finally:
+ Transpose = vTranspose
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.Transpose
+
+REM -----------------------------------------------------------------------------
+Public Function TrimArray(Optional ByRef Array_1D As Variant) As Variant
+''' Remove from a 1D array all Null, Empty and zero-length entries
+''' Strings are trimmed as well
+''' Args:
+''' Array_1D: the array to scan
+''' Return: The trimmed array
+''' Examples:
+''' SF_Array.TrimArray(Array("A","B",Null," D ")) returns ("A","B","D")
+
+Dim vTrimArray As Variant ' Return value
+Dim lIndex As Long ' vTrimArray index
+Dim lMin As Long ' LBound of input array
+Dim lMax As Long ' UBound of input array
+Dim vItem As Variant ' Single array item
+Dim i As Long
+Const cstThisSub = "Array.TrimArray"
+Const cstSubArgs = "Array_1D"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vTrimArray = Array()
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_1D, "Array_1D", 1) Then GoTo Finally
+ End If
+
+Try:
+ lMin = LBound(Array_1D)
+ lMax = UBound(Array_1D)
+ If lMin <= lMax Then
+ ReDim vTrimArray(lMin To lMax)
+ End If
+ lIndex = lMin - 1
+
+ ' Load only valid items from Array_1D to vTrimArray
+ For i = lMin To lMax
+ vItem = Array_1D(i)
+ Select Case VarType(vItem)
+ Case V_EMPTY
+ Case V_NULL : vItem = Empty
+ Case V_STRING
+ vItem = Trim(vItem)
+ If Len(vItem) = 0 Then vItem = Empty
+ Case Else
+ End Select
+ If Not IsEmpty(vItem) Then
+ lIndex = lIndex + 1
+ vTrimArray(lIndex) = vItem
+ End If
+ Next i
+
+ 'Keep valid entries
+ If lMin <= lIndex Then
+ ReDim Preserve vTrimArray(lMin To lIndex)
+ Else
+ vTrimArray = Array()
+ End If
+
+Finally:
+ TrimArray = vTrimArray
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.TrimArray
+
+REM -----------------------------------------------------------------------------
+Public Function Union(Optional ByRef Array1_1D As Variant _
+ , Optional ByRef Array2_1D As Variant _
+ , Optional ByVal CaseSensitive As Variant _
+ ) As Variant
+''' Build a set being the Union of the two input arrays, i.e. items are contained in any of both arrays
+''' both input arrays must be filled homogeneously, i.e. all items must be of the same type
+''' Empty and Null items are forbidden
+''' The comparison between strings is case sensitive or not
+''' Args:
+''' Array1_1D: a 1st input array
+''' Array2_1D: a 2nd input array
+''' CaseSensitive: default = False
+''' Returns: a zero-based array containing unique items stored in any of both input arrays
+''' The output array is sorted in ascending order
+''' Examples:
+''' SF_Array.Union(Array("A", "C", "A", "b", "B"), Array("C", "Z", "b"), True) returns ("A", "B", "C", "Z", "b")
+
+Dim vUnion() As Variant ' Return value
+Dim iType As Integer ' VarType of elements in input arrays
+Dim lMin1 As Long ' LBound of 1st input array
+Dim lMax1 As Long ' UBound of 1st input array
+Dim lMin2 As Long ' LBound of 2nd input array
+Dim lMax2 As Long ' UBound of 2nd input array
+Dim lSize As Long ' Number of Union items
+Dim i As Long
+Const cstThisSub = "Array.Union"
+Const cstSubArgs = "Array1_1D, Array2_1D, [CaseSensitive=False]"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vUnion = Array()
+
+Check:
+ If IsMissing(CaseSensitive) Then CaseSensitive = False
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array1_1D, "Array1_1D", 1, 0, True) Then GoTo Finally
+ iType = SF_Utils._VarTypeExt(Array1_1D(LBound(Array1_1D)))
+ If Not SF_Utils._ValidateArray(Array2_1D, "Array2_1D", 1, iType, True) Then GoTo Finally
+ If Not SF_Utils._Validate(CaseSensitive, "CaseSensitive", V_BOOLEAN) Then GoTo Finally
+ End If
+
+Try:
+ lMin1 = LBound(Array1_1D) : lMax1 = UBound(Array1_1D)
+ lMin2 = LBound(Array2_1D) : lMax2 = UBound(Array2_1D)
+
+ ' If both arrays are empty, do nothing
+ If lMax1 < lMin1 And lMax2 < lMin2 Then
+ ElseIf lMax1 < lMin1 Then ' only 1st array is empty
+ vUnion = SF_Array.Unique(Array2_1D, CaseSensitive)
+ ElseIf lMax2 < lMin2 Then ' only 2nd array is empty
+ vUnion = SF_Array.Unique(Array1_1D, CaseSensitive)
+ Else
+
+ ' Build union of both arrays
+ ReDim vUnion(0 To (lMax1 - lMin1) + (lMax2 - lMin2) + 1)
+ lSize = -1
+
+ ' Fill vUnion one by one only with items present in any set
+ For i = lMin1 To lMax1
+ lSize = lSize + 1
+ vUnion(lSize) = Array1_1D(i)
+ Next i
+ For i = lMin2 To lMax2
+ lSize = lSize + 1
+ vUnion(lSize) = Array2_1D(i)
+ Next i
+
+ ' Remove duplicates
+ vUnion() = SF_Array.Unique(vUnion, CaseSensitive)
+ End If
+
+Finally:
+ Union = vUnion()
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.Union
+
+REM -----------------------------------------------------------------------------
+Public Function Unique(Optional ByRef Array_1D As Variant _
+ , Optional ByVal CaseSensitive As Variant _
+ ) As Variant
+''' Build a set of unique values derived from the input array
+''' the input array must be filled homogeneously, i.e. all items must be of the same type
+''' Empty and Null items are forbidden
+''' The comparison between strings is case sensitive or not
+''' Args:
+''' Array_1D: the input array with potential duplicates
+''' CaseSensitive: default = False
+''' Returns: the array without duplicates with same LBound as input array
+''' The output array is sorted in ascending order
+''' Examples:
+''' Unique(Array("A", "C", "A", "b", "B"), True) returns ("A", "B", "C", "b")
+
+Dim vUnique() As Variant ' Return value
+Dim vSorted() As Variant ' The input array after sort
+Dim lMin As Long ' LBound of input array
+Dim lMax As Long ' UBound of input array
+Dim lUnique As Long ' Number of unique items
+Dim vIndex As Variant ' Output of _FindItem() method
+Dim vItem As Variant ' One single item in the array
+Dim i As Long
+Const cstThisSub = "Array.Unique"
+Const cstSubArgs = "Array_1D, [CaseSensitive=False]"
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vUnique = Array()
+
+Check:
+ If IsMissing(CaseSensitive) Then CaseSensitive = False
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._ValidateArray(Array_1D, "Array_1D", 1, 0, True) Then GoTo Finally
+ If Not SF_Utils._Validate(CaseSensitive, "CaseSensitive", V_BOOLEAN) Then GoTo Finally
+ End If
+
+Try:
+ lMin = LBound(Array_1D)
+ lMax = UBound(Array_1D)
+ If lMax >= lMin Then
+ ' First sort the array
+ vSorted = SF_Array.Sort(Array_1D, "ASC", CaseSensitive)
+ ReDim vUnique(lMin To lMax)
+ lUnique = lMin
+ ' Fill vUnique one by one ignoring duplicates
+ For i = lMin To lMax
+ vItem = vSorted(i)
+ If i = lMin Then
+ vUnique(i) = vItem
+ Else
+ If SF_Array._ValCompare(vItem, vSorted(i - 1), CaseSensitive) = 0 Then ' Ignore item
+ Else
+ lUnique = lUnique + 1
+ vUnique(lUnique) = vItem
+ End If
+ End If
+ Next i
+ ' Remove unfilled entries
+ ReDim Preserve vUnique(lMin To lUnique)
+ End If
+
+Finally:
+ Unique = vUnique()
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function ' ScriptForge.SF_Array.Unique
+
+REM ============================================================= PRIVATE METHODS
+
+REM -----------------------------------------------------------------------------
+Public Function _FindItem(ByRef pvArray_1D As Variant _
+ , ByVal pvToFind As Variant _
+ , ByVal pbCaseSensitive As Boolean _
+ , ByVal psSortOrder As String _
+ ) As Variant
+''' Check if a 1D array contains the ToFind number, string or date and return its index
+''' The comparison between strings can be done case-sensitively or not
+''' If the array is sorted then a binary search is done
+''' Otherwise the array is scanned from top. Null or Empty items are simply ignored
+''' Args:
+''' pvArray_1D: the array to scan
+''' pvToFind: a number, a date or a string to find
+''' pbCaseSensitive: Only for string comparisons, default = False
+''' psSortOrder: "ASC", "DESC" or "" (= not sorted, default)
+''' Return: a (0:1) array
+''' (0) = True when found
+''' (1) = if found: index of item
+''' if not found: if sorted, index of next item in the array (might be = UBound + 1)
+''' if not sorted, meaningless
+''' Result is unpredictable when array is announced sorted and is in reality not
+''' Called by Contains, IndexOf and InsertSorted. Also called by SF_Dictionary
+
+Dim bContains As Boolean ' True if match found
+Dim iToFindType As Integer ' VarType of pvToFind
+Dim lTop As Long, lBottom As Long ' Interval in scope of binary search
+Dim lIndex As Long ' Index used in search
+Dim iCompare As Integer ' Output of _ValCompare function
+Dim lLoops As Long ' Count binary searches
+Dim lMaxLoops As Long ' Max number of loops during binary search: to avoid infinite loops if array not sorted
+Dim vFound(1) As Variant ' Returned array (Contains, Index)
+
+ bContains = False
+
+ If LBound(pvArray_1D) > UBound(pvArray_1D) Then ' Empty array, do nothing
+ Else
+ ' Search sequentially
+ If Len(psSortOrder) = 0 Then
+ For lIndex = LBound(pvArray_1D) To UBound(pvArray_1D)
+ bContains = ( SF_Array._ValCompare(pvToFind, pvArray_1D(lIndex), pbCaseSensitive) = 0 )
+ If bContains Then Exit For
+ Next lIndex
+ Else
+ ' Binary search
+ If psSortOrder = "ASC" Then
+ lTop = UBound(pvArray_1D)
+ lBottom = lBound(pvArray_1D)
+ Else
+ lBottom = UBound(pvArray_1D)
+ lTop = lBound(pvArray_1D)
+ End If
+ lLoops = 0
+ lMaxLoops = CLng((Log(UBound(pvArray_1D) - LBound(pvArray_1D) + 1.0) / Log(2.0))) + 1
+ Do
+ lLoops = lLoops + 1
+ lIndex = (lTop + lBottom) / 2
... etc. - the rest is truncated
More information about the Libreoffice-commits
mailing list