简体   繁体   中英

How to improve iterative loop performance

So I am trying to loop through a set of data and essentially create a result tree that shows all of the different paths, which I have finally been successful in, but it runs terrible slow, which from my testing is because of the recurring search function and the additional regex match at the end since the function can't determine if the final result matches the stanard windows. How can I do a check on the root call to filter the results that do start with windows: and how can I remove the recurring search function. My guess is I could make a new hashtable that contains only the name/parent value and using that but I have been playing with that concept for a few hours without any luck.

Current working code.

Function New-CategoryTree {
    Param (
        $Categories
    )

    $GetParentCategory = [ScriptBlock]::Create({
        Param(
            [psobject]$Category
        )
        
        #Examine the parentCategory field and see if it matches certain criteria.
        Switch ($Category.parentCategory.ref) {
            { $_ -match "^windows:([a-zA-Z_\\ 1-9]+)$" } { #if Parent Category matches 'Windows:*'
                Return "$($Category.parentCategory.ref)\$($Category.name)"
            }
            { [String]::IsNullOrWhiteSpace($_) } { #If pareent
                Return
            }
            Default {
                Return ("$(& $GetParentCategory -Category ($Categories.where({$_.name -eq $Category.parentCategory.ref})))\$($Category.name)")
            }
        }
    })

    New-Variable -Name Result -Value (New-Object -TypeName System.Collections.ArrayList)
    New-Variable -Name NewCategories -Value (New-Object -TypeName System.Collections.ArrayList)
    ForEach ($Category in $Categories) {
        [Void]$NewCategories.Add((& $GetParentCategory -Category $Category))
    }

    ForEach ($Category in $NewCategories) {
        ([Regex]::Match($Category,'^windows:([a-zA-Z_\\ 1-9]+)$').groups[1]).where({$_.success -eq $True}).value |ForEach-Object {[Void]$Result.Add($_)}
    }
    Return $Result
}

Data set

<categories>
    <category name="InternetExplorer" displayName="$(string.InternetExplorer)" explainText="$(string.IE_ExplainCat)">
        <parentCategory ref="windows:WindowsComponents" />
    </category>
    <category name="AdvancedPage" displayName="$(string.AdvancedPage)">
        <parentCategory ref="InternetCPL" />
    </category>
    <category name="InternetCPL_Advanced_Accessibility" displayName="$(string.InternetCPL_Advanced_Accessibility)">
        <parentCategory ref="AdvancedPage" />
    </category>
    <category name="InternetCPL_Advanced_International" displayName="$(string.InternetCPL_Advanced_International)">
        <parentCategory ref="AdvancedPage" />
    </category>
    <category name="InternetCPL_Advanced_Security" displayName="$(string.InternetCPL_Advanced_Security)">
        <parentCategory ref="AdvancedPage" />
    </category>
</categories>

Expected Output

