Jump to content

How to Sort a array by string length ?


Trong
 Share

Recommended Posts

You're right both, $iMaxlen length is too much. Instead, 10 could be enough.

So now, the code is shorter  :):

#include<array.au3>
$sString = "B|A|A|C|C|AB|DDD|BAAS|003|100|02"

$s = Execute("'" & StringRegExpReplace($sString, "([^|]+)", "' & StringFormat('%010i', StringLen('$1')) & '$1' & '") & "'")
$res = StringRegExp($s, "[^|]+", 3)
_ArraySort($res)

For $i = 0 To UBound($res) - 1
    $res[$i] = StringTrimLeft($res[$i], 10)
Next
_ArrayDisplay($res)

 

Link to comment
Share on other sites

?

#include<array.au3>
$sString = "B|A|A|C|C|AB|DDD|BAAS|003|100|02"

$a = StringSplit($sString, "|")
Local $b[$a[0]+1][2]
For $i = 1 To $a[0]
   $b[$i][0] = $a[$i]
   $b[$i][1] = StringLen($a[$i]) & $a[$i]
Next
_ArraySort($b, 0, 1, 0, 1)
For $i = 1 To $a[0]
   $a[$i] = $b[$i][0]
Next
_ArrayDisplay($a)

Edit
Please don't hit me too hard  :>... :)

Edited by mikell
Link to comment
Share on other sites

_ArrayUnique is missing

$a = StringSplit($sString, "|")
$a = _ArrayUnique($a)
Local $b[$a[0]+1][2]

;)

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

UEZ, I'm not sure that _ArrayUnique is appropriate as we don't know if the OP wants to keep duplicates or not
Anyway if duplicates exist then using _ArrayUnique will change the count at index 0 and my code will need to be revised  ;)
And if a row contains the same value as the count in row 0 then the whole thing becomes terrible  :)

Edited by mikell
Link to comment
Share on other sites

For this kinds of problem the best approach is to use a sorting function where you can set a user defined comparison function. Because the most sorting algorithms compare two elements of the set for greater/lesser/equal.

If you have such a dynamic sorting function it's easy to sort every kind of the data exactly how you wan't without thinking about the sorting himself.

Here is an example for such a function for your case:

#include <Array.au3>

Global $sString = "B|A|A|C|C|AB|DDD|BAAS|003|100|02"

Global $a_Array = StringSplit($sString, "|", 2)
_ArrayDisplay($a_Array, "initial Array")
_ArraySortFlexible($a_Array, cb_compareStringSizes)
_ArrayDisplay($a_Array, "sorted Array")

; user defined comparison-function
Func cb_compareStringSizes($a, $b)
    Local $1 = StringLen($a), $2 = StringLen($b)
    If $1 = $2 Then Return StringCompare($a,$b, 2)
    Return $1 > $2 ? 1 : -1
EndFunc



