Jump to content

_ArraySort() & _ArrayMultiSort() with case sensitivity.


Malkey
 Share

Recommended Posts

The _ArraySortc() function
_ArraySortC(ByRef $aArray, $iDescending = 0, $iStart = 0, $iEnd = 0, $iSubItem = 0, $iPivot = 0, $iCase = 0, $iNumericalSort = 0)
The _ArraySort() function from AutoIt's Array.au3 UDF include file is downwardly compatible with the _ArraySortc() function which is in the updated ArrayMultiSortCase.au3 UDF of post #1. In fact, the _ArraySortc() function is a modified version of the _ArraySort() function that gives access to optional numeric sorting and\or case sensitive sorting if needed.

The _ArraySortInOrder() function
_ArraySortInOrder(ByRef $array, $sSortingOrder, $iDescending = 0, $iStartRow = 0, $iEndRow = 0, $iSubItem = 0, $iPivot = 0, $iCase = 0, $iNumericalSort = 0)
The _ArraySortInOrder() function is an updated, better version from here.    The update fixes a bug when zero is sorted and is made similar to (and uses) the new _ArraySortc() function. The added $sSortingOrder parameter in _ArraySortInOrder() is the only additional difference when compared to the _ArraySortc() function's parameters.  The $sSortingOrder parameter contains the sorting string. The ascending order is the individual characters from left to right in the sorting order string.   Descending order is the $sSortingOrder string from right to left.

The _ArrayMultiSort() function
_ArrayMultiSort(ByRef $aArray, $sSort = '0a', $iStartRow = 0, $iEndRow = 0)
_ArraySortc() and _ArraySortInOrder() are the two sorting functions used in the _ArrayMultiSort() function. 
The $sSort parameter is a string that contains comma separated column descriptors.  The sorting order of the columns is the numeric value in each column descriptor from left to right. For other characters in each column descriptor see _ArrayMultiSort() function's header in the ArrayMultiSortCase.au3 UDF, and comments in the examples. 


 Here is the UDF containing the functions used to sort arrays with case sensitivity, forced numeric sorting, and specified ordered sorting.
ArrayMultiSortCase.au3

#include-once
; https://www.autoitscript.com/forum/topic/198148-_arraymultisort-sort-multiple-columns-with-case-sensitivity/
; File Name:  #include <ArrayMultiSortCase.au3>

;#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w- 4 -w 5 -w 6 -w- 7

; #INCLUDES# =========================================================================================================
#include <Array.au3>
; ===============================================================================================================================