WindowsComponents\InternetExplorer
WindowsComponents\RSS_Feeds
WindowsComponents\InternetExplorer\InternetCPL\AdvancedPage\InternetCPL_Advanced_Accessibility
WindowsComponents\InternetExplorer\InternetCPL\AdvancedPage\InternetCPL_Advanced_International
WindowsComponents\InternetExplorer\InternetCPL\AdvancedPage\InternetCPL_Advanced_Security
WindowsComponents\InternetExplorer\InternetCPL\InternetCPL_Connections
WindowsComponents\InternetExplorer\InternetCPL\InternetCPL_Content
WindowsComponents\InternetExplorer\InternetCPL\InternetCPL_Content\InternetCPL_Content_Certificates
WindowsComponents\InternetExplorer\InternetCPL\InternetCPL_Privacy
WindowsComponents\InternetExplorer\InternetCPL\InternetCPL_Programs
WindowsComponents\InternetExplorer\InternetSettings\Advanced\Browsing
WindowsComponents\InternetExplorer\InternetSettings\Advanced\ICWSettings
WindowsComponents\InternetExplorer\InternetSettings\Advanced\Multimedia
WindowsComponents\InternetExplorer\InternetSettings\Advanced\Printing
WindowsComponents\InternetExplorer\InternetSettings\Advanced\Searching
WindowsComponents\InternetExplorer\InternetSettings\Advanced\SignupSettings
WindowsComponents\InternetExplorer\CategoryAppCompat\ScriptPaste
WindowsComponents\InternetExplorer\InternetSettings\ComponentUpdates\HelpAbout128
WindowsComponents\InternetExplorer\InternetSettings\ComponentUpdates\UpdateCheck
WindowsComponents\InternetExplorer\CorporateSettings\CodeDownload
WindowsComponents\InternetExplorer\InternetSettings\DisplaySettings\GeneralColors
WindowsComponents\InternetExplorer\InternetSettings\DisplaySettings\LinkColors
WindowsComponents\InternetExplorer\InternetCPL\AdvancedPage
WindowsComponents\InternetExplorer\InternetCPL\IZ_SecurityPage
WindowsComponents\InternetExplorer\SecurityFeatures\IESF_CategoryNetworkProtocolLockdown\IESF_NPLRest_Category
WindowsComponents\InternetExplorer\AdminApproved
WindowsComponents\InternetExplorer\CategoryAppCompat
WindowsComponents\InternetExplorer\Channels
WindowsComponents\InternetExplorer\CorporateSettings
WindowsComponents\InternetExplorer\DeleteBrowsingHistory
WindowsComponents\InternetExplorer\InternetCPL
WindowsComponents\InternetExplorer\InternetSettings
WindowsComponents\InternetExplorer\Menus
WindowsComponents\InternetExplorer\Persistence
WindowsComponents\InternetExplorer\SecurityFeatures
WindowsComponents\InternetExplorer\Toolbars
WindowsComponents\InternetExplorer\InternetSettings\Advanced
WindowsComponents\InternetExplorer\InternetSettings\AutoCompleteCat
WindowsComponents\InternetExplorer\InternetSettings\ComponentUpdates
WindowsComponents\InternetExplorer\InternetSettings\DisplaySettings
WindowsComponents\InternetExplorer\InternetSettings\Encoding
WindowsComponents\InternetExplorer\InternetCPL\IZ_SecurityPage\IZ_InternetZone
WindowsComponents\InternetExplorer\InternetCPL\IZ_SecurityPage\IZ_InternetZoneLockdown
WindowsComponents\InternetExplorer\InternetCPL\IZ_SecurityPage\IZ_IntranetZone
WindowsComponents\InternetExplorer\InternetCPL\IZ_SecurityPage\IZ_IntranetZoneLockdown
WindowsComponents\InternetExplorer\InternetCPL\IZ_SecurityPage\IZ_LocalMachineZone
WindowsComponents\InternetExplorer\InternetCPL\IZ_SecurityPage\IZ_LocalMachineZoneLockdown
WindowsComponents\InternetExplorer\InternetCPL\IZ_SecurityPage\IZ_RestrictedSitesZone
WindowsComponents\InternetExplorer\InternetCPL\IZ_SecurityPage\IZ_RestrictedSitesZoneLockdown
WindowsComponents\InternetExplorer\InternetCPL\IZ_SecurityPage\IZ_TrustedSitesZone
WindowsComponents\InternetExplorer\InternetCPL\IZ_SecurityPage\IZ_TrustedSitesZoneLockdown
WindowsComponents\InternetExplorer\SecurityFeatures\IESF_AddOnManagement
WindowsComponents\InternetExplorer\SecurityFeatures\IESF_CategoryAJAX
WindowsComponents\InternetExplorer\SecurityFeatures\IESF_CategoryBinaryBehaviorSecurityRestriction
WindowsComponents\InternetExplorer\SecurityFeatures\IESF_CategoryConsistentMimeHandling
WindowsComponents\InternetExplorer\SecurityFeatures\IESF_CategoryInformationBar
WindowsComponents\InternetExplorer\SecurityFeatures\IESF_CategoryLocalMachineZoneLockdownSecurity
WindowsComponents\InternetExplorer\SecurityFeatures\IESF_CategoryMimeSniffingSafetyFeature
WindowsComponents\InternetExplorer\SecurityFeatures\IESF_CategoryMKProtocolSecurityRestriction
WindowsComponents\InternetExplorer\SecurityFeatures\IESF_CategoryNetworkProtocolLockdown
WindowsComponents\InternetExplorer\SecurityFeatures\IESF_CategoryObjectCachingProtection
WindowsComponents\InternetExplorer\SecurityFeatures\IESF_CategoryProtectionFromZoneElevation
WindowsComponents\InternetExplorer\SecurityFeatures\IESF_CategoryRestrictActiveXInstall
WindowsComponents\InternetExplorer\SecurityFeatures\IESF_CategoryRestrictFileDownload
WindowsComponents\InternetExplorer\SecurityFeatures\IESF_CategoryScriptedWindowSecurityRestrictions
WindowsComponents\InternetExplorer\CategoryPrivacy
WindowsComponents\InternetExplorer\CategoryAccelerators
WindowsComponents\InternetExplorer\CategoryCompatView
WindowsComponents\InternetExplorer\InternetCPL\CategoryGeneralPage
WindowsComponents\InternetExplorer\InternetCPL\CategoryGeneralPage\CategoryBrowsingHistory

