paulpmeier Posted November 7, 2020 Share Posted November 7, 2020 Example (AutoIt 3.3.14.5): #include <Array.au3> Global $aSortTest[6] = [Ptr(1), Ptr(2), Ptr(3), Ptr(10), Ptr(11), Ptr(12)] _ArrayDisplay($aSortTest) Have I missed something? Paul Link to comment Share on other sites More sharing options...
Dan_555 Posted November 7, 2020 Share Posted November 7, 2020 (edited) Looks normal to me. I guess that the Listview does not care or know of the hexadecimal numbers, so it is sorting it alphabetically. The first click is sorting and the second click is reversing the sorting order. Edited November 7, 2020 by Dan_555 Some of my script sourcecode Link to comment Share on other sites More sharing options...
Moderators Melba23 Posted November 7, 2020 Moderators Share Posted November 7, 2020 paulpmeier, _ArrayDisplay does indeed use simple alphabetical sorting for columns. If you were to use my GUIListViewEx UDF (the link is in my sig) you can use your own sort algorithm for user-defined columns. But that might be using a sledgehammer to crack a nut..... M23 Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind Open spoiler to see my UDFs: Spoiler ArrayMultiColSort ---- Sort arrays on multiple columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area Link to comment Share on other sites More sharing options...
paulpmeier Posted November 7, 2020 Author Share Posted November 7, 2020 Alphabetical order means for me numbers before letters. Paul. Link to comment Share on other sites More sharing options...
paulpmeier Posted November 7, 2020 Author Share Posted November 7, 2020 (edited) Melba, look at this string-array: #include <Array.au3> Global $aSortTest[5][2] = [["1", "01"], ["2", "02"], ["3", "03"], ["A", "0A"], ["B", "0B"]] _ArrayDisplay($aSortTest) The first column is sorted correctly, the second is not. Paul Edited November 7, 2020 by paulpmeier Link to comment Share on other sites More sharing options...
paulpmeier Posted November 7, 2020 Author Share Posted November 7, 2020 (edited) If I replace 'StrCmpLogicalW' with 'StrCmpCW' in line 613 in ArrayDisplayInternals.au3, the sorting is correct. Pointers and handles can also be sorted this way. But numeric values are then also sorted alphabetically. Paul Edited November 7, 2020 by paulpmeier Link to comment Share on other sites More sharing options...
pixelsearch Posted November 7, 2020 Share Posted November 7, 2020 Hi Paul, Long explanation coming Your comments can be explained, because _ArrayDisplay always uses a comparison type based on Windows API StrCmpLogical, as you can see in the help file, topic _GUICtrlListView_RegisterSortCallBack, where the 2nd parameter (= 2) means "Use Windows API StrCmpLogical" This corresponds to line #448 in ArrayDisplayInternals.au3 (version 3.3.14.5) __ArrayDisplay_RegisterSortCallBack($idListView, 2, True, "__ArrayDisplay_SortCallBack") When this 2nd parameter = 2, then the sort isn't done in a pure alphabetically (ascii) way because "Digits in the strings are considered as numerical content rather than text", as explained on the MSDN web page. A note from Wikipedia concerning this "natural sort" : Natural sort order is an ordering of strings in alphabetical order, except that multi-digit numbers are treated atomically, i.e., as if they were a single character. Natural sort order has been promoted as being more human-friendly ("natural") than the machine-oriented pure alphabetical order. It could explain why, in your very last diagram, when the 1st character in col1 is a digit (0 for all rows), then "01" means 1, "02" => 2, "03" => 3, while "0A" and "0B" equal to 0 (because their 1st 0 is not followed by a digit), which could explain why "0A" and "0B" are placed on the 1st two lines, in an ascending sort using StrCmpLogical Your other column (col0) doesn't have the same character at 1st place but a simple "1", "2", "3", "A", "B" In this case, I guess that StrCmpLogical immediately sorts the letters after the numbers when the sort is ascending, after comparing for example "3" to "A" and finding that the ascii code for "A" is higher than the ascii code for "3", which will result in an ascending sort like this : 1 - 2 - 3 - A - B So it seems that both columns have their contents treated very differently, depending on the fact that a digit is found or not... at least this is the only explanation I can find. If anyone could confirm or disprove this explanation, please do not hesitate, thanks I find it hard to solve your issue, because changing permanently the 2nd parameter in line #448 (from 2 to 0, meaning you want a pure String comparison) will solve your case... but will bring issues when you have numerals to compare. Same if you change it permanently from 2 to 1 ("String treated as number") The only (bad) way I found to sort correctly your pointers & handles (or any hex number starting with 0x) in your 1st diagram is : * Line 448 : 2nd parameter = 1 (String treated as number) * Lines 596 to 600, added a test on "0x" : If $__g_aArrayDisplay_SortInfo[8] = 1 Then ; force Treat as Number if possible If StringIsFloat($sVal1) Or StringIsInt($sVal1) Or StringLeft($sVal1, 2) = "0x" Then $sVal1 = Number($sVal1) If StringIsFloat($sVal2) Or StringIsInt($sVal2) Or StringLeft($sVal2, 2) = "0x" Then $sVal2 = Number($sVal2) EndIf Then an array like this one... Global $aSortTest[6][1] = [["0x0B"], ["0x020"], ["0x0C"], ["0x03"], ["0x01"], ["0x0A"]] ...would correctly be sorted (ascending) This is the reason why, in my CSV file editor, a choice is given to the user, before each sort (by right-clicking a header column) so the user chooses the kind of sort he wants, instead of being stuck with a "forced" way of sorting : This required of course to register the sort, do the sort, then unregister it each time the user wants to sort, no big deal. So, if this is really important to you, I guess you'll have to tweak a bit your own version of ArrayDisplayInternals.au3 Good luck Zedna and paulpmeier 1 1 Link to comment Share on other sites More sharing options...
paulpmeier Posted November 8, 2020 Author Share Posted November 8, 2020 Hello pixelsearch, thanks for the detailed explanation. The reference to the functioning of natural sorting explains the behavior of the sort function in _ArrayDisplay. Your "hack" is a great help to me. Thanks a lot. Paul Link to comment Share on other sites More sharing options...
pixelsearch Posted November 19, 2020 Share Posted November 19, 2020 (edited) Paul, you're welcome I just tried a little context menu inside _ArrayDisplay to choose the way of sorting, it seems to give good results. Lets have a look at the results first, then the personal modifications made to ArrayDisplayInternals.au3 code. Test script : #include <Array.au3> Global $aSortTest[12][1] = [["0x0B"], ["0x020"], ["0x0C"], ["0x03"], ["0x01"], ["0x0A"], _ ["0x0FF"], ["0x030"], ["0x04"], ["0x0E"], ["0x0D"], ["0x02"]] _ArrayDisplay($aSortTest) The pic above is your case : you need to choose the Numeric sort to be able to sort correctly a column of handles, pointers or any hex number starting with 0x In the pic above, we see it's very different if we want to sort the "Row" column. It needs a Natural sort to be correctly sorted because it is composed of letters ("Row ") and numbers. I suppose this is the main reason why Dev's forced the Natural sort in ArrayDisplay (when the default way of sorting in GuiListView.au3 is a Numeric sort +++) . Maybe if this Row column had only been composed of numeric elements (like the Excel column numerotation) then Dev's would have kept the original default Numeric sort found in GuiListView.au3... maybe Here is the code change in _ArrayDisplay, it's certainly optimizable but it may give ideas to users who want to tweak their own version of ArrayDisplayInternals.au3 1) Creation of a context menu associated to the listview (lines to be inserted after the listview has been created) ; Create ListView context menu (so user can choose the kind of sort he wants) Local $idContext_Menu = GUICtrlCreateContextMenu($idListView) Local $idContext_0 = GUICtrlCreateMenuItem("String sort", $idContext_Menu) Local $idContext_1 = GUICtrlCreateMenuItem("Numeric sort", $idContext_Menu) Local $idContext_2 = GUICtrlCreateMenuItem("Natural sort <<<", $idContext_Menu) ; default (keeps compatibility with old scripts) Local $iSort_Desired = 2, $iSort_Actual = -1 ; 0 = String sort, 1 = Numeric Sort, 2 = Natural sort 2) Inside the While... Wend loop, 3 Case added (Case $idContext_0 ... Case $idContext_1 ... Case $idContext_2) Case $idContext_0 ; String sort (chosen by user in context menu) $iSort_Desired = 0 GUICtrlSetData($idContext_0, "String sort <<<") GUICtrlSetData($idContext_1, "Numeric sort") GUICtrlSetData($idContext_2, "Natural sort") Case $idContext_1 ; Numeric sort (chosen by user in context menu) $iSort_Desired = 1 GUICtrlSetData($idContext_0, "String sort") GUICtrlSetData($idContext_1, "Numeric sort <<<") GUICtrlSetData($idContext_2, "Natural sort") Case $idContext_2 ; Natural sort (chosen by user in context menu) $iSort_Desired = 2 GUICtrlSetData($idContext_0, "String sort") GUICtrlSetData($idContext_1, "Numeric sort") GUICtrlSetData($idContext_2, "Natural sort <<<") 3) Inside the While... Wend loop, modifications made in Case $idListView code : Case $idListView ; left click on listview column header => sort If $iSort_Desired <> $iSort_Actual Then ; note that $iSort_Actual = -1 at 1st passage DllCallbackFree($__g_aArrayDisplay_SortInfo[2]) __ArrayDisplay_RegisterSortCallBack($idListView, $iSort_Desired, True, "__ArrayDisplay_SortCallBack") $iSort_Actual = $iSort_Desired EndIf ; Kick off the sort callback __ArrayDisplay_SortItems($idListView, GUICtrlGetState($idListView)) 4) Original line #448 to be deleted because it was placed outside the While... Wend loop (eventual sorts are now registered / unregistered within the While... Wend loop, inside the Case $idListView as shown just above) #448 __ArrayDisplay_RegisterSortCallBack($idListView, 2, True, "__ArrayDisplay_SortCallBack") 5) Original lines #598 et #599 amended to convert strings to numbers when "0x" is found at the beginning of the string. These 2 lines are originally already included inside a test where the user choosed voluntarily a Numeric sort. In case you're afraid that a string starting with "0x" is not a valid hex number, then you can use guinness _IsHex() function, found in this link, where he checks the string with a Regex expression '^0x[[:xdigit:]]+$' If StringIsInt($sVal1) Or StringIsFloat($sVal1) Or StringLeft($sVal1, 2) = "0x" Then $sVal1 = Number($sVal1) If StringIsInt($sVal2) Or StringIsFloat($sVal2) Or StringLeft($sVal2, 2) = "0x" Then $sVal2 = Number($sVal2) 6) As discussed in this link , original line #560 has to be moved "just below" the 2 lines below it. This will make the sort arrow visible when you click on a column header in ArrayDisplay It will also correct the value found in the variable $hHeader and in the array element $__g_aArrayDisplay_SortInfo[10] This is fixed in trac ticket #3791 ;~ Local Const $LVM_GETHEADER = (0x1000 + 31) ; 0x101F Local $hHeader = HWnd(GUICtrlSendMsg($hWnd, 0x101F, 0, 0)) If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd) 7) Finally, DllCallbackFree should be added before both GuiDelete($hGUI) as discussed in this link : DllCallbackFree($__g_aArrayDisplay_SortInfo[2]) GUIDelete($hGUI) Hope I didn't miss something important. If Mods think it's better to upload (or not) the complete amended ArrayDisplayInternals.au3 somewhere (for test or whatever) I'll do it. I just thought it could be a bit dangerous to share it fully here, because all these tweaks are very new. Dinner time Edit : 11/21/2020 Parts 1) and 2) in the code above can be scripted differently, to get a shorter code based on menu radioitems. 1) Creation of a context menu associated to the listview (lines to be inserted after the listview has been created) ; Create ListView context menu (so user can choose the kind of sort he wants) Local $idContext_Menu = GUICtrlCreateContextMenu($idListView) Local $idContext_0 = GUICtrlCreateMenuItem("String sort", $idContext_Menu, -1, 1) ; menuradioitem Local $idContext_1 = GUICtrlCreateMenuItem("Numeric sort", $idContext_Menu, -1, 1) ; menuradioitem Local $idContext_2 = GUICtrlCreateMenuItem("Natural sort", $idContext_Menu, -1, 1) ; menuradioitem GUICtrlSetState(-1, 1) ; $GUI_CHECKED = 1 (Natural sort is the default, to keep compatibility) Local $iSort_Desired = 2, $iSort_Actual = -1 ; 0 = String sort, 1 = Numeric Sort, 2 = Natural sort 2) Inside the While... Wend loop, 3 Case added (Case $idContext_0 ... Case $idContext_1 ... Case $idContext_2) Case $idContext_0 ; String sort (chosen by user in context menu) $iSort_Desired = 0 Case $idContext_1 ; Numeric sort (chosen by user in context menu) $iSort_Desired = 1 Case $idContext_2 ; Natural sort (chosen by user in context menu) $iSort_Desired = 2 This will display a context menu based on menu radio items : Thanks to lemony & Malkey in this post from 2008 (I discovered it right now !) Edited November 21, 2020 by pixelsearch 11/21/2020 : changes done in parts 1) and 2) of the code GokAy, Zedna, Musashi and 1 other 2 2 Link to comment Share on other sites More sharing options...
JockoDundee Posted November 20, 2020 Share Posted November 20, 2020 On 11/7/2020 at 2:51 PM, pixelsearch said: So it seems that both columns have their contents treated very differently, depending on the fact that a digit is found or not... at least this is the only explanation I can find. It certainly could be, on the other hand would this mean that a big sort might get to the last element in the first pass before deciding to do it a different way? Yeesh! Code hard, but don’t hard code... Link to comment Share on other sites More sharing options...
pixelsearch Posted November 20, 2020 Share Posted November 20, 2020 Hi JockoDundee Not sure of that, I think that items to compare are passed 2 by 2 to the sort callback function. Let's detail an example : #include <Array.au3> #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> Example() Func Example() Local $aSortTest[6] = ["2", "A", "C", "B", "3", "1"] Local $hGUI = GUICreate("Test", 200, 100, -1, 80, -1, $WS_EX_TOPMOST) Local $idButton = GUICtrlCreateButton("Click me", 60, 40, 80, 30) GUISetState(@SW_SHOW) While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop Case $idButton GUISetState(@SW_DISABLE, $hGUI) _ArrayDisplay($aSortTest) ; Now click only once on the column header to sort the column <=============== GUISetState(@SW_ENABLE, $hGUI) EndSwitch WEnd GUIDelete($hGUI) EndFunc ;==>Example We need to add a couple of lines inside the sort callback function, to display in the Console what happens when the function will be called several times during the sort process : Func __ArrayDisplay_SortCallBack($nItem1, $nItem2, $hWnd) Local Static $iCounter = 0 $iCounter += 1 ConsoleWrite("$iCounter = " & $iCounter & " $nItem1 = " & $nItem1 & " $nItem2 = " & $nItem2 & @lf) ... Results : When the user clicks on a column header, then $LVM_SORTITEMSEX message is processed and here is what we can read on its MSDN web page : LVM_SORTITEMSEX message Parameter lParam : Pointer to an application-defined comparison function. It is called during the sort operation each time the relative order of two list items needs to be compared. As we can see in the console, the callback function (the "application-defined comparison function") has been called 10 times during this script, 1st time comparing item 1 to item 2, second time comparing item 0 to item 1 etc... Each time that 2 items have been compared, then Windows (not us) builds internally the final sorted array, maybe by inserting the 2 items at their correct place, moving them, changing the indexes in the listview control etc... A "sort guru" could certainly explain it much better than I do, but well... you got the idea Link to comment Share on other sites More sharing options...
pixelsearch Posted November 21, 2020 Share Posted November 21, 2020 11/21/2020 : Changes made in my penultimate post above. The context sort menu is now based on menu radio items, which also makes the code in part 2) much shorter Link to comment Share on other sites More sharing options...
paulpmeier Posted November 22, 2020 Author Share Posted November 22, 2020 Hello pixelsearch, I have added your changes to "ArrayDisplayInternals.au3". The sort context menu is very useful. Thanks again. pixelsearch 1 Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now