Powershell 构建Hashmaps的Hashmap
我不经常问问题(大多数情况下,这个问题可以通过一些研究来解决,对吗?),但我只想听听你的意见,因为可能有更好(更有效)的方法 让我们看一下,下面的代码工作得非常好,它满足了它的目的。代码的结果是hashmap的hashmap,我需要它作为另一个工作的查找表 背景:Powershell 构建Hashmaps的Hashmap,powershell,hashmap,hashtable,Powershell,Hashmap,Hashtable,我不经常问问题(大多数情况下,这个问题可以通过一些研究来解决,对吗?),但我只想听听你的意见,因为可能有更好(更有效)的方法 让我们看一下,下面的代码工作得非常好,它满足了它的目的。代码的结果是hashmap的hashmap,我需要它作为另一个工作的查找表 背景: $ccDb是一个由大约20万个项目组成的数组,其属性为companyCd、costCenterNbr、costCenterShortNm、costCenterLongDescr 每个属性都必须修剪(请不要要求我修剪我的Db,遗憾的是
是一个由大约20万个项目组成的数组,其属性为$ccDb
companyCd、costCenterNbr、costCenterShortNm、costCenterLongDescr
- 每个属性都必须修剪(请不要要求我修剪我的Db,遗憾的是我不能)
包含在costCenterNbr
上,这意味着每个companyCd
可以有多个companyCd
costCenterNbr
可以包含X个数量的companyCd
costCenterNbr
具有唯一值,与costCenterNbr
相同companyCd
和costCenterShortNm
与costCenterLongDescr
costCenterNbr
$ccMap=@{}
foreach($line in $ccDb)
{
$companyCd=$line.companyCd.trim()
$costCenterNbr=$line.costCenterNbr.trim()
$costCenterShortNm=$line.CostCenterShortNm.trim()
$costCenterLongDescr=$line.CostCenterLongDescr.trim()
$coceMap=@{
$costCenterNbr=@{
shortDesc=$costCenterShortNm
longDesc=$costCenterLongDescr
}
}
if($ccMap.ContainsKey($companyCd))
{
$ccMap[$companyCd]+=$coceMap
}
else
{
$ccMap.Add($companyCd,$coceMap)
}
}
对不起,我解释得太长了,但我觉得最好事先提供最多的信息。非常感谢您的帮助。此外,我知道PowerShell对于我正在做的事情来说是一种非常糟糕的语言,C#可能会更有效,但它就是这样
编辑:添加测量值以供参考
编辑:
非常感谢@Mathias R.Jessen,以下是他的代码的测量结果。优秀的代码
我有两个建议:
您根本不需要
if
\else
\.add()
语句,powershell将根据需要添加密钥。这将减少大部分时间,因为您没有在整个表中搜索每个条目:
$ccMap[$companyCd]+=$coceMap
如果只使用一次值,则不需要在上面设置变量。只需使用您的$行
:
$coceMap=@{
$line.costCenterNbr.trim()=@{
shortDesc = $line.CostCenterShortNm.trim()
longDesc = $line.CostCenterLongDescr.trim()
}
}
不要在紧循环中使用+=
这是你最大的水槽:
$ccMap[$companyCd] += $coceMap
当您使用+
将一个哈希表添加到另一个哈希表时(或在这方面使用+=
),PowerShell将创建一个全新的哈希表:
运行此命令,您会发现$B
和$memory
没有更改,但$A
具有两个键,因此必须是新键
要避免这种性能损失,请完全跳过$coceMap
的构造,并颠倒顺序(如果不存在,请先构造哈希表,然后分配):
基准测试
+=
下面是一个简单的示例,演示了使用50个Distinct键与10000个项目之间的差异:
$data = @(
1..10000 |Select-Object @{Name='Company';Expression={Get-Random -Maximum 50}},@{Name='CostCenter';Expression={Get-Random}}
)
@(
Measure-Command {
$map = @{}
foreach($line in $data){
$entry = @{
$line.CostCenter = @{
Value = 123
}
}
if($map.ContainsKey($line.Company)){
$map[$line.Company] += $entry
}
else {
$map[$line.Company] = $entry
}
}
}
Measure-Command {
$map = @{}
foreach($line in $data){
if($map.ContainsKey($line.Company)){
$entry = $map[$line.Company]
}
else {
$entry = $map[$line.Company] = @{}
}
$entry[$line.CostCenter] = @{
Value = 123
}
}
}
) |select TotalMilliseconds
在我的笔记本电脑上显示:
TotalMilliseconds
-----------------
306.4218
47.8164
一般来说,如何识别这样的时间下沉? 有很多方法可以分析PowerShell的运行时行为,但以下是我个人的第一选择:
PSProfiler
的维护者):
安装模块PSProfiler-范围CurrentUser
Measure脚本
的方法与使用Measure命令
的方法相同: 9950 12 00:00.3965227 $map[$line.Company] += $entry
您可以使用-ashtable开关查看组对象。听起来您正在进行经典的数据管理。一个经典的数据库管理系统(DBMS)非常适合这种事情。您的数据显然来自SQL,所以我假设它来自DBMS。为什么不在SQL中进行数据转换和缩减,然后将缩减后的数据传递到Powershell?是的,已经这样做了,组对象的速度要慢得多。在
$ccDb
中是否有许多重复的companyCd
值?:-)您只是在使用SQL模块吗?您也应该能够让DB为您修剪它“powershell将根据需要添加密钥”。@MathiasR.Jessen在这里是正确的,无法判断此工作是否正常,因为它运行了20分钟以上,所以我决定停止它。谢谢!让我检查一下,我将很快用时间度量进行更新。我可能在这里出错,但这不会产生任何输出$coceMap=$ccMap.Add($companyCd,@{})
。我遇到以下异常:System.Management.Automation.RuntimeException:无法索引到空数组。
@santisq My bad,answer update好吧,你疯了。我试图理解你的代码,但这太聪明了。查看我的编辑。非常感谢您的帮助。@santisq我添加了一个更简单的示例,以及我如何进行测量以识别它,也许它可以帮助您进行推理?
TotalMilliseconds
-----------------
306.4218
47.8164
Measure-Script {
$map = @{}
foreach($line in $data){
$entry = @{
$line.CostCenter = @{
Value = 123
}
}
if($map.ContainsKey($line.Company)){
$map[$line.Company] += $entry
}
else {
$map[$line.Company] = $entry
}
}
}
Anonymous ScriptBlock
Count Line Time Taken Statement
----- ---- ---------- ---------
0 1 00:00.0000000 {
1 2 00:00.0000187 $map = @{}
0 3 00:00.0000000
0 4 00:00.0000000 foreach($line in $data){
10000 5 00:00.0635585 $entry = @{
0 6 00:00.0000000 $line.CostCenter = @{
0 7 00:00.0000000 Value = 123
0 8 00:00.0000000 }
0 9 00:00.0000000 }
0 10 00:00.0000000
0 11 00:00.0000000 if($map.ContainsKey($line.Company)){
9950 12 00:00.3965227 $map[$line.Company] += $entry
0 13 00:00.0000000 }
0 14 00:00.0000000 else {
50 15 00:00.0002810 $map[$line.Company] = $entry
0 16 00:00.0000000 }
0 17 00:00.0000000 }
0 18 00:00.0000000 }
9950 12 00:00.3965227 $map[$line.Company] += $entry