Design Problem: Nested Loop Nightmare

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.

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

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