; #FUNCTION# ======================================================================================
; Name ..........: _ArraySortFlexible
; Description ...: sort an array with a user-defined sorting rule by choosing from Quicksort/Dual-Pivot-Quicksort/Insertion-Sort
; Syntax ........:_ArraySortFlexible(ByRef $a_Array, [$cb_Func = Default, [Const $i_Min = 0, [Const $i_Max = UBound($a_Array) - 1, [Const $b_MedianPivot = True, [Const $b_InsSort = True, [Const $d_SmallThreshold = 25, {Const $b_First = True}]]]]]])
; Parameters ....: $a_Array       - the array which should be sorted (by reference means direct manipulating of the array - no copy)
;                  $cb_Func       - function variable points to a function of a form "[1|0|-1] function(value, value)"
;                                   the function compares two values a,and b for a>b/a=b/a<b
;                                   an example is the AutoIt-Function "StringCompare".
;                                   If the default value is used this functions gets only a wrapper for the optimized _ArraySort()-function
;                  $i_Min         - the start index for the sorting range in the array
;                  $i_Max         - the end index for the sorting range in the array
;                  $b_MedianPivot - If True: pivot-element is median(first,last,middle) Else: pivot = list[Random]
;                  $b_InsSort     - If True: if length(list) < $d_SmallThreshold then insertion sort is used instead of recursive quicksort
;                  $d_SmallThreshold - the threshold-value for $b_InsSort (value=15 determined empirical)
;                  {$b_First}     - don't touch - for internal use only (checks if call is sub-call or user-call)
; Return values .: Success: True  - array is sorted now
;                  Failure: False
;                     @error = 1: $a_Array is'nt an array
;                              2: invalid value for $i_Min
;                              3: invalid value for $i_Max
;                              4: invalid combination of $i_Min and $i_Max
;                              5: invalid value for $cb_Func
; Author ........: AspirinJunkie
; Related .......: __cb_NormalComparison(), __PartitionHoare, __ArrayDualPivotQuicksort
; Remarks .......: for sorting the quicksort-algorithm is used with hoare's algorithm for partitioning
; =================================================================================================
Func _ArraySortFlexible(ByRef $a_Array, $cb_Func = Default, Const $i_Min = 0, Const $i_Max = UBound($a_Array) - 1, Const $b_DualPivot = True, Const $b_MedianPivot = True, Const $b_InsSort = True, Const $d_SmallThreshold = 15, Const $b_First = True)
    If $b_First Then
        If $cb_Func = Default Then Return _ArraySort($a_Array, 0, 0, 0, 0, 1)
        ; error-handling:
        If Not IsArray($a_Array) Then Return SetError(1, 0, False)
        If Not IsInt($i_Min) Or $i_Min < 0 Then Return SetError(2, $i_Min, False)
        If Not IsInt($i_Max) Or $i_Max > UBound($a_Array) - 1 Then Return SetError(3, $i_Min, False)
        If $i_Min > $i_Max Then Return SetError(4, $i_Max - $i_Min, False)
        If Not IsFunc($cb_Func) Then Return SetError(5, 0, False)
    EndIf

    ; choose the sorting-algorithm:
    If $b_DualPivot Then ; Dual-Pivot-Quicksort
        __ArrayDualPivotQuicksort($a_Array, $cb_Func, $i_Min, $i_Max)
    ElseIf $b_InsSort And (($i_Max - $i_Min) < $d_SmallThreshold) Then ; insertion-sort:
        Local $t1, $t2
        For $i = $i_Min + 1 To $i_Max
            $t1 = $a_Array[$i]
            For $j = $i - 1 To $i_Min Step -1
                $t2 = $a_Array[$j]
                If $cb_Func($t1, $t2) <> -1 Then ExitLoop
                $a_Array[$j + 1] = $t2
            Next
            $a_Array[$j + 1] = $t1
        Next
    Else ; Quicksort:
        ; the pivot element which divides the list in two separate lists (values < pivot -> left list, values > pivot -> right list); here pivot=list[random] to minimize the probability of worst case. Other solution is median(iMin, iMiddle, iMax)          Das Trennelement (alles was kleiner ist - links davon, alles was größer ist - rechts davon), Hier Random damit Worst-Case unwahrscheinlich wird
        If $b_MedianPivot Then ; pivot = median(iMin, iMiddle, iMax)
            Local Const $iMiddle = Floor(($i_Max + $i_Min) / 2)
            Local Const $a = $a_Array[$i_Min], $b = $a_Array[$i_Max], $c = $a_Array[$iMiddle]
            Local $p_Value = $cb_Func($a, $b) = 1 ? $cb_Func($a, $c) = 1 ? $cb_Func($c, $b) = 1 ? $c : $b : $a : $cb_Func($a, $c) = 1 ? $a : $cb_Func($c, $b) = 1 ? $b : $c ; = Median(a,b,c)
            Local $p_Index = $p_Value = $a ? $i_Min : $p_Value = $b ? $i_Max : $iMiddle ; = Index(p_Value)
        Else ; pivot=list[random]
            Local $p_Index = Random($i_Min, $i_Max, 1)
            Local $p_Value = $a_Array[$p_Index]
        EndIf
        Local $v_Temp
        ; move the pivot-element to the end of the array
        If $p_Index < $i_Max Then
            $a_Array[$p_Index] = $a_Array[$i_Max]
            $a_Array[$i_Max] = $p_Value
        EndIf

        Local $i = __PartitionHoare($a_Array, $i_Min, $i_Max, $p_Value, $cb_Func)
        ; sort the left list (if length > 1) :
        If $i_Min < $i - 1 Then _ArraySortFlexible($a_Array, $cb_Func, $i_Min, $i - 1, False, False)
        ; recursively sort the right list (if length > 1):
        If $i_Max > $i + 1 Then _ArraySortFlexible($a_Array, $cb_Func, $i + 1, $i_Max, False, False)
    EndIf
    Return True
EndFunc   ;==>_ArraySortFlexible