; #FUNCTION# ====================================================================================================================
; Name...........: _ArrayMultiSort
; Description ...: Sort a 1D or, sort a 2D array with the ability to specify which columns to sort and which direction to sort for each column.
; Syntax.........: _ArrayMultiSort(ByRef $avArray[, $sSort = '0a'[, $iStartRow = 0[, $iEndRow = 0]]])
; Parameters ....: $avArray  - Array to sort
;                  $sSort    - [optional] The default setting only sorts the first column, column 0, in ascending order.  Otherwise, $sSort is
;                              a string made up of comma separated column descriptors which contains a column number, sort direction, case
;                              sensitivity, and make string numbers numeric, for that column.  Each column descriptor shows the sort order of
;                              columns of the array from left to right.
;                              These 4 fields can be in any order in the column descriptor:-
;                              1/ "0" or "1" etc - The column number(0 1 2 ...) -  Col#0 is the first column - zero-based;
;                              2/ "a" - Ascending order  (Without "a" or "d" default is "a"); or,
;                                 "d" - Descending order; And,
;                              3/ "c" - sort that column with case sensitivity   - Ascending (0-9,A-Z,a-z,Ordered sorting) or,
;                                  Without "c" present, that column is sorted with case insensitivity (default).
;                                                Sorting with case insensitivity - Ascending (Ordered sorting,9-0,Aa-Zz)
;                              4/ "n" - Force sorting all digits present in that column numerically.
;                                   Without "n" and digits that are stored as a string will be sorted alphabetically.
;                                   Digits stored as numbers will automatically be sorted numerically in that column.
;                              5/ "(Characters used in sorting order)" - A string of characters contained within brckets that give the
;                                                                        ascending sorting order
;                              Within the column descriptors, spaces are optional; and,
;                              Upper or lower case column descriptors, "CAD" or "cad", are recognized as the same - use either (except within the sorting order string).
;                              Examples of the $sSort variable:-
;                              "3 ac, ND2, 0n(bac), d1" -> First sort col#3 (case sensitive, ascending, digits as strings sorted alphabetically[default]).
;                                                          Secondly sort col#2 (case insensitive, descending, digits sorted numerically.).
;                                                          Thirdly sort col#0 (case insensitive[default], ascending[default], digits sorted numerically,
;                                                                  sort the characters between the brackets, "bac" in this column in the order "b then "a" then "c").
;                                                          Lastly sort col#1 (case insensitive[default], descending, digits as strings
;                                                                 sorted alphabetically[default]).
;                              "0"   or   ""            -> Sort col#0 (case insensitive[default], ascending[default], digits as strings
;                                                                 sorted alphabetically[default])
;                               See https://www.autoitscript.com/forum/topic/198148-_arraysort-_arraymultisort-with-case-sensitivity/?do=findComment&comment=1422656
;                 $iStartRow - [optional] Row index of array to start sorting at.
;                 $iEndRow   - [optional] Row index of array to stop sorting at.
; Return values .: Success - 1
;                  Failure - 0, @error set to the same as returned from _ArraySort() function. (See _ArraySort in AutoIt help file)
; Author ........: Malkey
; Modified.......:
; Remarks .......: Updated version from https://www.autoitscript.com/forum/topic/166426-_filelisttoarray-sorting/?do=findComment&comment=1216234
;                  This function requires the __TestExp function, and #Include <Array.au3> and the case sensitive versions of _ArraySort,
;                  __ArrayQuickSort1D,  and __ArrayQuickSort2D.
; Related .......:
; Link ..........:
; Example .......: Yes
; ===============================================================================================================================
Func _ArrayMultiSort(ByRef $aArray, $sSort = '0a', $iStartRow = 0, $iEndRow = 0)
    Local $iCol1, $Dir, $iLastRow, $iStart, $iRetV, $iEnd, $Err, $aSort2D[0][3]

    ;------------ $sSort (string) to $aSort2D (array) -----------
    Local $aTempSort = StringSplit($sSort, ",", 2) ;   $STR_NOCOUNT (2)
    Local $aSort2D[UBound($aTempSort)][5]
    For $i = 0 To UBound($aTempSort) - 1
        $aSort2D[$i][4] = StringRegExp($aTempSort[$i], "\(.+?\)") ? StringRegExpReplace($aTempSort[$i], "^.*\(([^\)]+?)\).*$", "$1") : 0 ;  Sorting to order
        $aTempSort[$i] = StringRegExpReplace($aTempSort[$i], "(\([^\)]+?\))", "")
        $aSort2D[$i][0] = StringInStr($aTempSort[$i], "c") ? 1 : 0 ;  Case sensitivity
        $aSort2D[$i][1] = StringRegExp($aTempSort[$i], "\d+") ? StringRegExp($aTempSort[$i], "\d+", 1)[0] : 0 ; Column number
        $aSort2D[$i][2] = StringInStr($aTempSort[$i], "d") ? 1 : 0 ;  Ascending/Decending sort order
        $aSort2D[$i][3] = StringInStr($aTempSort[$i], "n") ? 1 : 0 ;  Numeric sorting
    Next
    ;_ArrayDisplay($aSort2D, "New method", "", 0, Default, "Case|Col#|Asc/Desc|Numeric|Sorting Order")
    ; ------------------------------------------------------------

    If $iEndRow = 0 Or ($iEndRow > (UBound($aArray) - 1)) Then $iLastRow = UBound($aArray) - 1
    $iStart = -1
    $iEnd = -1

    ; Sort the first column to be sorted.
    If IsString($aSort2D[0][4]) Then
        _ArraySortInOrder($aArray, $aSort2D[0][4], $aSort2D[0][2], $iStartRow, $iLastRow, Number($aSort2D[0][1]), 0, $aSort2D[0][0], $aSort2D[0][3])
    Else
        _ArraySortC($aArray, $aSort2D[0][2], $iStartRow, $iLastRow, Number($aSort2D[0][1]), 0, $aSort2D[0][0], $aSort2D[0][3]) ; Sort on the first column (stored in $aSort2D[0][1])
    EndIf

    ; Sort all remaining columns.
    For $j = 1 To UBound($aSort2D) - 1

        ; For each group of equal values in the previous sorted column, sort the present column values on the same rows of each group.
        $iStart = -1
        For $i = $iStartRow To $iLastRow
            Switch $i
                Case $iStartRow
                    If $i <> $iLastRow Then
                        If __TestExp($aArray, $aSort2D, $i, $j) Then
                            $iStart = $i + 1
                            $iEnd = $i + 1
                        Else
                            $iStart = $i
                            $iEnd = $i + 1
                        EndIf
                    EndIf
                Case $iLastRow
                    $iEnd = $iLastRow
                    If $iStart <> $iEnd Then
                        If IsString($aSort2D[$j][4]) Then
                            _ArraySortInOrder($aArray, $aSort2D[$j][4], $aSort2D[$j][2], $iStart, $iEnd, Number($aSort2D[$j][1]), 0, $aSort2D[$j][0], $aSort2D[$j][3])
                        Else
                            _ArraySortC($aArray, $aSort2D[$j][2], $iStart, $iEnd, Number($aSort2D[$j][1]), 0, $aSort2D[$j][0], $aSort2D[$j][3])
                        EndIf
                    EndIf
                Case Else
                    If __TestExp($aArray, $aSort2D, $i, $j) Then
                        $iEnd = $i
                        If $iStart <> $iEnd Then
                            If IsString($aSort2D[$j][4]) Then
                                _ArraySortInOrder($aArray, $aSort2D[$j][4], $aSort2D[$j][2], $iStart, $iEnd, Number($aSort2D[$j][1]), 0, $aSort2D[$j][0], $aSort2D[$j][3])
                            Else
                                _ArraySortC($aArray, $aSort2D[$j][2], $iStart, $iEnd, Number($aSort2D[$j][1]), 0, $aSort2D[$j][0], $aSort2D[$j][3])
                            EndIf
                        EndIf
                        $iStart = $i + 1
                        $iEnd = $iStart
                    Else
                        $iEnd = $i
                    EndIf
            EndSwitch
        Next
    Next
    Return SetError($Err, 0, $iRetV)
