我有以下列表:
$list = @('PONTEVEDRA:false:siAPP','MADRID:true:noAPP','MADRID:true:noAPP','PONTEVEDRA:true:siAPP')
我想生成另一个包含以下内容的列表:
$transformedList = @('PONTEVEDRA:1:1:2:0','MADRID:2:1:3:0')
其中 {0} 为地点 {1} 真值总数 {2} 假值总数 {3} siAPP 总数,{4} 为 noAPP 总数,均与其地点相关
我怎样才能在 powershell 中做到这一点?
答案1
我认为群组对象(别名:group) 是此任务的执行 cmdlet。
以下是一种方法:
$SourceList = @('PONTEVEDRA:false','MADRID:true','MADRID:true','PONTEVEDRA:true')
$NewList = $SourceList | ForEach{
$_ -match '(.+):(.+)' | out-null
[PSCustomObject]@{
'Loc' = $matches[1]
'Bool'= $matches[2]
}
} | Group Loc -pv loc | ForEach{
$BoolSplit = $_.Group | Group Bool -NoElement
'{0}:{1}:{2}' -f $loc.Name,
($BoolSplit | ? Name -eq 'True').Count,
($BoolSplit | ? Name -eq 'False').Count
}
$NewList
编辑:包括摘要信息
如果你分解上面的代码来查看管道中的内容,你会看到以下代码段:
$SourceList | ForEach{
$_ -match '(.+):(.+)' | out-null
[PSCustomObject]@{
'Loc' = $matches[1]
'Bool'= $matches[2]
}
}
创建这些对象:
Loc Bool
--- ----
PONTEVEDRA false
MADRID true
MADRID true
PONTEVEDRA true
如果按 分组Bool
,将获得“True”和“False”的总计数:
PS > $SourceList | ForEach{
>> $_ -match '(.+):(.+)' | out-null
>> [PSCustomObject]@{
>> 'Loc' = $matches[1]
>> 'Bool'= $matches[2]
>> }
>> } | Group Bool
Count Name Group
----- ---- -----
1 false {@{Loc=PONTEVEDRA; Bool=false}}
3 true {@{Loc=MADRID; Bool=true}, @{Loc=MADRID; Bool=true}, ...
因此,为了捕获中间对象,需要修改原始代码以创建另一个集合。此段:
$NewList = $SourceList | ForEach{
$_ -match '(.+):(.+)' | out-null
[PSCustomObject]@{
'Loc' = $matches[1]
'Bool'= $matches[2]
}
}
变成这样:
$NewList = ( $SumList = $SourceList | ForEach{
$_ -match '(.+):(.+)' | out-null
[PSCustomObject]@{
'Loc' = $matches[1]
'Bool'= $matches[2]
}
} )
然后,当您运行修改后的代码时,您不仅创建了$NewList
,而且$SumList
还创建了。这样,您就可以分组并选择:
PS > $sumList | Group Bool -NoElement | select Name, Count
Name Count
---- -----
false 1
true 3
因此,完整的修改后的代码如下:
$SourceList = @('PONTEVEDRA:false','MADRID:true','MADRID:true','PONTEVEDRA:true')
$NewList = ( $SumList = $SourceList | ForEach{
$_ -match '(.+):(.+)' | out-null
[PSCustomObject]@{
'Loc' = $matches[1]
'Bool'= $matches[2]
}
} ) | Group Loc -pv loc | ForEach{
$BoolSplit = $_.Group | Group Bool -NoElement
'{0}:{1}:{2}' -f $loc.Name,
($BoolSplit | ? Name -eq 'True').Count,
($BoolSplit | ? Name -eq 'False').Count
}
$NewList
$sumList | Group Bool -NoElement | select Name, Count
编辑3:替代摘要方法:
如果您需要进一步访问对象形式的原始数据,上述方法会很好,但如果您只需要上述总和,则下面的方法可能更有效、更快捷。它不会创建另一个集合或列表,而是使用引用变量来在处理数据时保持总数:
$SourceList = @('PONTEVEDRA:false','MADRID:true','MADRID:true','PONTEVEDRA:true')
[ref]$TrueTotal = 0
[ref]$FalseTotal = 0
$NewList = $SourceList | ForEach{
$_ -match '(.+):(.+)' | out-null
[PSCustomObject]@{
'Loc' = $matches[1]
'Bool'= $matches[2]
}
} | Group Loc -pv loc | ForEach{
$BoolSplit = $_.Group | Group Bool -NoElement
'{0}:{1}:{2}' -f $loc.Name,
($TrueCount = ($BoolSplit | ? Name -eq 'True').Count),
($FalseCount = ($BoolSplit | ? Name -eq 'False').Count)
$TrueTotal.Value += $TrueCount
$FalseTotal.Value += $FalseCount
}
$NewList
'Total "True" : {0}' -f $TrueTotal.Value
'Total "False" : {0}' -f $FalseTotal.Value`
编辑#2:附加数据
与之前一样,您要做的第一件事是将数据转换为对象。这次我采取了略有不同的策略——不是为了混淆,而是为了展示另一种方法。如果您的数据实际上来自文件,您可以使用Import-Csv
而不是ConvertFrom-Csv
:
$list = @('PONTEVEDRA:false:siAPP','MADRID:true:noAPP','MADRID:true:noAPP','PONTEVEDRA:true:siAPP')
$ListObjects = $List | ConvertFrom-Csv -Delimiter ':' -Header ('Loc','locBool','AppInfo')
得出的结果是:
PS > $ListObjects
Loc locBool AppInfo
--- ------- -------
PONTEVEDRA false siAPP
MADRID true noAPP
MADRID true noAPP
PONTEVEDRA true siAPP
再次,分组**Loc**
:
PS > $ListObjects | group Loc
Count Name Group
----- ---- -----
2 PONTEVEDRA {@{Loc=PONTEVEDRA; locBool=false; AppInfo=siAPP; Count=2; Na...
2 MADRID {@{Loc=MADRID; locBool=true; AppInfo=noAPP; Count=2; Name=MA...
并且Group
财产我们可以使用以下方法提取所需的数据:Where
方法可供收藏:
$ListObjects | Group Loc | %{
$BoolGroup = $_.Group | Group locBool -NoElement
$AppGroup = $_.Group | Group AppInfo -NoElement
'{0}:{1}:{2}:{3}:{4}' -f $_.Name ,
$BoolGroup.Where{$_.Name -match 'true'}[0].Count ,
$BoolGroup.Where{$_.Name -match 'false'}[0].Count ,
$AppGroup.Where{$_.Name -match 'siAPP'}[0].Count ,
$AppGroup.Where{$_.Name -match 'noApp'}[0].Count
}
输出:
PS > $ListObjects | Group Loc | %{
>> $BoolGroup = $_.Group | Group locBool -NoElement
>> $AppGroup = $_.Group | Group AppInfo -NoElement
>> '{0}:{1}:{2}:{3}:{4}' -f $_.Name ,
>> $BoolGroup.Where{$_.Name -match 'true'}[0].Count ,
>> $BoolGroup.Where{$_.Name -match 'false'}[0].Count ,
>> $AppGroup.Where{$_.Name -match 'siAPP'}[0].Count ,
>> $AppGroup.Where{$_.Name -match 'noApp'}[0].Count
>> }
>>
PONTEVEDRA:1:1:2:0
MADRID:2:0:0:2
PS >
请注意,由于该Where()
方法即使有零个或一个元素也会返回一个集合 -- 并且集合具有Count
自己的属性,因此您必须指定数组索引才能从和子组中[0]
获取。如果没有这个,将只返回或-- 取决于指定分组中是否有任何元素。Count
BoolGroup
AppGroup
Count
0
1