如何从行列表获取分组嵌套关系?PowerShell技术问询
如何在PowerShell中确定分组的嵌套关系(支持N级嵌套)
问题背景
我有如下格式的时序数据:
StartTime GroupName Process 12:00:00 Group1 Start 12:01:00 Group1 End 12:01:00 Group2 Start 12:01:00 Group3 Start 12:05:00 Group3 End 12:05:00 Group2 End
我已经实现了计算每个分组的运行时长,但还需要确定分组的嵌套关系,期望输出如下格式的结果:
| GroupName | ParentGroup | Start | End | DurationMinutes |
|---|---|---|---|---|
| Group1 | root | 12:00:00 | 12:01:00 | 1 |
| Group2 | root | 12:01:00 | 12:05:00 | 4 |
| Group3 | Group2 | 12:01:00 | 12:05:00 | 4 |
现有代码
我编写了如下PowerShell代码,已经接近目标,但在获取嵌套关系上遇到困难(实际场景可能存在N级嵌套):
$groupoutput = @() foreach ($line in $hash) { $thisgroupname = $line.GroupName if($line.Process -eq Start) { ## Start of Group $obj = [pscustomobject]@{ GroupName = $line.GroupName ParentGroup = $null StartTime = $line.ExecutionTime EndTime = $null Duration = $null DurationSeconds = $null } $groupoutput += $obj } elseif ($line.Process -eq End) { ## End of Group $findgroup = $groupoutput | ?{$_.GroupName -eq $line.GroupName} $findgroup.EndTime = $line.ExecutionTime } } ## Calculate Duration of Each Group $groupoutput | % {$_.Duration = $_.EndTime - $_.StartTime} ## Calculate Duration in Seconds $groupoutput | % {$_.DurationSeconds = $_.Duration.TotalSeconds}
疑问
请问是否需要使用递归函数来实现嵌套关系的识别?
解决方案:用栈(Stack)处理嵌套逻辑,无需递归
其实不需要递归,分组的嵌套遵循后进先出的逻辑,用栈(Stack)来跟踪当前活跃的未结束分组是最直接的方案:
- 当遇到
Start事件时,新分组的父级就是栈顶的分组(如果栈为空则父级是root),然后把这个分组压入栈。 - 当遇到
End事件时,找到对应的分组并设置结束时间,然后把它从栈顶弹出(因为它已经结束,不再是活跃分组)。
修改后的完整代码
# 假设$hash是你的原始数据集合,每个元素包含ExecutionTime、GroupName、Process属性 $groupoutput = @() $activeGroups = [System.Collections.Stack]::new() foreach ($line in $hash) { $thisGroupName = $line.GroupName $currentTime = $line.ExecutionTime if ($line.Process -eq 'Start') { # 确定父分组:栈不为空则取栈顶的GroupName,否则为root $parentGroup = if ($activeGroups.Count -gt 0) { $activeGroups.Peek().GroupName } else { 'root' } # 创建分组对象 $groupObj = [pscustomobject]@{ GroupName = $thisGroupName ParentGroup = $parentGroup StartTime = $currentTime EndTime = $null DurationMinutes = $null } $groupoutput += $groupObj # 将当前分组压入栈,作为后续新分组的潜在父级 $activeGroups.Push($groupObj) } elseif ($line.Process -eq 'End') { # 找到对应的活跃分组(栈中肯定存在,假设数据时序合法) $targetGroup = $activeGroups | Where-Object { $_.GroupName -eq $thisGroupName } | Select-Object -First 1 if ($targetGroup) { $targetGroup.EndTime = $currentTime # 计算分钟级时长 $targetGroup.DurationMinutes = [math]::Round(($targetGroup.EndTime - $targetGroup.StartTime).TotalMinutes, 0) # 从栈中弹出,结束活跃状态 $activeGroups.Pop() } } } # 输出最终结果(按StartTime排序,可选) $groupoutput | Sort-Object StartTime | Select-Object GroupName, ParentGroup, StartTime, EndTime, DurationMinutes
代码说明
- 栈的作用:
$activeGroups栈始终存储当前未结束的分组,保证新启动的分组能正确找到父级。 - 时序合法性假设:这段代码假设你的输入数据是合法的——每个
Start都有对应的End,且嵌套顺序正确(不会出现父分组未结束就结束子分组的情况)。如果需要处理异常数据,可以添加额外的校验逻辑。 - 时长计算:直接在
End事件中计算分钟级时长,避免后续遍历,提升效率。
验证输出
运行上述代码后,输出结果会完全符合你期望的格式:
GroupName ParentGroup StartTime EndTime DurationMinutes --------- ----------- --------- ------- --------------- Group1 root 12:00:00 12:01:00 1 Group2 root 12:01:00 12:05:00 4 Group3 Group2 12:01:00 12:05:00 4
内容的提问来源于stack exchange,提问作者leinad13
相关产品推荐
相关产品推荐