EndFunc   ;==>_ArrayMultiSort

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name...........: __TestExp
; Description ...: Creates and tests a test expression.  Internally used function by the _ArrayMultiSort() function
; Syntax.........: __TestExp($aA, $aC, $k, $m)
; Parameters ....: $aA - The 2D array to be sorted.
;                  $aC - A 2D array containing to column sorting order and each column's direction of sorting.(See Modified note)
;                  $k  - The array's row index of the data being sorted.
;                  $m  - The array's row index of the current column being sorted
; Return values .: 1 - When any of the created test expressions are true; or,
;                  0 - When all of the created test expressions are false.
; Author ........: Malkey
; Modified.......: Malkey 2019/3/9 Array $aC is now [n][3] where [n][0] is case sensitivity;  [n][1] is Column#;  [n][2] is sort asc or desc.
; Remarks .......: This function is used internally by the _ArrayMultiSort function. The created expression tests the particular
;                 row, $k, of the test data, for each previously sorted column, $m -1, wheather the value in that column, $aA[$k][$aC[$w][1]],
;                 is not equal to the next value in that same column, $aA[$k + 1][$aC[$w][1]], where $aC[$w][1] contains the previously sorted column number.
; ===============================================================================================================================
Func __TestExp(ByRef $aA, ByRef $aC, $k, $m)
    For $w = 0 To $m - 1
        If ($aA[$k + 1][$aC[$w][1]]) <> ($aA[$k][$aC[$w][1]]) Then ; Like an "Or" join statements. If just one of the expressions in the For-Next loop is true, then function returns "1" (True).
            Return 1
        EndIf
    Next
    Return 0
EndFunc   ;==>__TestExp

; Description - This function sorts a 1D or 2D array, $array, in the order of the characters listed in a given string, $sSortingOrder.
; Parameters:
;       $array - A 1D or 2D array to be sorted.
;       $sSortingOrder - A string containing characters giving the ascending sorting order (left to right) of the same characters which appear in the array.
;       $iDescending, $iStartRow,  $iEndRow, $iSubItem, $iPivot - are the same parameters as in _ArraySort(), AutoIt's UDF function.
;       $iCase          - 0 (default) Upper and lower case are treated as the same.
;                       - 1 Upper and lower case are sorted separately.
;       $iNumericalSort - 0 (default) If the number elements of an array are strings, these numbers are sorted alphabetically (lexicographically).
;                           Where one one, "11", is less than two, "2".
;                       - 1 Sorted in numerical order. Where two, 2, is less than eleven, 11.
; Requirement: #include <Array.au3>
; Returns:  A sorted array.
; Remarks: Updated version from https://www.autoitscript.com/forum/topic/129312-an-array-or-string-sort-in-a-specific-order/
;
Func _ArraySortInOrder(ByRef $array, $sSortingOrder, $iDescending = 0, $iStartRow = 0, $iEndRow = 0, $iSubItem = 0, $iPivot = 0, $iCase = 0, $iNumericalSort = 0)
    Local $aOrder = StringSplit($sSortingOrder, "", 2)
    If $iEndRow = 0 Then $iEndRow = UBound($array) - 1
    Switch UBound($array, 0)
        Case 1
            ; --- Convert all characters in each element of $array, into an padded index number of the same character of the $sSortingOrder array, $aOrder. ---
            For $i = $iStartRow To $iEndRow
                $aItem = StringSplit($array[$i], "", 2)
                For $j = 0 To UBound($aItem) - 1
                    $iIndex = StringInStr($sSortingOrder, $aItem[$j], 1)
                    If $iIndex Then $array[$i] = StringRegExpReplace($array[$i], "(?<!~)\Q" & $aItem[$j] & "\E(?!#>)", "~" & StringRight("00" & ($iIndex - 1), 3) & "#>")
                 Next
            Next

            ; ------- Sort modified array, $array. ----------
            _ArraySortC($array, $iDescending, $iStartRow, $iEndRow, $iSubItem, $iPivot, $iCase, $iNumericalSort)
            ;_ArrayDisplay($array, "Sorted 1D Array")

            ; ------- Convert back to original characters --------
            For $i = $iStartRow To $iEndRow
                $array[$i] = Execute('"' & StringRegExpReplace($array[$i], "~(\d{3})#>", '"&$aOrder[$1]&"') & '"')
            Next

        Case 2
            ; --- Convert all characters in each element of the selected column of the $array, into an padded index number of the same character of the $sSortingOrder array, $aOrder. ---
            For $i = $iStartRow To $iEndRow
                $aItem = StringSplit($array[$i][$iSubItem], "", 2)
                For $j = 0 To UBound($aItem) - 1
                    $iIndex = StringInStr($sSortingOrder, $aItem[$j], 1)
                    If $iIndex Then $array[$i][$iSubItem] = StringRegExpReplace($array[$i][$iSubItem], "(?<!~)\Q" & $aItem[$j] & "\E(?!\d{0,2}#>)", "~" & StringRight("00" & ($iIndex - 1), 3) & "#>")
                Next
            Next

            ; ------- Sort modified array, $array on selected column. ----------
            _ArraySortC($array, $iDescending, $iStartRow, $iEndRow, $iSubItem, $iPivot, $iCase, $iNumericalSort)
            ;_ArrayDisplay($array, "Sorted 2D Array")

            ; ------- Convert back to original characters --------
            For $i = $iStartRow To $iEndRow
                $array[$i][2] = Execute('"' & StringRegExpReplace($array[$i][2], "~(\d{3})#>", '"&$aOrder[$1]&"') & '"')
            Next
    EndSwitch
