Jump to content
Sign in to follow this  
benners

Sorting an array

Recommended Posts

benners

I have written a function that reads a specified table from an msi file. The function uses sql syntax to query the table and uses the "Order By" keyword to sort the results.

Being Microsoft, things are sorted differently. The columns that are integer based sort as expected but the string columns don't sort. After wasting time trying different things I came across some info on MS site

The ORDER BY clause is optional and causes an initial delay during sorting. Ordering by strings will group identical strings together, but it will not alphabetize the strings.

WTF is the point in that I don't know but this leads me to my question.

If the column is an integer I can let SQL sort it. If not I can sort the array by getting the column index of the specified column. Is there a way to check a string to see if it is a number or, (before StringIsInt is mentioned) a number in version format such as 1.0.0.0?.

I am thinking of letting _ArraySort do all the sorting once I establish which column in the array is the one to sort but was looking for other solutions.

I have attached the files with code and an msi.

Thanks

Test.7z

Share this post


Link to post
Share on other sites
LurchMan

_ArraySort () will be your best option in my opinion.


Dating a girl is just like writing software. Everything's going to work just fine in the testing lab (dating), but as soon as you have contract with a customer (marriage), then your program (life) is going to be facing new situations you never expected. You'll be forced to patch the code (admit you're wrong) and then the code (wife) will just end up all bloated and unmaintainable in the end.

Share this post


Link to post
Share on other sites
benners

_ArraySort () will be your best option in my opinion.

That's another problem, >_< . The SQL will sort the integer columns, ascending order but not alphabetize the string column. The _ArraySort will alphabetize the strings column, but the integer columns are just added as strings so get sorted as them i.e 1 11 100 2 20 3 30 etc.

I shall have to try and use StringRegExpReplace and StringIsInt to check the string and sort with the best method. I'll check the string like

StringIsInt(StringRegExpReplace('0x400', '\D', ''))
this should return 1 for number strings and 0 for actual strings

Cheers

Share this post


Link to post
Share on other sites
MilesAhead

I did a modification of _ArraySort that allows you to pass in the sorting function of your own design. See the

It's along the lines of C Qsort where you passed in a pointer to the sort function. Only in AutoIt you pass in the string that's the function name for use by Call().

Share this post


Link to post
Share on other sites
czardas

I'm not quite sure what the problem is here.

#include <Array.au3>

Global $aArray[7] = ["1","11","100","2","20","3","30"] ; Array Contains strings

_ArraySort($aArray) ; Sort strings
_ArrayDisplay($aArray, "Alphanumeric")

For $i = 0 To 6 ; Convert all string values to numbers
    $aArray[$i] = Number($aArray[$i])
Next

_ArraySort($aArray) ; Sort Numbers
_ArrayDisplay($aArray, "Numeric Sorting")
  • Like 1

Share this post


Link to post
Share on other sites
kylomas

czardas,

Thank You, this solves a prob thats been bugging me for some time. K.I.S.S. is the order of the day!

kylomas


Forum Rules         Procedure for posting code

"I like pigs.  Dogs look up to us.  Cats look down on us.  Pigs treat us as equals."

- Sir Winston Churchill

Share this post


Link to post
Share on other sites
MilesAhead

I'm not quite sure what the problem is here.

#include <Array.au3>

Global $aArray[7] = ["1","11","100","2","20","3","30"] ; Array Contains strings

_ArraySort($aArray) ; Sort strings
_ArrayDisplay($aArray, "Alphanumeric")

For $i = 0 To 6 ; Convert all string values to numbers
$aArray[$i] = Number($aArray[$i])
Next

_ArraySort($aArray) ; Sort Numbers
_ArrayDisplay($aArray, "Numeric Sorting")

Conversion to number works in the above format. But strings in typical file version format such as 1.0.0.0 you would have to traverse the array doing some conversion. With a custom comparison function the orginal data is not changed. It's just compared by a function that knows which of the two values is greater, less than, or equal. It's hardly revolutionary. C standard library came out what, in the late 80s or early 90s? Edited by MilesAhead

Share this post


Link to post
Share on other sites
czardas

Conversion to number works in the above format. But strings in typical file version format such as 1.0.0.0 you would have to traverse the array doing some conversion.

How true!

#include <Array.au3>

_Test()

Func _Test()
    Local $aArray[4] = ["12.5.3.09","7.34.45.1","7.2.0.0","12.5.0.143"]
    _ArrayDisplay($aArray, "Before")
    _VersionSort($aArray)
    _ArrayDisplay($aArray, "After")
EndFunc

Func _VersionSort(ByRef $aVersions, $iStart = 0)
    If Not IsArray($aVersions) Or UBound($aVersions, 0) > 1 Then Return SetError(1) ; Not a 1D array.
    If Not IsInt($iStart) Then Return SetError(2) ; This parameter needs to be an integer.

    Local $iEnd = UBound($aVersions) -1
    If $iStart < 0 Or $iStart > $iEnd Then Return SetError(3) ; Start parameters out of range.

    Local $aSplit, $iDelimiters = 0
    For $i = 1 To StringLen($aVersions[$iStart])
        If StringMid($aVersions[$iStart], $i, 1) = "." Then $iDelimiters += 1
    Next

    For $i = $iStart To $iEnd
        $aSplit = StringSplit($aVersions[$i], ".")
        If $iDelimiters <> $aSplit[0] -1 Then Return SetError(4) ; Inconsistant version format.

        $aVersions[$i] = ""
        For $j = 1 To $aSplit[0]
            If StringLen($aSplit[$j]) > 5 Then Return SetError(5) ; Max five figures between dots
            $aVersions[$i] &= StringFormat("%05d", $aSplit[$j])
        Next
    Next
    _ArraySort($aVersions)

    For $i = $iStart To $iEnd
        $aSplit = _StringEqualSplit($aVersions[$i], 5)

        $aVersions[$i] = ""
        For $j = 0 To $iDelimiters -1
            $aVersions[$i] &= Number($aSplit[$j]) & "."
        Next
        $aVersions[$i] &= Number($aSplit[$j])
    Next
EndFunc

Func _StringEqualSplit($sString, $iNumChars)
    If Not IsString($sString) Or $sString = "" Then Return SetError(1, 0, 0)
    If Not IsInt($iNumChars) Or $iNumChars < 1 Then Return SetError(2, 0, 0)
    Return StringRegExp($sString, "(?s).{1," & $iNumChars & "}", 3)
EndFunc

Works for up to 5 digits between dots. version xxxxx.xxxxx.xxxxx.xxxxx.xxxxx.xxxxx etc... It's only working on a 1D array ATM, but that can easily be changed.

Edit: Minor Adjustments

Edited by czardas

Share this post


Link to post
Share on other sites
MilesAhead

How true!

I think you miss the point. With your method you have to traverse the array, transform all the values, sort, then once you have the order, traverse again the return the contents to the original values if the source data needs to be preserved.

With a custom compare function, you don't alter the orignal data. Only load local variales in the compare function.

I don't understand why I have to arge stuff that's in every computer science textbook. Notice I still have the orginal array contents. Only the order of elements has changed.

Dim $testArray[8] = ["2.54.0.2", "1.0.0.0", "7.25.11.4", "2.1.0.0", "3.4.15.1", "1.1.0.0", "7.7.7.7", "4.4.4.4"]
$msg = ""
For $x = 0 To 7
    $msg &= $testArray[$x] & @CRLF
Next
MsgBox(0x1040, "Test", $msg)
_ArraySort($testArray, 0, 0, 0, 0, "CustomCompare")
$msg = ""
For $x = 0 To 7
    $msg &= $testArray[$x] & @CRLF
Next
MsgBox(0x1040, "Test", $msg)

Func CustomCompare($left, $right)
    Local $l = StringSplit($left, ".")
    Local $r = StringSplit($right, ".")
    For $x = 1 To 4
        $l[$x] = Int($l[$x])
        $r[$x] = Int($r[$x])
    Next
    For $x = 1 To 4
        If $l[$x] < $r[$x] Then
            Return -1
        ElseIf $l[$x] = $r[$x] Then
            ContinueLoop
        Else
            Return 1
        EndIf
    Next
EndFunc   ;==>CustomCompare

; #FUNCTION# ====================================================================================================================
; Name...........: _ArraySort
; Description ...: Sort a 1D or 2D array on a specific index using the quicksort/insertionsort algorithms.
; Syntax.........: _ArraySort(ByRef $avArray[, $iDescending = 0[, $iStart = 0[, $iEnd = 0[, $iSubItem = 0]]]])
; Parameters ....: $avArray     - Array to sort
;                  $iDescending - [optional] If set to 1, sort descendingly
;                  $iStart      - [optional] Index of array to start sorting at
;                  $iEnd        - [optional] Index of array to stop sorting at
;                  $iSubItem    - [optional] Sub-index to sort on in 2D arrays
; Return values .: Success - 1
;                  Failure - 0, sets @error:
;                  |1 - $avArray is not an array
;                  |2 - $iStart is greater than $iEnd
;                  |3 - $iSubItem is greater than subitem count
;                  |4 - $avArray has too many dimensions
; Author ........: Jos van der Zande <jdeb at autoitscript dot com>
; Modified.......: LazyCoder - added $iSubItem option, Tylo - implemented stable QuickSort algo,
; Modified.......: Jos van der Zande - changed logic to correctly Sort arrays with mixed Values and Strings,
; Modified.......: Ultima - major optimization, code cleanup, removed $i_Dim parameter
; Modified.......: MilesAhead - added user defined $iCompare for generalized qsort()
; Remarks .......:
; Related .......:
; Link ..........:
; Example .......: Yes
; ===============================================================================================================================
Func _ArraySort(ByRef $avArray, $iDescending = 0, $iStart = 0, $iEnd = 0, $iSubItem = 0, $iCompare = "")
    If Not IsArray($avArray) Then Return SetError(1, 0, 0)

    Local $iUBound = UBound($avArray) - 1

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

    ; Sort
    Switch UBound($avArray, 0)
        Case 1
            __ArrayQuickSort1D($avArray, $iStart, $iEnd, $iCompare)
            If $iDescending Then _ArrayReverse($avArray, $iStart, $iEnd)
        Case 2
            Local $iSubMax = UBound($avArray, 2) - 1
            If $iSubItem > $iSubMax Then Return SetError(3, 0, 0)

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

            __ArrayQuickSort2D($avArray, $iDescending, $iStart, $iEnd, $iSubItem, $iSubMax, $iCompare)
        Case Else
            Return SetError(4, 0, 0)
    EndSwitch

    Return 1
EndFunc   ;==>_ArraySort

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name...........: __ArrayQuickSort1D
; Description ...: Helper function for sorting 1D arrays
; Syntax.........: __ArrayQuickSort1D(ByRef $avArray, ByRef $iStart, ByRef $iEnd)
; Parameters ....: $avArray - 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.......: MilesAhead - added user defined $iCompare for generalized qsort()
; Remarks .......: For Internal Use Only
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func __ArrayQuickSort1D(ByRef $avArray, ByRef $iStart, ByRef $iEnd, $iCompare = "")
    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 = $avArray[$i]

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

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

    ; QuickSort
    Local $l = $iStart, $r = $iEnd, $vPivot = $avArray[Int(($iStart + $iEnd) / 2)], $fNum = IsNumber($vPivot)
    Do
        If $iCompare Then
            While (Call($iCompare, $avArray[$l], $vPivot) < 0)
                $l += 1
            WEnd
            ; While $avArray[$R] > $vPivot
            While (Call($iCompare, $avArray[$r], $vPivot) > 0)
                $r -= 1
            WEnd
        ElseIf $fNum Then
            ; While $avArray[$L] < $vPivot
            While ($avArray[$l] < $vPivot And IsNumber($avArray[$l])) Or (Not IsNumber($avArray[$l]) And StringCompare($avArray[$l], $vPivot) < 0)
                $l += 1
            WEnd
            ; While $avArray[$R] > $vPivot
            While ($avArray[$r] > $vPivot And IsNumber($avArray[$r])) Or (Not IsNumber($avArray[$r]) And StringCompare($avArray[$r], $vPivot) > 0)
                $r -= 1
            WEnd
        Else
            While (StringCompare($avArray[$l], $vPivot) < 0)
                $l += 1
            WEnd
            While (StringCompare($avArray[$r], $vPivot) > 0)
                $r -= 1
            WEnd
        EndIf

        ; Swap
        If $l <= $r Then
            $vTmp = $avArray[$l]
            $avArray[$l] = $avArray[$r]
            $avArray[$r] = $vTmp
            $l += 1
            $r -= 1
        EndIf
    Until $l > $r

    __ArrayQuickSort1D($avArray, $iStart, $r, $iCompare)
    __ArrayQuickSort1D($avArray, $l, $iEnd, $iCompare)
EndFunc   ;==>__ArrayQuickSort1D

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name...........: __ArrayQuickSort2D
; Description ...: Helper function for sorting 2D arrays
; Syntax.........: __ArrayQuickSort2D(ByRef $avArray, ByRef $iStep, ByRef $iStart, ByRef $iEnd, ByRef $iSubItem, ByRef $iSubMax)
; Parameters ....: $avArray  - 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
; Return values .: None
; Author ........: Jos van der Zande, LazyCoder, Tylo, Ultima
; Modified.......: MilesAhead - added user defined $iCompare for generalized qsort()
; Remarks .......: For Internal Use Only
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func __ArrayQuickSort2D(ByRef $avArray, ByRef $iStep, ByRef $iStart, ByRef $iEnd, ByRef $iSubItem, ByRef $iSubMax, $iCompare = "")
    If $iEnd <= $iStart Then Return

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

        ; Swap
        If $l <= $r Then
            For $i = 0 To $iSubMax
                $vTmp = $avArray[$l][$i]
                $avArray[$l][$i] = $avArray[$r][$i]
                $avArray[$r][$i] = $vTmp
            Next
            $l += 1
            $r -= 1
        EndIf
    Until $l > $r

    __ArrayQuickSort2D($avArray, $iStep, $iStart, $r, $iSubItem, $iSubMax, $iCompare)
    __ArrayQuickSort2D($avArray, $iStep, $l, $iEnd, $iSubItem, $iSubMax, $iCompare)
EndFunc   ;==>__ArrayQuickSort2D

; #FUNCTION# ====================================================================================================================
; Name...........: _ArraySwap
; Description ...: Swaps two items.
; Syntax.........: _ArraySwap(ByRef $vItem1, ByRef $vItem2)
; Parameters ....: $vItem1 - First item to swap
;                  $vItem2 - Second item to swap
; Return values .: None.
; Author ........: David Nuttall <danuttall at rocketmail dot com>
; Modified.......: Ultima - minor optimization
; Remarks .......: This function swaps the two items in place, since they're passed by reference. Regular, non-array variables can also be swapped by this function.
; Related .......: _ArrayReverse
; Link ..........:
; Example .......: Yes
; ===============================================================================================================================
Func _ArraySwap(ByRef $vItem1, ByRef $vItem2)
    Local $vTmp = $vItem1
    $vItem1 = $vItem2
    $vItem2 = $vTmp
EndFunc   ;==>_ArraySwap

; #FUNCTION# ====================================================================================================================
; Name...........: _ArrayReverse
; Description ...: Takes the given array and reverses the order in which the elements appear in the array.
; Syntax.........: _ArrayReverse(ByRef $avArray[, $iStart = 0[, $iEnd = 0]])
; Parameters ....: $avArray - Array to modify
;                  $iStart  - [optional] Index of array to start modifying at
;                  $iEnd    - [optional] Index of array to stop modifying at
; Return values .: Success - 1
;                  Failure - 0, sets @error:
;                  |1 - $avArray is not an array
;                  |2 - $iStart is greater than $iEnd
;                  |3 - $avArray is not a 1 dimensional array
; Author ........: Brian Keene
; Modified.......: Jos van der Zande <jdeb at autoitscript dot com> -  added $iStart parameter and logic, Tylo - added $iEnd parameter and rewrote it for speed, Ultima - code cleanup, minor optimization
; Remarks .......:
; Related .......: _ArraySwap
; Link ..........:
; Example .......: Yes
; ===============================================================================================================================
Func _ArrayReverse(ByRef $avArray, $iStart = 0, $iEnd = 0)
    If Not IsArray($avArray) Then Return SetError(1, 0, 0)
    If UBound($avArray, 0) <> 1 Then Return SetError(3, 0, 0)

    Local $vTmp, $iUBound = UBound($avArray) - 1

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

    ; Reverse
    For $i = $iStart To Int(($iStart + $iEnd - 1) / 2)
        $vTmp = $avArray[$i]
        $avArray[$i] = $avArray[$iEnd]
        $avArray[$iEnd] = $vTmp
        $iEnd -= 1
    Next

    Return 1
EndFunc   ;==>_ArrayReverse

Share this post


Link to post
Share on other sites
czardas

I don't understand why I have to arge stuff that's in every computer science textbook.

Hold your horses there a minute. Nobody makes you arge stuff, whatever that means. The method I invented may not be the most efficient and I suspected all along that it probably wouldn't be. judging by what I can tell, the method you have posted will also require multiple passes using qsort.

Edit

I just did a speed comparison. Running both methods for 1000 iterations using your example array gave the following results.

string conversion method 943.359561061532 ms

qsort method 930.61492452253

So there's a 13 ms improvement with the text book method. I can live with that. :) I could probably lose those 13 ms with more accurate testing, but then the tests would need to be tried on different sized arrays.

Edited by czardas

Share this post


Link to post
Share on other sites
MilesAhead

Your code works. The benchmark is not the point. The point is, the array methods presumably have already been debugged since they have been around a long time. With the comparision injection method, you only have to implement and debug a comparison function. Since we assume for the moment that the _ArraySort function doesn't scramble the data since it's debugged, then there's less risk of scrambling the array contents. Traversing an array, changing it, sorting it, then changing it back seems more error prone to me. More stuff to debug.

The point of gerneralized methods is programmer efficiency more than run-time efficiency. I think it took me about 2 minutes to pull the CustomCompare out of my head. The passing in a custom compare technique is not for every situtation. But it's definitely a reusable tool. That's why it's in stuff like standardized libraries. But just like them, C++ "containers" are not always the answer. But they are there if you don't mind throwing in the hitchen sink to save development time.

I'm not saying your code is broken. Only that the "pass in the custom cmpare function" technique is resuable. Good for the toolbox.

I've never seen a board so resistant to sorting algorithm implementations as here. If you look at _ArraySort() it's an implementation of QuickSort. These algorithms have been around for decades and are proven to work well for the general case. Back in the 80s someone might have argued that passing in a pointer to a function in C was slower than directly calling it. Now peope would laugh since the machines are so fast. One extra defreference might mean something with real-time applications like if you are a passenger on a mach 4 rocket. But I can't think of much else that applies.

Edited by MilesAhead

Share this post


Link to post
Share on other sites
czardas

Your code works. The benchmark is not the point. The point is, the array methods presumably have already been debugged since they have been around a long time. With the comparision injection method, you only have to implement and debug a comparison function. Since we assume for the moment that the _ArraySort function doesn't scramble the data since it's debugged, then there's less risk of scrambling the array contents. Traversing an array, changing it, sorting it, then changing it back seems more error prone to me. More stuff to debug.

The point of gerneralized methods is programmer efficiency more than run-time efficiency. I think it took me about 2 minutes to pull the CustomCompare out of my head. The passing in a custom compare technique is not for every situtation. But it's definitely a reusable tool. That's why it's in stuff like standardized libraries. But just like them, C++ "containers" are not always the answer. But they are there if you don't mind throwing in the hitchen sink to save development time.

I'm not saying your code is broken. Only that the "pass in the custom cmpare function" technique is resuable. Good for the toolbox.

I've never seen a board so resistant to sorting algorithm implementations as here. If you look at _ArraySort() it's an implementation of QuickSort. These algorithms have been around for decades and are proven to work well for the general case. Back in the 80s someone might have argued that passing in a pointer to a function in C was slower than directly calling it. Now peope would laugh since the machines are so fast. One extra defreference might mean something with real-time applications like if you are a passenger on a mach 4 rocket. But I can't think of much else that applies.

The limitations of the above example code, which I put together in half an hour, are clear to me, and also easy to address. The advantages your method may have over mine are not apparent to me. There are some extra steps taken, but this is counterbalanced by less sort processing. I imagine your code, on the other hand, does more comparisons. but I don't quite see what's going on there.

Okay, so I didn't read all the text books, but can you convince me that what I wish to achieve with AutoIt is actually in a text book? Many have failed to convince me previously, simply bacause most of those pages are actually missing. There's a lot of cool stuff to learn, for sure; but some old habits die hard. Some of it is (well hmm) not so hot. Perhaps this is down to bad architectural design choices early on, but not in every case.

The most important thing for me is that I had fun solving this problem. I would have had less fun implementing a pre-made textbook solution.

Edited by czardas

Share this post


Link to post
Share on other sites
benners

Hi All,

Thanks for your replies.

@czardas

I had something similar to your #7 post when I knew specifically which column I was going to sort and what format the column was in, then I could specify if the column was such a column convert to numbers for the sort. I thought I would improve my function so that it automatically retrieved to format and acted accordingly. I will be altering and importing the arraytable so wanted to keep the format the same without an extra step of converting back to a string.

@MilesAhead

I tried your code and modified the CustomCompare function to combine your codes for strings and numbers as strings (i.e versions) for my needs

Func _CustomCompare($sLeft, $sRight)
If StringIsInt(StringReplace($sLeft, '.', '')) And StringIsInt(StringReplace($sRight, '.', '')) Then
Local $l = StringSplit($sLeft, ".")
Local $r = StringSplit($sRight, ".")
For $x = 1 To $l[0]
$l[$x] = Int($l[$x])
$r[$x] = Int($r[$x])
Next
For $x = 1 To $l[0]
If $l[$x] < $r[$x] Then
Return -1
ElseIf $l[$x] = $r[$x] Then
ContinueLoop
Else
Return 1
EndIf
Next
Else
Return StringCompare($sLeft, $sRight)
EndIf
EndFunc ;==>CustomCompare

Thanks again

Edited by benners

Share this post


Link to post
Share on other sites
kylomas

benners (F.Y.I.),

Here are three ways to remove periods ('.') from strings

#include <array.au3>

; remove periods with string functions

local $str = '24.333.1212.2.89765'
local $a10 = stringsplit($str,'.',2)
local $mynumber = _ArrayToString($a10,'')

ConsoleWrite($mynumber & @LF)

; remove periods with regexp

$mynumber = ''
$mynumber = stringregexpreplace($str,'\.','')

ConsoleWrite($mynumber & @LF)

; and of course the most obvious

$mynumber = ''
local $mynumber = stringreplace($str,'.','')

ConsoleWrite($mynumber & @LF)

kylomas

edit: spelling and added code

Edited by kylomas

Forum Rules         Procedure for posting code

"I like pigs.  Dogs look up to us.  Cats look down on us.  Pigs treat us as equals."

- Sir Winston Churchill

Share this post


Link to post
Share on other sites
guinness

Or

StringReplace()

Edit: Post number 7,777. Nice!

Edited by guinness

UDF List:

 
_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_ArrayFilter/_ArrayReduce_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 22/04/2018

Share this post


Link to post
Share on other sites
kylomas

@guinness,

Yes, I forgot the most obvious. It looks like the OP is processing the string by nodes, thus, the alternative methods.

kylomas


Forum Rules         Procedure for posting code

"I like pigs.  Dogs look up to us.  Cats look down on us.  Pigs treat us as equals."

- Sir Winston Churchill

Share this post


Link to post
Share on other sites
czardas

It was interesting to try this. Often different methods are suitable for specific circumstances. The weakness that I see with what I posted is that it is limited to equal numbers of dots per item and it returns the array ByRef. That's a potential problem if an error occurs during processing, and if no countermeasures are taken. Another potential problem, as MilesAhead did mention, was that some corruption is possible. The one thing I forgot was that someone might pass a string with preceeding zeros like this: 00010.012.0006.4.00222.00034 That string would indeed become corrupted with my current implementation.

I tend to use string functions a lot because they often have big speed advantages over array functions. I have received criticism for this previously, and while the assertions may sometimes be correct, I still like to see hard evidence and this isn't always forthcoming. Speed is a serious issue in some of my projects. One thing to note is that a delimitered numeric string is still a string, however you approach it.

At the end of the day, MileAhead may have had a point, but ruined it by calling me an idiot for not having read lots of text books; nor do I particularly like his implementation for several reasons: The example uses function names which are identical to the standard Array UDF, so I would need to change those if I were to use his code. The code contains no additional comments outside the header, so it's hard to understand. It would also help if AutoIt code tags had been used.

I'll refrain from saying any more.

Edited by czardas

Share this post


Link to post
Share on other sites
MilesAhead

Your opposition to standard sorting techniques where a comparison function is passed in is a position abandoned by just about everyone else decades ago. If you feel foolish for this opposition, I don;'t see that as my problem. The code I posted was a simple test stub to suggest a minor modification to the UDF in question to avoid just such scenarios as traversing arrays, modifying them, sorting and then removing the modification. Unless there's some constraint that mandates doing things that way, it seems rather tedioius and unnecessary to me.

But since I first posted the test code further searches turned up another poster suggesting much the same improvements back in 2007. For some reason there seems to be an irrational resistance that makes pursuing this and like topics pointless.

I don't understand the emotional component on display in this thread. It's like someone not liking me because they found out I once used HeapSort(). Really bizarre.

Share this post


Link to post
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
Sign in to follow this  

×