; #FUNCTION# ======================================================================================
; Name ..........: __ArrayDualPivotQuicksort
; Description ...: sort an array with the Dual-Pivot-Quicksort from Vladimir Yaroslavskiy
; Syntax ........:__ArrayDualPivotQuicksort(ByRef $A, [$cb_Func = Default, [Const $left = 0, [Const $right = UBound($a_Array) - 1, Const $d_SmThr = 25, [Const $b_MedQuant = True, {Const $b_First = True}]]]]])
; Parameters ....: $A             - the array which should be sorted (by reference means direct manipulating of the array - no copy)
;                  $cb_Func       - function variable points to a function of a form "[1|0|-1] function(value, value)"
;                                   the function compares two values a,and b for a>b/a=b/a<b
;                                   an example is the AutoIt-Function "StringCompare".
;                                   If the default value is used this functions gets only a wrapper for the optimized _ArraySort()-function
;                  $left          - the start index for the sorting range in the array
;                  $right         - the end index for the sorting range in the array
;                  $div           - factor for calculating the pivots positions - normally dont't change this value
;                  $d_SmThr       - the threshold-value for $b_InsSort (value=35 determined empirical)
;                  $b_MedQuant    - if true the dual-pivot-elements are estimated by an more robust approach which should lead to more similar sized subarrays
;                  {$b_First}     - don't touch - for internal use only (checks if call is sub-call or user-call)
; Return values .: Success: True  - array is sorted now
;                  Failure: False
;                     @error = 1: $a_Array is'nt an array
;                              2: invalid value for $i_Min
;                              3: invalid value for $i_Max
;                              4: invalid combination of $i_Min and $i_Max
;                              5: invalid value for $cb_Func
; Author ........: AspirinJunkie
; Related .......: __cb_NormalComparison()
; =================================================================================================
Func __ArrayDualPivotQuicksort(ByRef $a, $cb_Func = Default, Const $left = 0, Const $right = UBound($a) - 1, $div = 3, Const $d_SmThr = 47, Const $b_MedQuant = True, Const $b_First = True)
    Local $d_Len = $right - $left
    Local $k, $t, $N
    Local $t1, $t2 ; variables for insertion-sort

    If $b_First Then
        If $cb_Func = Default Then Return _ArraySort($a, 0, 0, 0, 0, 1)
        ; If $cb_Func = Default Then $cb_Func = __cb_NormalComparison
        ; error-handling:
        If Not IsArray($a) Then Return SetError(1, 0, False)
        If Not IsInt($left) Or $left < 0 Then Return SetError(2, $left, False)
        If Not IsInt($right) Or $right > UBound($a) - 1 Then Return SetError(3, $right, False)
        If $left >= $right Then Return SetError(4, $right - $left, False)
        If Not IsFunc($cb_Func) Then Return SetError(5, 0, False)
    EndIf