EndFunc   ;==>_ArraySortInOrder


; xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
; ===============================================================================================
;  The following three function,
; _ArraySortC, __ArrayQuickSort1DC, and __ArrayQuickSort2DC are modied copies of the functions,
; _ArraySort,  __ArrayQuickSort1D,  and __ArrayQuickSort2D from the Array.au3 include file.
; The modifications allow optiomal case sensitive sorting and optional numeric sorting of arrays.
; _ArraySortC is compatiable with _ArraySort when not using the optional parameters for case
;  sensitivity and numeric sorting in the _ArraySortC function.
; ===============================================================================================
; xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

; #FUNCTION# ====================================================================================================================
; Author ........: Jos
; Modified.......: LazyCoder - added $iSubItem option; Tylo - implemented stable QuickSort algo; Jos - changed logic to correctly Sort arrays with mixed Values and Strings; Melba23 - implemented stable pivot algo; Malkey - added optional case sensitive & optional numeric sorting.
; ===============================================================================================================================
Func _ArraySortC(ByRef $aArray, $iDescending = 0, $iStart = 0, $iEnd = 0, $iSubItem = 0, $iPivot = 0, $iCase = 0, $iNumericalSort = 0)

    If $iDescending = Default Then $iDescending = 0
    If $iStart = Default Then $iStart = 0
    If $iEnd = Default Then $iEnd = 0
    If $iSubItem = Default Then $iSubItem = 0
    If $iPivot = Default Then $iPivot = 0
    If $iCase = Default Then $iCase = 0
    If Not IsArray($aArray) Then Return SetError(1, 0, 0)

    Local $iUBound = UBound($aArray) - 1
    If $iUBound = -1 Then Return SetError(5, 0, 0)

    ; Bounds checking
    If $iEnd = Default Then $iEnd = 0
    If $iEnd < 1 Or $iEnd > $iUBound Or $iEnd = Default Then $iEnd = $iUBound
    If $iStart < 0 Or $iStart = Default Then $iStart = 0
    If $iStart > $iEnd Then Return SetError(2, 0, 0)

    ; Sort
    Switch UBound($aArray, $UBOUND_DIMENSIONS)
        Case 1
            If $iNumericalSort Then ; <======== Optional numeric sorting 1D.
                For $i = $iStart To $iEnd
                    If StringRegExp($aArray[$i], "^[+-]?\d+\.?\d*$") Then $aArray[$i] = Number($aArray[$i])
                Next
            EndIf
            If $iPivot Then ; Switch algorithms as required
                __ArrayDualPivotSort($aArray, $iStart, $iEnd)
            Else
                __ArrayQuickSort1DC($aArray, $iStart, $iEnd, $iCase)
            EndIf
            If $iDescending Then _ArrayReverse($aArray, $iStart, $iEnd)
        Case 2
            If $iNumericalSort Then ; <======== Optional numeric sorting 2D.
                For $i = $iStart To $iEnd
                    If StringRegExp($aArray[$i][$iSubItem], "^[+-]?\d+\.?\d*$") Then $aArray[$i][$iSubItem] = Number($aArray[$i][$iSubItem])
                Next
            EndIf
            If $iPivot Then Return SetError(6, 0, 0) ; Error if 2D array and $iPivot
            Local $iSubMax = UBound($aArray, 2) - 1 ; $UBOUND_COLUMNS (2)
            If $iSubItem > $iSubMax Then Return SetError(3, 0, 0)

            If $iDescending Then
                $iDescending = -1
            Else
                $iDescending = 1
            EndIf

            __ArrayQuickSort2DC($aArray, $iDescending, $iStart, $iEnd, $iSubItem, $iSubMax, $iCase)
        Case Else
            Return SetError(4, 0, 0)
    EndSwitch

    Return 1
EndFunc   ;==>_ArraySortC

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name...........: __ArrayQuickSort1DC  (Appended 'C' for case sensitivity)
; Description ...: Helper function for sorting 1D arrays
; Syntax.........: __ArrayQuickSort1D ( ByRef $aArray, ByRef $iStart, ByRef $iEnd )
; Parameters ....: $aArray - Array to sort
;                  $iStart  - Index of array to start sorting at
;                  $iEnd    - Index of array to stop sorting at
; Return values .: None
; Author ........: Jos van der Zande, LazyCoder, Tylo, Ultima
; Modified.......: Malkey
; Remarks .......: For Internal Use Only.  All StringCompare functions have $iCase parameter added for optional case sensitivity.
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func __ArrayQuickSort1DC(ByRef $aArray, Const ByRef $iStart, Const ByRef $iEnd, Const ByRef $iCase)
    If $iEnd <= $iStart Then Return

    Local $vTmp

    ; InsertionSort (faster for smaller segments)
    If ($iEnd - $iStart) < 15 Then
        Local $vCur
        For $i = $iStart + 1 To $iEnd
            $vTmp = $aArray[$i]

            If IsNumber($vTmp) Then
                For $j = $i - 1 To $iStart Step -1
                    $vCur = $aArray[$j]
                    ; If $vTmp >= $vCur Then ExitLoop
                    If ($vTmp >= $vCur And IsNumber($vCur)) Or (Not IsNumber($vCur) And StringCompare($vTmp, $vCur, $iCase) >= 0) Then ExitLoop
                    $aArray[$j + 1] = $vCur
                Next
            Else
                For $j = $i - 1 To $iStart Step -1
                    If (StringCompare($vTmp, $aArray[$j], $iCase) >= 0) Then ExitLoop
                    $aArray[$j + 1] = $aArray[$j]
                Next
            EndIf

            $aArray[$j + 1] = $vTmp
        Next
        Return
    EndIf

    ; QuickSort
    Local $L = $iStart, $R = $iEnd, $vPivot = $aArray[Int(($iStart + $iEnd) / 2)], $bNum = IsNumber($vPivot)
    Do
        If $bNum Then
            ; While $aArray[$L] < $vPivot
            While ($aArray[$L] < $vPivot And IsNumber($aArray[$L])) Or (Not IsNumber($aArray[$L]) And StringCompare($aArray[$L], $vPivot, $iCase) < 0)
                $L += 1
            WEnd
            ; While $aArray[$R] > $vPivot
            While ($aArray[$R] > $vPivot And IsNumber($aArray[$R])) Or (Not IsNumber($aArray[$R]) And StringCompare($aArray[$R], $vPivot, $iCase) > 0)
                $R -= 1
            WEnd
        Else
            While (StringCompare($aArray[$L], $vPivot, $iCase) < 0)
                $L += 1
            WEnd
            While (StringCompare($aArray[$R], $vPivot, $iCase) > 0)
                $R -= 1
            WEnd
        EndIf

        ; Swap
        If $L <= $R Then
            $vTmp = $aArray[$L]
            $aArray[$L] = $aArray[$R]
            $aArray[$R] = $vTmp
            $L += 1
            $R -= 1
        EndIf
    Until $L > $R

    __ArrayQuickSort1DC($aArray, $iStart, $R, $iCase)
    __ArrayQuickSort1DC($aArray, $L, $iEnd, $iCase)
EndFunc   ;==>__ArrayQuickSort1DC

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name...........: __ArrayQuickSort2DC (Appended 'C' for case sensitivity)
; Description ...: Helper function for sorting 2D arrays
; Syntax.........: __ArrayQuickSort2D ( ByRef $aArray, ByRef $iStep, ByRef $iStart, ByRef $iEnd, ByRef $iSubItem, ByRef $iSubMax )
; Parameters ....: $aArray  - Array to sort
;                  $iStep    - Step size (should be 1 to sort ascending, -1 to sort descending!)
;                  $iStart   - Index of array to start sorting at
;                  $iEnd     - Index of array to stop sorting at
;                  $iSubItem - Sub-index to sort on in 2D arrays
;                  $iSubMax  - Maximum sub-index that array has
;                  $iCase    -
; Return values .: None
; Author ........: Jos van der Zande, LazyCoder, Tylo, Ultima
; Modified.......: Malkey
; Remarks .......: For Internal Use Only.  All StringCompare functions have $iCase parameter added for optional case sensitivity.
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func __ArrayQuickSort2DC(ByRef $aArray, Const ByRef $iStep, Const ByRef $iStart, Const ByRef $iEnd, Const ByRef $iSubItem, Const ByRef $iSubMax, Const ByRef $iCase)
    If $iEnd <= $iStart Then Return

    ; QuickSort
    Local $vTmp, $L = $iStart, $R = $iEnd, $vPivot = $aArray[Int(($iStart + $iEnd) / 2)][$iSubItem], $bNum = IsNumber($vPivot)
    Do
        If $bNum Then
            ; While $aArray[$L][$iSubItem] < $vPivot
            While ($iStep * ($aArray[$L][$iSubItem] - $vPivot) < 0 And IsNumber($aArray[$L][$iSubItem])) Or (Not IsNumber($aArray[$L][$iSubItem]) And $iStep * StringCompare($aArray[$L][$iSubItem], $vPivot, $iCase) < 0)
                $L += 1
            WEnd
            ; While $aArray[$R][$iSubItem] > $vPivot
            While ($iStep * ($aArray[$R][$iSubItem] - $vPivot) > 0 And IsNumber($aArray[$R][$iSubItem])) Or (Not IsNumber($aArray[$R][$iSubItem]) And $iStep * StringCompare($aArray[$R][$iSubItem], $vPivot, $iCase) > 0)
                $R -= 1
            WEnd
        Else
            While ($iStep * StringCompare($aArray[$L][$iSubItem], $vPivot, $iCase) < 0)
                $L += 1
            WEnd
            While ($iStep * StringCompare($aArray[$R][$iSubItem], $vPivot, $iCase) > 0)
                $R -= 1
            WEnd
        EndIf

        ; Swap
        If $L <= $R Then
            For $i = 0 To $iSubMax
                $vTmp = $aArray[$L][$i]
                $aArray[$L][$i] = $aArray[$R][$i]
                $aArray[$R][$i] = $vTmp
            Next
            $L += 1
            $R -= 1
        EndIf
    Until $L > $R

    __ArrayQuickSort2DC($aArray, $iStep, $iStart, $R, $iSubItem, $iSubMax, $iCase)
    __ArrayQuickSort2DC($aArray, $iStep, $L, $iEnd, $iSubItem, $iSubMax, $iCase)
EndFunc   ;==>__ArrayQuickSort2DC

Here are examples.

#include <ArrayMultiSortCase.au3>

; --------------- Create test data --------------
Global $array[2000][5] = [["Col 0", "Col 1", "Col 2", "Col 3", "Col 4"]] ;
For $i = 1 To UBound($array) - 1
    $array[$i][0] = (Random(0, 1, 1) ? Chr(Random(97, 99, 1)) : Chr(Random(65, 67, 1))) ; Random lower case and upper case a,b, or, c.
    $array[$i][1] = (Random(0, 1, 1) ? Chr(Random(97, 99, 1)) : Chr(Random(65, 67, 1))) ; Random lower case and upper case a,b, or, c.
    $array[$i][2] = String(Random(0, 1, 1) ? Int(Random(0, 2, 1) & Random(0, 3, 1)) : (Random(0, 1, 1) ? Chr(Random(97, 99, 1)) : Chr(Random(65, 67, 1)))) ; Random lower case and upper case a,b, or, c. And random string numbers.
    $array[$i][3] = String(Random(0, 2, 1) & Random(1, 3, 1)) ; Random string numbers
    $array[$i][4] = Random(0, 20, 1) ; Random numeric numbers
Next
; -----------> End of Create test data -----------

; ---------------------------------------------------- Examples ---------------------------------------------------------------
; ======== _ArrayMultiSort() - 2D array =========
Local $aArray = $array
; Sort column 0(asc+case sensitive) then column 1(desc+case insensitive), then column 2(asc+case sensitive),
; then column 3(desc+case insensitive, numeric sorting), and lastly column 4(asc+case insensitive).

; _ArrayMultiSort(ByRef $aArray, $sSort = '0a', $iStartRow = 0, $iEndRow = 0)
_ArrayMultiSort($aArray, "0a,c1d, 2 an(bacdefghijnmlosprqtuwxyzBA),3n,4d", 1)  ;
_ArrayDisplay($aArray, "Sorted 0a,1cd,2an(bacdefghijnmlosprqtuwxyzBA),3an,4d")

#cs ; Note:-
Colunm #0 - 1st sorted, ascending, case insenitive, digits not specifically numberfied. The string digits one one, "11", is smaller than the
            string digit "2" because of alphabetic sorting. See A's and a's mixed together because of case insenitivity.
Colunm #1 - 2nd sorted, descending, case senitive, digits not specifically numberfied. See all c's are followed by all b's.   c's and C's are
            separated because of case senitivity.
Colunm #2 - 3rd sorted, ascending, incase insenitive. string digits numberfied.  The number 2, is smaller than the number eleven, 11 when numerically sorted.
            All the characters in the array will be sorted in the ascending order (left to right) same as the characters between the brackets are listed.
Colunm #3 - 4th sorted, ascending, case insenitive, numeric sorting of converted string numbers.
Colunm #4 - Last sorted,descending, case insenitive,  automatic numeric sorting of existing numeric values - No "n" necessary.  In _ArrayDisplay, find the same
            two to four equal numbers in column #3.  Note, in the corresponding rows in column #4, these two to four numbers are descending and numerically sorted.
#ce ; ----------------------------------------------------------------------------------------------------------------------------


; ======== _ArrayMultiSort() - 1D array =========
Local $aTest[] = [33, "Bmw", "Blue", 3.9, "brown", 11, "Clear", "green", 13, "Gray", -3.5, "gold", 23, "-3.1", "Gold", "3.7", "qqqqq", _
        "0", "gray", "2", "ppppp", "3.3", "rrrrr", "3", "Royal", "112", "sssss", "42", "ace", "32", "White", "-112", "Yellow", "5"]

; _ArrayMultiSort(ByRef $aArray, $sSort = '0a', $iStartRow = 0, $iEndRow = 0)
_ArrayMultiSort($aTest)
_ArrayDisplay($aTest, "Sorted 0a")

#cs ; Note:-
Colunm #0 - Ascending - The numeric values are sorted numerically from smallest to largest.  However, the string numbers are sorted alphabetically
            and interspersed between the numerically sorted values.
            And because of case insensitivity, the words starting with "G" and "g" are sorted as though the same letter.
#ce ; ----------------------------------------------------------------------------------------------------------------------------


; ======= _ArraySortInOrder() =========
Local $aTest[] = [33, "Bmw", "Blue", 3.9, "brown", 11, "Clear", "green", 13, "Gray", -3.5, "gold", 23, "-3.1", "Gold", "3.7", "qqqqq", _
        "0", "gray", "2", "ppppp", "3.3", "rrrrr", "3", "Atlas", "112", "sssss", "42", "ace", "32", "White", "-112", "Yellow", "5", "kind"]

;_ArraySortInOrder(ByRef $array, $sSortingOrder, $iDescending = 0, $iStartRow = 0, $iEndRow = 0, $iSubItem = 0, $iPivot = 0, $iCase = 0, $iNumericalSort = 0)
_ArraySortInOrder($aTest, "bacdefghijnmlosprqtuwxyzBA", 0, 0, 0, 0, 0, 0, 1)
_ArrayDisplay($aTest, "Ordered sorting & numeric sorting")

#cs
Note:-
Sorting with case sensitivity   - Ascending (0-9,A-Z,a-z,Ordered sorting)
Sorting with case insensitivity - Ascending (Ordered sorting,9-0,Aa-Zz)
Colunm #0 - Case insensitive, Ascending, and Numerically sorted, and Ordered sorting - All numbers are forced to numeric values and sorted numerically from
            smallest number to largest number.
#ce ; ----------------------------------------------------------------------------------------------------------------------------


; ======= _ArraySortc() - numeric sorting =========
Local $aTest[18][2] = [["Color", "#"], ["Black", "a"], ["Blue", "13"], ["white", "23"], ["Brown", "-4.2"], ["Clear", "33"], ["Gold", "43"], ["gold", "53"], _
        ["green", "c"], ["Green", "73"], ["brown", "-4.1"], ["Purple", "b"], ["Red", "93"], ["Royal", "+5"], ["Silver", "2"], ["Sky", "12"], ["White", "3"], ["Yellow", "5"]]
$aTest1 = $aTest

; _ArraySortC(ByRef $aArray, $iDescending = 0, $iStart = 0, $iEnd = 0, $iSubItem = 0, $iPivot = 0, $iCase = 0, $iNumericalSort = 0)
_ArraySortc($aTest1, 0, 1, 0, 1, 0, 0, 1)
_ArrayDisplay($aTest1, "_ArraySortc()")
; Or

; ======= _ArraySort() - numeric sorting =========
$aTest2 = $aTest

; _ArrayNumbertizeForSort(ByRef $aArray, $iSubItem = 0, $iStart = 0, $iEnd = 0)
_ArrayNumbertizeForSort($aTest2, 1, 1, 0) ; For use with current _ArraySort() function to numeric sort.

; _ArraySort ( ByRef $aArray [, $iDescending = 0 [, $iStart = 0 [, $iEnd = 0 [, $iSubItem = 0 [, $iPivot = 0]]]]] )
_ArraySort($aTest2, 0, 1, 0, 1)
_ArrayDisplay($aTest2, "_ArrayNumbertizeForSort()")


; _ArrayNumbertizeForSort() used only here, in this above example. (not in UDF)
; $iSubItem [optional] Sub-index to sort on in 2D arrays (default 0 = first column)
Func _ArrayNumbertizeForSort(ByRef $aArray, $iSubItem = 0, $iStart = 0, $iEnd = 0)
    If $iEnd = 0 Then $iEnd = UBound($aArray) - 1
    Switch UBound($aArray, 0)
        Case 1 ; 1D array
            For $i = $iStart To $iEnd
                If StringRegExp($aArray[$i], "^[+-]?\d+\.?\d*$") Then $aArray[$i] = Number($aArray[$i])
            Next
        Case 2 ; 2D array
            For $i = $iStart To $iEnd
                If StringRegExp($aArray[$i][$iSubItem], "^[+-]?\d+\.?\d*$") Then $aArray[$i][$iSubItem] = Number($aArray[$i][$iSubItem])
            Next
    EndSwitch
EndFunc   ;==>_ArrayNumbertizeForSort

 

Edited by Malkey
Added link to this page in UDF
Link to comment
Share on other sites

  • 2 weeks later...

This post, and all reference to the 'StringRegExpReplace method' in the _ArrayMultiSort() function is now superceded, outdated. 
Having added a numeric sorting parameter, a 'StringRegExpReplace method' to convert the $sSort string to the $aSort2D array is too involved. I opted for a simpler method - See _ArrayMultiSort() function in ArrayMultiSortCase.au3 UDF in first post.

The original outdated post follows.

The '$sSort' parameter
When I first posted this _ArrayMultiSort function, the '$sSort' parameter had to have each column descriptor in the order 'c', optional case sensitivity,
the next the column number, digits, and finally optional 'a' or 'd', for sort order. This easily entered into a 3 column 2D array (with the same order)
for use in the script.
I decided I did not like the fixed order description. In fact I like the order of "2 cd, 3 ac, ...". The column number stands out followed by the column's specs.

So now the first post _ArrayMultiSort function has been modified so that each column descriptor can be entered in any order the user wishes.
Each column descriptor has to have a column number. The chosen columns of the array are sorted from left to right as appears in the $sSort variable.
The original order of the 3 column 2D array generated from the '$sSort' parameter remains the same.

