我正在寻找两个数组的负面交集。每个阵列都有大约20k个元素。我在一个数组上使用foreach循环,并在另一个数组中查找每个值。我只保留第二个数组中找不到的第一个数组中的元素:
$deadpaths=@()
$ix=0
ForEach ($f in $FSBuildIDs)
{
if (-not($blArray -like $f)) {$deadpaths+=$paths[$ix]}
$ix++
}
$ blArray包含有效的ID。 $ FSBuildIDs包含与$ paths中的文件系统路径对应的ID。目的是仅将$ FSBuildIDS中相应ID的$ path中的元素保留在$ blArray中。
有更好的方法吗?这里的处理需要很长时间。 $ blArray和$ FSBuildID都有大约20k元素,我怀疑我正在看On ^ 2比较。
我想过使用带有$ FSBuildIDs元素的Dictionary作为键和$ path作为值,但是我无法从文档中找出如何初始化和加载Dictionary(假设这种方法可以加快速度) )。显然负设置交叉点是最好的,但这不是TSQL,我很痛苦地意识到即使PS的V4也不支持设置操作。
在这个问题中使用字典会加快比较吗?如果是这样,我如何从$ FSBuildIDs和$ paths创建它?任何其他技术可能会给我带来性能提升而只是迭代这些大(ish)列表?
$ blArray的示例数据:
51012
51044
51049
51055
51058
51060
51073
51074
51077
51085
$ FSBuildIDs的示例数据:
51001
51003
51005
51009
51013
51017
51018
51020
51021
51024
51026
$ path的示例数据:
\\server1\d$\software\anthill\var\artifacts\0000\3774\0000\3792\0005\2335
\\server1\d$\software\anthill\var\artifacts\0000\3774\0000\3792\0005\2336
\\server1\d$\software\anthill\var\artifacts\0000\3774\0000\3792\0005\2337
\\server1\d$\software\anthill\var\artifacts\0000\3774\0000\3792\0005\2338
\\server1\d$\software\anthill\var\artifacts\0000\3774\0000\3792\0005\2339
\\server1\d$\software\anthill\var\artifacts\0000\3774\0000\3792\0005\2340
\\server1\d$\software\anthill\var\artifacts\0000\3774\0000\3792\0005\2341
这类似于先前提出的问题,但在某些方面有所不同。我基本上是在寻找从两个现有数组构建字典的指导。我在发帖后意识到我真的需要一个来自$ blarray的字典作为键,并且可能是$ True作为值。价值无关紧要。重要的测试是$ $ BizBuildIDs中的当前值是否在$ blarray中找到。这可以是基于ID作为关键字的字典查找。这应该加快处理速度,对吧?
我不清楚我每次都在摧毁和重新创建数组的评论。那是$ deadPaths数组吗?只需添加它就会导致这种情况?如果是这样我会更好地使用.Net ArrayList?
答案 0 :(得分:0)
我认为这将是您正在寻找的开始。正如评论中所讨论的,我们将进行两次比较。首先要获取BuildID,我们需要从$FSBuildIDs
和$blArray
进行比较,然后我们将结果与$paths
列表进行比较。我将假设它现在只是一个字符串数组路径。请注意,此处存在错误预防和纠正的余地。现在还在测试。
$parsedIDs = Compare-Object $blArray $FSBuildIDs | Where{$_.SideIndicator -eq "=>"} | Select-Object -ExpandProperty InputObject
$paths = $paths | ForEach-Object{
$_ | Add-Member -MemberType NoteProperty -Name BuildID -Value (($_.Parent.Name + $_.Name) -as [int32]) -PassThru
}
$paths | Where-Object{$_.BuildID -in $parsedIDs}
首先,我们比较两个ID数组并保留$FSBuildIDs
的唯一元素。
接下来我们浏览$paths
。对于每一个,我们添加一个包含buildid
的属性。其中buildid
是连接的最后两个路径元素并转换为整数。
一旦我们得到一个简单的Where-Object
给我们第一次比较中存在id的路径。
答案 1 :(得分:0)
使用-contains
运算符代替-like
可以实现显着改进。
当-like
操作的左侧是一个数组时,PowerShell将迭代该数组并对每个条目执行-like
比较。
-contains
会在找到匹配后立即返回。
考虑以下示例:
$array1 = 1..2000
$array2 = 2..2001
$like = Measure-Command {
foreach($i in $array2){
$array1 -like $i
}
} |Select -Expand TotalMilliseconds
$contains = Measure-Command {
foreach($i in $array2){
$array1 -contains $i
}
} |Select -Expand TotalMilliseconds
Write-Host "Operation with -like took: $($like)ms"
Write-Host "Operation with -contains took: $($contains)ms"
就像在你的真实世界的例子中一样,我们有2个具有大重叠的整数数组。让我们看看它在我的Windows 7笔记本电脑(PowerShell 4.0)上的表现如何:
我认为结果不言自明: - )
话虽如此,正如您所预料的那样,您可以通过填充哈希表来实现更大的改进,使用第一个数组中的值作为键:
$hashtable = $array1 |ForEach-Object -Begin {$t = @{}} -Process {
$t[$_] = $null
# the value doesn't matter, we're only interested in the key lookup
} -End { $t }
然后在哈希表上使用ContainsKey()
方法而不是-like
:
foreach($i in $array2){
if($hashtable.ContainsKey($i)) { # do stuff }
}
您需要提高数组的大小以查看实际差异(此处使用第一个数组中的20K项目):
答案 2 :(得分:0)
回答有关构建哈希表的问题:
$keyEnumerator = $FSBuildIDs.GetEnumerator()
$valEnumerator = $paths.GetEnumerator()
$idPathHash = @{}
foreach ($key in $keyEnumerator ) {
$null = $valEnumerator.movenext()
$idPathHash[$key] = $valEnumerator.current
}
在我的系统上使用20000个元素的假数据数组运行此代码需要138毫秒。
构建不在$ idPathHash中的构建ID列表:
$buildIDsNotIn =
foreach ($buildId in $blArray) {
if (!$idPathHash.ContainsKey($buildId )) {
$buildId
}
}
我的系统花了50毫秒,$ blArray中有20000个项目,还有假数据。