;~  ; Insertion-Sort if array is small enough:
    If $d_Len < $d_SmThr Then
        For $i = $left + 1 To $right
            $t1 = $a[$i]
            For $j = $i - 1 To $left Step -1
                $t2 = $a[$j]
                If $cb_Func($t1, $t2) <> -1 Then ExitLoop
                $a[$j + 1] = $t2
            Next
            $a[$j + 1] = $t1
        Next
        Return True
    EndIf

    ; ------------ estimate the two pivot-elements --------------------------
    If $b_MedQuant And $d_Len > $d_SmThr Then
        Local $d_third = Floor($d_Len / 3)
        ; Estimate the 25% / 75% -quantils better:
        Local $aMp[5] = [$left, Floor($left + $d_third), Floor($left + 0.5 * $d_Len), Floor($right - $d_third), $right]
        For $i = 1 To 4 ; insertion-sort for the quantils (sort the quartils also inside the array) (is like a 5-Point Double-Median for the 25%/75%-quantil)
            $t1 = $a[$aMp[$i]]
            For $j = $i - 1 To 0 Step -1
                $t2 = $a[$aMp[$j]]
                If $cb_Func($t1, $t2) <> -1 Then ExitLoop
                $a[$aMp[$j + 1]] = $t2
            Next
            $a[$aMp[$j + 1]] = $t1
        Next
        Local $m1 = $aMp[1], $m2 = $aMp[3]
    Else
        Local $d_third = Floor($d_Len / $div)
        ;"medians" (at index 25% and 75%)
        Local $m1 = $left + $d_third
        Local $m2 = $right - $d_third

        If $m1 <= $left Then $m1 = $left + 1
        If $m2 >= $right Then $m2 = $right - 1
    EndIf

    ; ensure that m1 < m2 and move them to the outer fields
    If $cb_Func($a[$m1], $a[$m2]) = -1 Then
        __swap($a, $m1, $left)
        __swap($a, $m2, $right)
    Else
        __swap($a, $m1, $right)
        __swap($a, $m2, $left)
    EndIf

    ; pivots:
    Local $pivot1 = $a[$left]
    Local $pivot2 = $a[$right]

    ; pointers:
    Local $less = $left + 1
    Local $great = $right - 1

    ; sorting:
    $k = $less
    Do
        ; move elements < pivot1 to the beginning of the array
        If $cb_Func($a[$k], $pivot1) = -1 Then
            ; __swap($A, $k, $less)
            $t = $a[$k]
            $a[$k] = $a[$less]
            $a[$less] = $t
            $less += 1
            ; move elements > pivot1 to the end of the array
        ElseIf $cb_Func($a[$k], $pivot2) = 1 Then
            While $k < $great And $cb_Func($a[$great], $pivot2) = 1
                $great -= 1
            WEnd
            ;__swap($A, $k, $great)
            $t = $a[$k]
            $a[$k] = $a[$great]
            $a[$great] = $t
            $great -= 1
            If $cb_Func($a[$k], $pivot1) = -1 Then
                ;__swap($A, $k, $less)
                $t = $a[$k]
                $a[$k] = $a[$less]
                $a[$less] = $t
                $less += 1
            EndIf
        EndIf
        $k += 1
    Until $k > $great

    ; swaps
    Local $dist = $great - $less
    If $dist < 13 Then $div += 1
    __swap($a, $less - 1, $left)
    __swap($a, $great + 1, $right)

    ; subarrays
    If ($less - 2 - $left) > 0 Then __ArrayDualPivotQuicksort($a, $cb_Func, $left, $less - 2, $div, $d_SmThr, $b_MedQuant, False)
    If ($right - ($great + 2)) > 0 Then __ArrayDualPivotQuicksort($a, $cb_Func, $great + 2, $right, $div, $d_SmThr, $b_MedQuant, False)

    ; equal elements
    If ($dist > ($d_Len - 13)) And ($cb_Func($pivot2, $pivot1) <> 0) Then
        $k = $less
        Do
            If $cb_Func($a[$k], $pivot1) = 0 Then
                ;__swap($A, $k, $less)
                $t = $a[$k]
                $a[$k] = $a[$less]
                $a[$less] = $t
                $less += 1
            ElseIf $cb_Func($a[$k], $pivot2) = 0 Then
                ;__swap($A, $k, $great)
                $t = $a[$k]
                $a[$k] = $a[$great]
                $a[$great] = $t
                $great -= 1
                If $cb_Func($a[$k], $pivot1) = 0 Then
                    ;__swap($A, $k, $less)
                    $t = $a[$k]
                    $a[$k] = $a[$less]
                    $a[$less] = $t
                    $less += 1
                EndIf
            EndIf
            $k += 1
        Until $k > $great
    EndIf

    ; the middle subarray
    If $cb_Func($pivot1, $pivot2) = -1 And $great - $less > 0 Then __ArrayDualPivotQuicksort($a, $cb_Func, $less, $great, $div, $d_SmThr, $b_MedQuant, False)
    Return True
EndFunc   ;==>__ArrayDualPivotQuicksort

; #FUNCTION# ======================================================================================
; Name ..........: __swap
; Description ...: helper function for swap two values inside an array
; Syntax ........: __swap(ByRef Const $a, ByRef Const $b)
; Parameters ....: $a             - the array
;                  $i             - index of value 1
;                  $j             - index of value 2
; Return values .: -
; Author ........: AspirinJunkie
; =================================================================================================
Func __swap(ByRef $a, Const $i, Const $j)
    Local Const $t = $a[$i]
    $a[$i] = $a[$j]
    $a[$j] = $t
EndFunc   ;==>__swap