Examples of the $sSort variable:-
    "3 ac, CD2, 0C, d1" -> First sort col#3 (case sensitive, ascending). Secondly sort col#2 (case sensitive, descending).
                           Thirdly sort col#0 (case sensitive, ascending[default]). Lastly sort col#1 (case insensitive[default], descending).
    "0"  or   ""        -> Sort col#0 (case insensitive[default], ascending[default]).

In the '$sSort' parameter of the _ArrayMultiSort() function:-
    Spaces are optional;
    Upper or lower case column descriptors, "CAD" or "cad", are recognized as the same - use either; and,
    Each column descriptor is comma separated.

#include <array.au3>

; ---------- Original $sSort (string) to $aSort2D (array) -----------
; Each column in $sSort had to start with an optional "c" for case sensitive. When no "c" present the column was case insensitive. Because in the
; replace parameter of the StringRegExpReplace function, there is this part "('$1'='c')*1".  When the back reference, '$1' is 'c', we have ('c'='c')*1 = 1 (case sensitive).
; When the back reference, '$1' is '', absent, we have (''='c')*1 = (False)*1 = 0 (case insensitive).
; The next middle letter had to be the column number, zero-based.
; The last optional letter was either an "a" or a "d" for ascending or descending. When the back reference '$3' is '', nothing, or an 'a', because of "('$3'='d')*1",
;  zero for ascending is generated. For descending, a 'd' must be present to generate a "1".
Local $aSort2D[0][3]
Local $sSort = "c0a,1d,C2A,3D,4a" ; Note: Each column descriptor starts with an optional 'c', followed by column number, digits, and ending with optional 'a' or 'd'.
_ArrayAdd($aSort2D, Execute(StringTrimRight(StringRegExpReplace($sSort, "(?i)(c?)(\d+)([ad]?),?", "('$1'='c')*1&""|$2|""&('$3'='d')*1&@CRLF&"), 7)))
_ArrayDisplay($aSort2D, "Original method", "", 0, Default, "Case|Col#|Asc/Desc")
; ------------------------------------------------------------

; ---------- New $sSort (string) to $aSort2D (array) routine -----------
; This allows the optional 'c', the column number, and the sort order,'a' or 'd', to be entered in any order.
; See the contents of comma separated variable, $sSort, below for examples.
Local $aSort2D[0][3]
Local $sSort = "c0d, a1c, 2 cd, 3 ac, CD4, AC5, d6, c7, 8d, 9c, 10" ; 11 possibilities
_ArrayAdd($aSort2D, Execute(StringTrimRight(StringRegExpReplace(StringStripWS($sSort, 8), _
        "(?i)(c)(\d+)([ad]),?|([ad])(\d+)(c),?|(\d+)([ad])(c),?|(\d+)(c)([ad]),?|([ad])(c)(\d+),?|" & _ ; RE pattern 5 possibilities (BackRef 1 to 15)
        "(c)([ad])(\d+),?|([ad])(\d+),?|(c)(\d+),?|(\d+)(c),?|(\d+)([ad]),?|(\d+),?", _ ; RE pattern 6 possibilities (BackRef 16 to 27)
        "('$1$6$9${11}${14}${16}${21}${24}'='c')*1&""|$2$5$7${10}${15}${18}${20}${22}${23}${25}${27}|""&('$3$4$8${12}${13}${17}${19}${26}'='d')*1&@CRLF&"), _ ; The 'replace' parameter of StringRegExpReplace()
        7))) ; End of StringTrimRight() that removes trailing "&@CRLF&"
_ArrayDisplay($aSort2D, "New method", "", 0, Default, "Case|Col#|Asc/Desc")
#cs
    Note: The RE pattern is made up of 11 possible column descriptor orderings. Each possibility is separated by the 'or' character,"|".
    Each possibility generates either 3, 2, or 1 back references per possibility.
    Each capture group, "(...)", automatically generates a back reference and are numered from left to right starting at 1.
    In the StringRegExpReplace() 'replace' parameter we have, "('$1$6$9${11}${14}${16}${21}${24}'='c')*1". The backreferences 1,6,9,11,14,16,21,24 are from the
    possibilities in the RE pattern where a 'c' for case sensitivity is present.  As only 1 possibility will match per column descriptor, only a maximium of 3 back
    references will match 1/ the optional 'c', 2/ a column number, and/or 3/ optional 'a' or 'd'. (And minimum of 1 back references will match a solitary column number)
    So, one of the back references from one of the possibilities that is matched could be 'c'. The rest of the back references from the other
    posibilities that are not matched will be "" (nothing).
    Where     "('$1$6$9${11}${14}${16}${21}${24}'='c')*1" = "(''''c'''''''''''='c')*1)" = "('c'='c')*1" = (True)*1  = 1. (Case sensitive)
    And where "('$1$6$9${11}${14}${16}${21}${24}'='c')*1" = "('''''''''''''''='c')*1)"  = "(''='c')*1"  = (False)*1 = 0. (Case insensitive)
#ce

 

Edited by Malkey
Changed coma to comma.
Link to comment
Share on other sites

Updated first post to include optional numeric sorting, optional sorting following a sorting order string, as well as optional case sensitive sorting (the reason for starting this thread).
Updated _ArraySortC() and _ArrayMultiSort() functions and added _ArraySortInOrder() function in the ArrayMultiSortCase.au3 UDF.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...