Design Problem: Nested Loop Nightmare

This topic contains 2 replies, has 2 voices, and was last updated by Profile photo of Isaac Garvin Isaac Garvin 1 year, 5 months ago.

  • Author
    Posts
  • #29443
    Profile photo of Isaac Garvin
    Isaac Garvin
    Participant

    Short question: How do I run through an object array, group them by unique values in properties, then iterate through the array based on those groups (which are nested groups btw) to generate reports? Don't need a code solution, just some design help.

    Long question:Using the following object array of log events, how do I iterate through it to create reports based on both devices and sites? There are multiple log events (objects) per device, and multiple devices per site. Here's how it was made:

    $SyncCompletePattern = "Synchronization Complete"
    $SyncStartPattern = "Starting to sync"
    $SyncFailPattern = "Synchronization Failed"
    
    $SyncLog = Get-Childitem -Path C:\Users\igarvin\Documents\Logs\*\*\device*.log -Recurse | Select-String -Pattern $SyncCompletePattern, $SyncFailPattern, $SyncStartPattern
    
    $data = $(switch -Regex ($SyncLog) {
            "(?< =\\)Site\d{2}(?=\\)" { $site = $Matches[0] }
            "(?<=\\)\d{8}(?=\\)" { $device = $Matches[0] }
            "\d{2}/\d{2} \d{2}:\d{2}:\d{2}:\d{3}" { $timestamp = [datetime]::ParseExact($Matches[0],"MM/dd HH:mm:ss:fff",$null) }
            "(?<=^.*?  \d ).*$" { $message = $Matches[0] }
            "^.+$" {  New-Object PSObject -Property @{
                Timestamp = $timestamp
                Timespan = $timespan
                Message = $message
                Device = $device
                Site = $site
            } }   
        })
    

    Sample output:

    PS C:\Users\igarvin> $data[1..3]
    
    Device      : 00606049
    Timespan    : 
    Timestamp   : 6/2/2015 4:50:46 PM
    Site        : Site02
    Message     : Synchronization complete.
    
    Device      : 00606049
    Timespan    : 
    Timestamp   : 6/26/2015 1:27:09 PM
    Site        : Site02
    Message     : Starting to sync...
    
    Device      : 00606049
    Timespan    : 
    Timestamp   : 6/26/2015 1:27:10 PM
    Site        : Site02
    Message     : Synchronization complete.
    

    I want to:

    1. Create both device specific reports and site reports, both that cover how many "sync events" occurred (sync starts) and display their results.
    2. Fill in timespan properties of objects with completion times for syncs
    3. Add averages of timespans to such reports

    I've been trying to do it but it's become an ugly nested loop nightmare, repeating operations several times. Here's what I have so far:

    $SiteList = $data.Site | Get-Unique
    $DeviceList = $data.Device | Get-Unique
    
    
    foreach ($s in $SiteList)
    {
        foreach ($d in $DeviceList)
        {       
            for ($i=1; $i -lt $data.Length; $i++)
            {       
                if (($data[$i].Device -eq $d) -and ($data[$i].Site -eq $s))
                {
                    if(($data[$i].Message -match "Starting to sync") -and ($data[$i+1].Message -match "Synchronization Complete"))
                    {
                        $y = New-Timespan -Start $data[$i].TimeStamp -End $data[$i+1].TimeStamp
                        $data[$i].TimeSpan = $y
                        $SuccessCount++
                    }
                    if(($data[$i].Message -match "Starting to sync") -and ($data[$i+1].Message -match "Synchronization failed"))
                    {
                        $data[$i].TimeSpan = "Failed"
                        $FailCount++
                    }
                    if($data[$i].Message -match $CheckinTimePattern)
                    {
                        $CheckInCount++
                    }
    
            
                }         
            }
    
            
            $SiteSuccessCount += $SuccessCount
            $SiteFailCount += $FailCount
            $SiteCheckinCount += $CheckinCount
    
            $DevSyncPath = "C:\Users\igarvin\Documents\Logs\$s\SyncLog$d.csv"
            #$DevTimespanAvg = ($data | Where-Object {$_.Device -eq $d} | Measure-Object -Property Timespan -Average).Average
            $data | Where-Object {$_.Device -eq $d} | Export-Csv -Path $DevSyncPath -NoTypeInformation
            Add-Content -Path $DevSyncPath -Value "`n`n`nCheckins`n$CheckinCount`n`nSync Average`n`n`nSuccesses, Failures`n$SuccessCount, $FailCount"
            
            $CheckinCount = $null
            $SuccessCount = $null
            $FailCount = $null  
    
            
        }
    
        $SiteSyncPath = "C:\Users\igarvin\Documents\Logs\SyncLog$s.csv"
        #$SiteTimeSpanAvg = ($data | Where-Object {$_.Site -eq $s} | Measure-Object -Property Timespan -Average).Average
        Add-Content -Path $SiteSyncPath -Value "Checkins, Sync Average, Successes, Failures`n$SiteCheckinCount, , $SiteSuccessCount, $SiteFailCount" -Force
    
    }
    

    I know where i'm going wrong with this, but I don't know what the correct solution is in order to loop based on group (nested groups even) and not repeat stuff unnecessarily. I don't need someone to code a solution but to tell me the direction I need to go to design one.

  • #29474
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

    To make it more usable, the message itself is irrelevant information, what you care about is the Start and Stop time, so rather than:

    Device      : 00606049
    Timespan    : 
    Timestamp   : 6/26/2015 1:27:09 PM
    Site        : Site02
    Message     : Starting to sync...
    
    Device      : 00606049
    Timespan    : 
    Timestamp   : 6/26/2015 1:27:10 PM
    Site        : Site02
    Message     : Synchronization complete.
    

    you should have one object for each sync like this:

    Device      : 00606049
    Timespan    : 
    StartTimestamp   : 6/26/2015 1:27:09 PM
    StopTimestamp   : 6/26/2015 1:27:10 PM
    Site        : Site02
    

    Then you could easily generate a timespan from your Start and Stop time. To get reports for #1, it would be Group-Object to get a count of device or site from your object. The #2 is would be solved by changing how your are generating your object. The #3 would be using Measure-Object to get the average. You are paring a log with the sychronization? What is generating that log? Is it another Powershell script?.

  • #30409
    Profile photo of Isaac Garvin
    Isaac Garvin
    Participant

    As a notice this was resolved using the method mentioned by Rob. Thank you!

You must be logged in to reply to this topic.