start by treating your xml data properly so you can interact with it:

[xml]$categories

Then you can display it easily:

[xml]$categories.categories.category | 
    select @{l='ParentCategory';e={$_.ParentCategory.ref}},Name,Displayname

ParentCategory            name                              
--------------            ----                              
windows:WindowsComponents InternetExplorer                  
InternetCPL               AdvancedPage                      
AdvancedPage              InternetCPL_Advanced_Accessibility
AdvancedPage              InternetCPL_Advanced_International
AdvancedPage              InternetCPL_Advanced_Security

Filter it however you need:

$filtered = [xml]$categories.categories.category | 
    where {$_.ParentCategory.ref -match '^windows:([a-zA-Z_\\ 1-9]+)$'}
$filtered

name             displayName                explainText             parentCategory
----             -----------                -----------             --------------
InternetExplorer $(string.InternetExplorer) $(string.IE_ExplainCat) parentCategory

And to export the filtered xml objects back to a string, adding the outside nodes:

$xmlout = '<categories>'+$filtered.outerxml+'</categories>'

After several more hours of research I stumbled upon the 'systems.collections.generic.dictionary' property, which allowed me to essentially create a hashtable with a PSCustomObject as a value. This allowed me to create a new object with referenceable names which allowed me to avoid using search function. Combined with the new data types + utilizing Streamreader to intake the file my script running from about 3-5s to about 0.5s ps ignore all my comments in the code, I was struggling to wrap my head around the function lol.

Function New-CategoryTree {
    Param (
        $Categories
    )
        
    #Scriptblock that will be used to check if the currently returned object is the root object.
    $GetParentCategory = [ScriptBlock]::Create({
        Param(
            [Parameter(mandatory=$True)]
            $Category,
            [Parameter(mandatory=$True)]
            $Categories
        )
        Switch -regex ($Category.ParentCategory) {
            "^windows:([a-zA-Z_\\ 1-9]+)$" { #if Category matches 'Windows:*' (a.k.a. we are done with the dive.)
                Return "$($Category.ParentCategory)\$($Category.DisplayName)"
            }
            "/^$|\s+/"  { #Check if whitespace or empty.
                Write-Host "Warning: This shouldn't be possible, best guess is ADMX is missconfigured. CategoryName: '$($Category.Name)'."
                #This means its a root category without a parent so just return.
                Return
            }
            Default {#If not root category, then restart the loop to dive 1 layer deeper then eventually retun with parentCategory.DisplayName\Category.DisplayName.
                #If there is no valid parent category then thow a ignorable warning.
                If ([String]::IsNullOrEmpty($Categories["$($Category.ParentCategory)"])) {
                    Write-Host "Warning: $($Category.name) has no valid parent category."
                } Else {
                    Return ("$(& $GetParentCategory -Category $Categories["$($Category.ParentCategory)"] -Categories $Categories)\$($Category.displayName)")
                }
            }
        }
    })

    New-Variable -Name Results -Value (New-Object -TypeName System.Collections.Generic.List[String]) -Force
    ForEach ($Category in $Categories.GetEnumerator()) {
        $Results.Add((& $GetParentCategory -Category $Category.Value -Categories $Categories))
    }
    Return $Results
}

#Start Looping through every ADMX files.
ForEach ($File in $Script.ADMXFiles) {
    #Get content of ADMX file and store results as XML
    Set-Variable -Name ADMXFile      -Value ([xml]((New-Object -TypeName System.IO.StreamReader -ArgumentList $File.FullName,([Text.Encoding]::Default),$False,"10000").ReadToEnd()))
    New-Variable -Name Categories -Value (New-Object -TypeName 'System.Collections.Generic.Dictionary[[string], [PSCustomObject]]') -Force
    $ADMXFile.policyDefinitions.categories.Category |ForEach-Object {
        $Categories.add($_.name,[PSCustomObject]@{
            Name           = $_.name
            DisplayName    = ConvertFrom-StringTable -String $_.displayName -ADMX ($File.BaseName)
            ParentCategory = $_.ParentCategory.ref
        })
    }
    New-CategoryTree -Categories $Categories
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM