Jump to content

Recursive _ArraySort


Recommended Posts

I have a list of files from _FileListToArrayRec. I then _ArraySort them and put them into a treeview so i can browse the structure of the folders. It works but it sorts the files too literally, giving me this:

  • A file
  • B file
  • B subfolder
    • A subfile
    • B subfile
    • C subfile
    • C subfolder
      • A subsubfile
      • B subsubfile
    • D subfile
    • E subfile
  • C file
  • C subfolder
    • A subfile
    • A subfolder
      • A subsubfile
    • B subfile

What I want is in each subfolder, It to list the FOLDERS in alphabetical folder, and then the FILES.

  • B subfolder
    • C subfolder
      • A subsubfile
      • B subsubfile
    • A subfile
    • B subfile
    • C subfile
    • D subfile
    • E subfile
  • C subfolder
    • A subfolder
      • A subsubfile
    • A subfile
    • B subfile
  • A file
  • B file
  • C file

Most threads I found when googling this say something along the lines of "Just get a list of folders and files separately, and then put the two arrays files after folders" but that only works for a single folder, it doesn't work for recursive folders. Has anyone found a way to get the desired result?

Edited by corgano

0x616e2069646561206973206c696b652061206d616e20776974686f7574206120626f64792c20746f206669676874206f6e6520697320746f206e657665722077696e2e2e2e2e

Link to comment
Share on other sites

Does specifying the full path being returned help?

$iReturnPath = $FLTAR_RELPATH

    $FLTAR_NOPATH (0) - File/folder name only
    $FLTAR_RELPATH (1) - Relative to initial path (Default)
    $FLTAR_FULLPATH (2) - Full path included

Link to comment
Share on other sites

Also the function includes a sort option?

$iSort[optional] Sort results in alphabetical and depth order
    $FLTAR_NOSORT (0) - Not sorted (Default)
    $FLTAR_SORT (1) - Sorted
    $FLTAR_FASTSORT (2) - Sorted with faster algorithm (assumes files in folder returned sorted - requires NTFS and not guaranteed)

Link to comment
Share on other sites

$FLTAR_FASTSORT gives me almost EXACTLY what I want - but it sorts it FILESA-Z then FOLDERSA-Z. Any way to make it have FOLDERS first?

0x616e2069646561206973206c696b652061206d616e20776974686f7574206120626f64792c20746f206669676874206f6e6520697320746f206e657665722077696e2e2e2e2e

Link to comment
Share on other sites

  • Moderators

corgano,

It was a design decision to place the files in the root folder first - and not that difficult to change them to the end. Does this modified function do what you want?

#include <File.au3>
#include <Array.au3>

; New constants for File.au3
Global $FLTAR_FILESFIRST = 1, $FLTAR_FILESLAST = 0