; #FUNCTION# ======================================================================================
; Name ..........: __PartitionHoare
; Description ...: helper function for partitioning inside the quicksort-function
;                  there exists several algorithms for this.
; Syntax ........: __PartitionHoare(ByRef $a_Array, Const $i_Min, Const $i_Max, Const $p_Value, Const $cb_Func)
; Parameters ....: $a_Array       - the array
;                  $i_Min         - the start index for the partitioning range in the array
;                  $i_Max         - the end index for the partitioning range in the array
;                  $p_Value       - the value of the pivot-element
;                  $cb_Func       - function variable points to a function of a form "[1|0|-1] function(value, value)"
;                                   the function compares two values a,and b for a>b/a=b/a<b
;                                   an example is the AutoIt-Function "StringCompare".
; Return values .: the position of the pivot-element
; Author ........: AspirinJunkie
; =================================================================================================
Func __PartitionHoare(ByRef $a_Array, Const $i_Min, Const $i_Max, Const $p_Value, Const $cb_Func)
    ; divide the array in two separate lists in dependency of the pivot-element
    ; there are several algorithms to reach this (here used: "Quickselect / Hoare's selection algorithm" - see "Lomuto's algorithm")
    Local $i = $i_Min - 1
    Local $j = $i_Max + 1
    Local $t
    Do
        ; start from right and go left until the next element which is smaller than pivot:
        Do
            $j -= 1
        Until $cb_Func($a_Array[$j], $p_Value) = -1 Or $j = $i_Min
        ; start from left and go right until the next element which is greater than pivot:
        Do
            $i += 1
        Until $cb_Func($a_Array[$i], $p_Value) = 1 Or $i = $i_Max
        ; swap if elements are on the wrong side of the lists
        If $i < $j Then
            $t = $a_Array[$j]
            $a_Array[$j] = $a_Array[$i]
            $a_Array[$i] = $t
        EndIf
    Until $i >= $j

    ; swap with pivot-element if pivot is at the wrong list-side:
    If $cb_Func($a_Array[$i], $p_Value) = 1 Then
        $a_Array[$i_Max] = $a_Array[$i]
        $a_Array[$i] = $p_Value
    EndIf
    Return $i
EndFunc   ;==>__PartitionHoare

; #FUNCTION# ======================================================================================
; Name ..........: __cb_NormalComparison
; Description ...: helper function which provides a standard AutoIt-comparison for flexible sorting
; Syntax ........: __cb_NormalComparison(ByRef Const $a, ByRef Const $b)
; Parameters ....: $a             - the first value
;                  $b             - the second value
; Return values .:  1: a > b
;                   0: a = b
;                  -1: a < b
; Author ........: AspirinJunkie
; =================================================================================================
Func __cb_NormalComparison(ByRef Const $a, ByRef Const $b)
    Return $a > $b ? 1 : $a = $b ? 0 : -1
EndFunc   ;==>__cb_NormalComparison

 

Edited by AspirinJunkie
Link to comment
Share on other sites

Correct... sorry  :)

Edit
I see, StringFormat is effectively the good way

#include<array.au3>
$sString = "B|A|1234567890123|C|100111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"

$a = StringSplit($sString, "|")
Local $b[$a[0]+1][2]
For $i = 1 To $a[0]
   $b[$i][0] = $a[$i]
   $b[$i][1] = StringFormat("%010d", StringLen($a[$i])) &"_"& $a[$i]
Next
;_ArrayDisplay($b)
_ArraySort($b, 0, 1, 0, 1)
For $i = 1 To $a[0]
   $a[$i] = $b[$i][0]
Next
_ArrayDisplay($a)

 

Edited by mikell
Link to comment
Share on other sites

@mikell:

UEZ, I'm not sure that _ArrayUnique is appropriate as we don't know if the OP wants to keep duplicates or not
Anyway if duplicates exist then using _ArrayUnique will change the count at index 0 and my code will need to be revised  ;)
And if a row contains the same value as the count in row 0 then the whole thing becomes terrible  :)

How to Sort a array by string length ?
String: B|A|A|C|C|AB|DDD|BAAS|003|100|02  ----TO->  A|B|C|02|AB|003|100|DDD|BAAS

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

UEZ has good eyes !

It's also possible (and faster) to use the 2nd loop to make the array uniq :

#include <array.au3>
$sString = "B|A|A|C|C|AB|DDD|BAAS|003|100|02"

$a = StringSplit($sString, "|", 2)
Local $b[UBound($a)][2]
For $i = 0 To UBound($a) - 1
   $b[$i][0] = $a[$i]
   $b[$i][1] = StringFormat("%010d", StringLen($a[$i])) &"_"& $a[$i]
Next

_ArraySort($b, 0, 0, 0, 1)

Local $sPrevious, $iIndex = 0
For $i = 0 To UBound($b) - 1
    If $b[$i][0] <> $sPrevious Then
        $sPrevious = $b[$i][0]
        $a[$iIndex] = $b[$i][0]
        $iIndex += 1
    EndIf
Next

Redim $a[$iIndex]

_ArrayDisplay($a)

 

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

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...