Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/xml/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Powershell 构建Hashmaps的Hashmap_Powershell_Hashmap_Hashtable - Fatal编程技术网

Powershell 构建Hashmaps的Hashmap

Powershell 构建Hashmaps的Hashmap,powershell,hashmap,hashtable,Powershell,Hashmap,Hashtable,我不经常问问题(大多数情况下,这个问题可以通过一些研究来解决,对吗?),但我只想听听你的意见,因为可能有更好(更有效)的方法 让我们看一下,下面的代码工作得非常好,它满足了它的目的。代码的结果是hashmap的hashmap,我需要它作为另一个工作的查找表 背景: $ccDb是一个由大约20万个项目组成的数组,其属性为companyCd、costCenterNbr、costCenterShortNm、costCenterLongDescr 每个属性都必须修剪(请不要要求我修剪我的Db,遗憾的是

我不经常问问题(大多数情况下,这个问题可以通过一些研究来解决,对吗?),但我只想听听你的意见,因为可能有更好(更有效)的方法

让我们看一下,下面的代码工作得非常好,它满足了它的目的。代码的结果是hashmap的hashmap,我需要它作为另一个工作的查找表

背景:

  • $ccDb
    是一个由大约20万个项目组成的数组,其属性为
    companyCd、costCenterNbr、costCenterShortNm、costCenterLongDescr
  • 每个属性都必须修剪(请不要要求我修剪我的Db,遗憾的是我不能)
  • costCenterNbr
    包含在
    companyCd
    上,这意味着每个
    companyCd
    可以有多个
    costCenterNbr
  • companyCd
    可以包含X个数量的
    costCenterNbr
  • costCenterNbr
    具有唯一值,与
    companyCd
    相同
  • costCenterShortNm
    costCenterLongDescr
    costCenterNbr
问题是:

必须在每次运行脚本时构造此映射,因为信息取自SQL表(SQL表一直在更改)。构建此地图大约需要15分钟(在一个相当好的服务器上,2CPUs 12核)

问题是:

您是否看到可以改进此代码以更快/更高效地执行的方法

$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命令
    的方法相同:
  • 等待代码完成
  • 查看输出:
  • 请注意,第12行占用了最多的总执行时间—明显多于任何其他行:

           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