$aArray = _FileListToArrayRec_Mod("M:\Program\Au3 Scripts\Forum Help Scripts\", "*", $FLTAR_FILESFOLDERS, $FLTAR_RECUR, $FLTAR_FASTSORT, $FLTAR_RELPATH, $FLTAR_FILESFIRST)
_ArrayDisplay($aArray, "", Default, 8)

$aArray = _FileListToArrayRec_Mod("M:\Program\Au3 Scripts\Forum Help Scripts\", "*", $FLTAR_FILESFOLDERS, $FLTAR_RECUR, $FLTAR_FASTSORT, $FLTAR_RELPATH, $FLTAR_FILESLAST)
_ArrayDisplay($aArray, "", Default, 8)

Func _FileListToArrayRec_Mod($sFilePath, $sMask = "*", $iReturn = $FLTAR_FILESFOLDERS, $iRecur = $FLTAR_NORECUR, $iSort = $FLTAR_NOSORT, $iReturnPath = $FLTAR_RELPATH, $iFilesFirst = $FLTAR_FILESFIRST)
    If Not FileExists($sFilePath) Then Return SetError(1, 1, "")

    ; Check for Default keyword
    If $sMask = Default Then $sMask = "*"
    If $iReturn = Default Then $iReturn = $FLTAR_FILESFOLDERS
    If $iRecur = Default Then $iRecur = $FLTAR_NORECUR
    If $iSort = Default Then $iSort = $FLTAR_NOSORT
    If $iReturnPath = Default Then $iReturnPath = $FLTAR_RELPATH
    If $iFilesFirst = Default Then $iFilesFirst = $FLTAR_FILESFIRST

    ; Check for valid recur value
    If $iRecur > 1 Or Not IsInt($iRecur) Then Return SetError(1, 6, "")

    Local $bLongPath = False
    ; Check for valid path
    If StringLeft($sFilePath, 4) == "\\?\" Then
        $bLongPath = True
    EndIf

    Local $sFolderSlash = ""
    ; Check if folders should have trailing \ and ensure that initial path does have one
    If StringRight($sFilePath, 1) = "\" Then
        $sFolderSlash = "\"
    Else
        $sFilePath = $sFilePath & "\"
    EndIf

    Local $asFolderSearchList[100] = [1]
    ; Add path to folder search list
    $asFolderSearchList[1] = $sFilePath

    Local $iHide_HS = 0, _
            $sHide_HS = ""
    ; Check for H or S omitted
    If BitAND($iReturn, $FLTAR_NOHIDDEN) Then
        $iHide_HS += 2
        $sHide_HS &= "H"
        $iReturn -= $FLTAR_NOHIDDEN
    EndIf
    If BitAND($iReturn, $FLTAR_NOSYSTEM) Then
        $iHide_HS += 4
        $sHide_HS &= "S"
        $iReturn -= $FLTAR_NOSYSTEM
    EndIf

    Local $iHide_Link = 0
    ; Check for link/junction omitted
    If BitAND($iReturn, $FLTAR_NOLINK) Then
        $iHide_Link = 0x400
        $iReturn -= $FLTAR_NOLINK
    EndIf

    Local $iMaxLevel = 0
    ; If required, determine \ count for max recursive level setting
    If $iRecur < 0 Then
        StringReplace($sFilePath, "\", "", 0, $STR_NOCASESENSEBASIC)
        $iMaxLevel = @extended - $iRecur
    EndIf

    Local $sExclude_List = "", $sExclude_List_Folder = "", $sInclude_List = "*"
    ; Check mask parameter
    Local $aMaskSplit = StringSplit($sMask, "|")
    ; Check for multiple sections and set values
    Switch $aMaskSplit[0]
        Case 3
            $sExclude_List_Folder = $aMaskSplit[3]
            ContinueCase
        Case 2
            $sExclude_List = $aMaskSplit[2]
            ContinueCase
        Case 1
            $sInclude_List = $aMaskSplit[1]
    EndSwitch

    Local $sInclude_File_Mask = ".+"
    ; Create Include mask for files
    If $sInclude_List <> "*" Then
        If Not __FLTAR_ListToMask($sInclude_File_Mask, $sInclude_List) Then Return SetError(1, 2, "")
    EndIf

    Local $sInclude_Folder_Mask = ".+"
    ; Set Include mask for folders
    Switch $iReturn
        Case 0
            ; Folders affected by mask if not recursive
            Switch $iRecur
                Case 0
                    ; Folders match mask for compatibility
                    $sInclude_Folder_Mask = $sInclude_File_Mask
            EndSwitch
        Case 2
            ; Folders affected by mask
            $sInclude_Folder_Mask = $sInclude_File_Mask
    EndSwitch

    Local $sExclude_File_Mask = ":"
    ; Create Exclude List mask for files
    If $sExclude_List <> "" Then
        If Not __FLTAR_ListToMask($sExclude_File_Mask, $sExclude_List) Then Return SetError(1, 3, "")
    EndIf

    Local $sExclude_Folder_Mask = ":"
    ; Create Exclude mask for folders
    If $iRecur Then
        If $sExclude_List_Folder Then
            If Not __FLTAR_ListToMask($sExclude_Folder_Mask, $sExclude_List_Folder) Then Return SetError(1, 4, "")
        EndIf
        ; If folders only
        If $iReturn = 2 Then
            ; Folders affected by normal mask
            $sExclude_Folder_Mask = $sExclude_File_Mask
        EndIf
    Else
        ; Folders affected by normal mask
        $sExclude_Folder_Mask = $sExclude_File_Mask
    EndIf

    ; Verify other parameters
    If Not ($iReturn = 0 Or $iReturn = 1 Or $iReturn = 2) Then Return SetError(1, 5, "")
    If Not ($iSort = 0 Or $iSort = 1 Or $iSort = 2) Then Return SetError(1, 7, "")
    If Not ($iReturnPath = 0 Or $iReturnPath = 1 Or $iReturnPath = 2) Then Return SetError(1, 8, "")
    If Not ($iFilesFirst = 0 Or $iFilesFirst = 1) Then Return SetError(1, 9, "")

    ; Prepare for DllCall if required
    If $iHide_Link Then
        Local $tFile_Data = DllStructCreate("struct;align 4;dword FileAttributes;uint64 CreationTime;uint64 LastAccessTime;uint64 LastWriteTime;" & _
                "dword FileSizeHigh;dword FileSizeLow;dword Reserved0;dword Reserved1;wchar FileName[260];wchar AlternateFileName[14];endstruct")
        Local $hDLL = DllOpen('kernel32.dll'), $aDLL_Ret
    EndIf

    Local $asReturnList[100] = [0]
    Local $asFileMatchList = $asReturnList, $asRootFileMatchList = $asReturnList, $asFolderMatchList = $asReturnList
    Local $bFolder = False, _
            $hSearch = 0, _
            $sCurrentPath = "", $sName = "", $sRetPath = ""
    Local $iAttribs = 0, _
            $sAttribs = ''
    Local $asFolderFileSectionList[100][2] = [[0, 0]]
    ; Search within listed folders
    While $asFolderSearchList[0] > 0

        ; Set path to search
        $sCurrentPath = $asFolderSearchList[$asFolderSearchList[0]]
        ; Reduce folder search list count
        $asFolderSearchList[0] -= 1
        ; Determine return path to add to file/folder name
        Switch $iReturnPath
            ; Case 0 ; Name only
            ; Leave as ""
            Case 1 ;Relative to initial path
                $sRetPath = StringReplace($sCurrentPath, $sFilePath, "")
            Case 2 ; Full path
                If $bLongPath Then
                    $sRetPath = StringTrimLeft($sCurrentPath, 4)
                Else
                    $sRetPath = $sCurrentPath
                EndIf
        EndSwitch

        ; Get search handle - use code matched to required listing
        If $iHide_Link Then
            ; Use DLL code
            $aDLL_Ret = DllCall($hDLL, 'handle', 'FindFirstFileW', 'wstr', $sCurrentPath & "*", 'struct*', $tFile_Data)
            If @error Or Not $aDLL_Ret[0] Then
                ContinueLoop
            EndIf
            $hSearch = $aDLL_Ret[0]
        Else
            ; Use native code
            $hSearch = FileFindFirstFile($sCurrentPath & "*")
            ; If folder empty move to next in list
            If $hSearch = -1 Then
                ContinueLoop
            EndIf
        EndIf

        ; If sorting files and folders with paths then store folder name and position of associated files in list
        If $iReturn = 0 And $iSort And $iReturnPath Then
            __FLTAR_AddToList($asFolderFileSectionList, $sRetPath, $asFileMatchList[0] + 1)
        EndIf
        $sAttribs = ''

        ; Search folder - use code matched to required listing
        While 1
            ; Use DLL code
            If $iHide_Link Then
                ; Use DLL code
                $aDLL_Ret = DllCall($hDLL, 'int', 'FindNextFileW', 'handle', $hSearch, 'struct*', $tFile_Data)
                ; Check for end of folder
                If @error Or Not $aDLL_Ret[0] Then
                    ExitLoop
                EndIf
                ; Extract data
                $sName = DllStructGetData($tFile_Data, "FileName")
                ; Check for .. return - only returned by the DllCall
                If $sName = ".." Then
                    ContinueLoop
                EndIf
                $iAttribs = DllStructGetData($tFile_Data, "FileAttributes")
                ; Check for hidden/system attributes and skip if found
                If $iHide_HS And BitAND($iAttribs, $iHide_HS) Then
                    ContinueLoop
                EndIf
                ; Check for link attribute and skip if found
                If BitAND($iAttribs, $iHide_Link) Then
                    ContinueLoop
                EndIf
                ; Set subfolder flag
                $bFolder = False
                If BitAND($iAttribs, 16) Then
                    $bFolder = True
                EndIf
            Else
                ; Reset folder flag
                $bFolder = False
                ; Use native code
                $sName = FileFindNextFile($hSearch, 1)
                ; Check for end of folder
                If @error Then
                    ExitLoop
                EndIf
                $sAttribs = @extended
                ; Check for folder
                If StringInStr($sAttribs, "D") Then
                    $bFolder = True
                EndIf
                ; Check for Hidden/System
                If StringRegExp($sAttribs, "[" & $sHide_HS & "]") Then
                    ContinueLoop
                EndIf
            EndIf

            ; If folder then check whether to add to search list
            If $bFolder Then
                Select
                    Case $iRecur < 0 ; Check recur depth
                        StringReplace($sCurrentPath, "\", "", 0, $STR_NOCASESENSEBASIC)
                        If @extended < $iMaxLevel Then
                            ContinueCase ; Check if matched to masks
                        EndIf
                    Case $iRecur = 1 ; Full recur
                        If Not StringRegExp($sName, $sExclude_Folder_Mask) Then ; Add folder unless excluded
                            __FLTAR_AddToList($asFolderSearchList, $sCurrentPath & $sName & "\")
                        EndIf
                        ; Case $iRecur = 0 ; Never add
                        ; Do nothing
                EndSelect
            EndIf

            If $iSort Then ; Save in relevant folders for later sorting
                If $bFolder Then
                    If StringRegExp($sName, $sInclude_Folder_Mask) And Not StringRegExp($sName, $sExclude_Folder_Mask) Then
                        __FLTAR_AddToList($asFolderMatchList, $sRetPath & $sName & $sFolderSlash)
                    EndIf
                Else
                    If StringRegExp($sName, $sInclude_File_Mask) And Not StringRegExp($sName, $sExclude_File_Mask) Then
                        ; Select required list for files
                        If $sCurrentPath = $sFilePath Then
                            __FLTAR_AddToList($asRootFileMatchList, $sRetPath & $sName)
                        Else
                            __FLTAR_AddToList($asFileMatchList, $sRetPath & $sName)
                        EndIf
                    EndIf
                EndIf
            Else ; Save directly in return list
                If $bFolder Then
                    If $iReturn <> 1 And StringRegExp($sName, $sInclude_Folder_Mask) And Not StringRegExp($sName, $sExclude_Folder_Mask) Then
                        __FLTAR_AddToList($asReturnList, $sRetPath & $sName & $sFolderSlash)
                    EndIf
                Else
                    If $iReturn <> 2 And StringRegExp($sName, $sInclude_File_Mask) And Not StringRegExp($sName, $sExclude_File_Mask) Then
                        __FLTAR_AddToList($asReturnList, $sRetPath & $sName)
                    EndIf
                EndIf
            EndIf

        WEnd

        ; Close current search
        If $iHide_Link Then
            DllCall($hDLL, 'int', 'FindClose', 'ptr', $hSearch)
        Else
            FileClose($hSearch)
        EndIf

    WEnd

    ; Close the DLL if needed
    If $iHide_Link Then
        DllClose($hDLL)
    EndIf

    ; Sort results if required
    If $iSort Then
        Switch $iReturn
            Case 2 ; Folders only
                ; Check if any folders found
                If $asFolderMatchList[0] = 0 Then Return SetError(1, 9, "")
                ; Correctly size folder match list
                ReDim $asFolderMatchList[$asFolderMatchList[0] + 1]
                ; Copy size folder match array
                $asReturnList = $asFolderMatchList
                ; Simple sort list
                __ArrayDualPivotSort($asReturnList, 1, $asReturnList[0])
            Case 1 ; Files only
                ; Check if any files found
                If $asRootFileMatchList[0] = 0 And $asFileMatchList[0] = 0 Then Return SetError(1, 9, "")
                If $iReturnPath = 0 Then ; names only so simple sort suffices
                    ; Combine file match lists
                    __FLTAR_AddFileLists($asReturnList, $asRootFileMatchList, $asFileMatchList)
                    ; Simple sort combined file list
                    __ArrayDualPivotSort($asReturnList, 1, $asReturnList[0])
                Else
                    ; Combine sorted file match lists
                    __FLTAR_AddFileLists($asReturnList, $asRootFileMatchList, $asFileMatchList, 1)
                EndIf
            Case 0 ; Both files and folders
                ; Check if any root files or folders found
                If $asRootFileMatchList[0] = 0 And $asFolderMatchList[0] = 0 Then Return SetError(1, 9, "")
                If $iReturnPath = 0 Then ; names only so simple sort suffices
                    ; Combine file match lists
                    __FLTAR_AddFileLists($asReturnList, $asRootFileMatchList, $asFileMatchList)
                    ; Set correct count for folder add
                    $asReturnList[0] += $asFolderMatchList[0]
                    ; Resize and add file match array
                    ReDim $asFolderMatchList[$asFolderMatchList[0] + 1]
                    _ArrayConcatenate($asReturnList, $asFolderMatchList, 1)
                    ; Simple sort final list
                    __ArrayDualPivotSort($asReturnList, 1, $asReturnList[0])
                Else
                    ; Size return list
                    Local $asReturnList[$asFileMatchList[0] + $asRootFileMatchList[0] + $asFolderMatchList[0] + 1]
                    $asReturnList[0] = $asFileMatchList[0] + $asRootFileMatchList[0] + $asFolderMatchList[0]
                    ; Sort root file list
                    __ArrayDualPivotSort($asRootFileMatchList, 1, $asRootFileMatchList[0])
                    ; Set insertion index
                    Local $iNextInsertionIndex = 1
                    If $iFilesFirst = 1 Then
                        ; Add the sorted root files at the beginning
                        For $i = 1 To $asRootFileMatchList[0]
                            $asReturnList[$iNextInsertionIndex] = $asRootFileMatchList[$i]
                            $iNextInsertionIndex += 1
                        Next
                    EndIf
                    ; Sort folder list
                    __ArrayDualPivotSort($asFolderMatchList, 1, $asFolderMatchList[0])
                    Local $sFolderToFind = ""
                    ; Work through folder list
                    For $i = 1 To $asFolderMatchList[0]
                        ; Add folder to return list
                        $asReturnList[$iNextInsertionIndex] = $asFolderMatchList[$i]
                        $iNextInsertionIndex += 1
                        ; Format folder name for search
                        If $sFolderSlash Then
                            $sFolderToFind = $asFolderMatchList[$i]
                        Else
                            $sFolderToFind = $asFolderMatchList[$i] & "\"
                        EndIf
                        Local $iFileSectionEndIndex = 0, $iFileSectionStartIndex = 0
                        ; Find folder in FolderFileSectionList
                        For $j = 1 To $asFolderFileSectionList[0][0]
                            ; If found then deal with files
                            If $sFolderToFind = $asFolderFileSectionList[$j][0] Then
                                ; Set file list indexes
                                $iFileSectionStartIndex = $asFolderFileSectionList[$j][1]
                                If $j = $asFolderFileSectionList[0][0] Then
                                    $iFileSectionEndIndex = $asFileMatchList[0]
                                Else
                                    $iFileSectionEndIndex = $asFolderFileSectionList[$j + 1][1] - 1
                                EndIf
                                ; Sort files if required
                                If $iSort = 1 Then
                                    __ArrayDualPivotSort($asFileMatchList, $iFileSectionStartIndex, $iFileSectionEndIndex)
                                EndIf
                                ; Add files to return list
                                For $k = $iFileSectionStartIndex To $iFileSectionEndIndex
                                    $asReturnList[$iNextInsertionIndex] = $asFileMatchList[$k]
                                    $iNextInsertionIndex += 1
                                Next
                                ExitLoop
                            EndIf
                        Next
                    Next
                    If $iFilesFirst = 0 Then
                        ; Add the sorted root files at the end
                        For $i = 1 To $asRootFileMatchList[0]
                            $asReturnList[$iNextInsertionIndex] = $asRootFileMatchList[$i]
                            $iNextInsertionIndex += 1
                        Next
                    EndIf

                EndIf
        EndSwitch
    Else ; No sort
        ; Check if any file/folders have been added
        If $asReturnList[0] = 0 Then Return SetError(1, 9, "")
        ; Remove any unused return list elements from last ReDim
        ReDim $asReturnList[$asReturnList[0] + 1]

    EndIf

    Return $asReturnList
EndFunc   ;==>_FileListToArrayRec_Mod

If so, we might consider a formal modification to the UDF to make the order user-selectable using a new parameter.

M23

Edited by Melba23
Amended example script - again

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png 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 columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

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...