【问题标题】:How to compare Properties of a Hash Table against an Array of Hash Tables in Powershell如何将哈希表的属性与 Powershell 中的哈希表数组进行比较
【发布时间】:2017-09-27 17:23:01
【问题描述】:

我正在尝试找到返回存储在数组中的哈希表的最佳方法,匹配另一个哈希表的所有属性,该哈希表与数组分开。

例如,我有以下 Array 和 Hash Table 变量:

$myarr = @(
    @{"first" = "A";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "E"},
    @{"first" = "M";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "E";"sixth"="F"},
    @{"first" = "A";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "Z"})

$crit = @{"first"="A";"third"="C"}

我需要一种方法来返回存储在数组中的每个完整哈希表,其中所有来自$crit 的属性都匹配。在此示例中,我希望看到 $myarr[0]$myarr[2] 返回。

我可以通过一个一个循环$crit的属性来实现这一点,并将它们与数组中的每个哈希表进行比较,如下所示,但我想看看是否有更好的方法来比较哈希表我想不通,类似于Compare-Object with Arrays。

ForEach ($hash in $myarr) {
    $match = $true
    ForEach ($key in $crit.Keys) {If ($hash.$key -ne $crit.$key) {$match = $false;Break}}
    If ($match) {$hash}}

这样做的最终目标是在内存使用量最少的情况下进行比较,因为现实世界的应用程序将比较数十万个这样的数组,其中包含数百个哈希表,它们都有 100 多个属性。显然,我们感谢您在正确的方向上提供的任何帮助,但我的目标是尽可能简化这种比较。

【问题讨论】:

  • 这是一对多比较过程,还是多对多?
  • @mjolinor 一对多;一个像 $crit 这样的哈希,与像 $myarr 这样的许多数组相比。

标签: arrays powershell hash compare


【解决方案1】:

首先将哈希值转换为对象。我已经包含了两个可能的选项

$myarr = @(
    @{"first" = "A";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "E"},
    @{"first" = "M";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "E";"sixth"="F"},
    @{"first" = "A";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "Z"}
)|ForEach-Object {New-Object -TypeName psobject -Property $_}

# or 

$myarr = @(
    [pscustomobject]@{"first" = "A";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "E"},
    [pscustomobject]@{"first" = "M";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "E";"sixth"="F"},
    [pscustomobject]@{"first" = "A";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "Z"}
)

然后开始过滤集合

  1. 将全套复制到$result
  2. 循环遍历每个条件
    1. 使用Where-Object 过滤此条件
    2. 将过滤后的结果存储到$result 以供下一个循环使用

代码如下所示

$crit = @{"first"="A";"third"="C"}

$result=$myarr
$crit.GetEnumerator()|ForEach-Object {
    $result=$result|Where-Object -Property $_.Name -EQ $_.Value
}
$result

输出是

first  : A
second : B
third  : C
fourth : D
fifth  : E

first  : A
second : B
third  : C
fourth : D
fifth  : Z

【讨论】:

  • 我一直在测试这个解决方案,到目前为止,它是最合适的。我的测试用例运行良好,但是当我添加一组真实数据时,我得到了正确的结果,加上大量的Cannot validate argument on parameter 'Property'. The argument is null or empty. 错误......我试图确定这是为什么,一旦我过滤出了罪魁祸首,我将得到一些与我的蛮力迭代相比的基准。一旦我有更多结果,我可能会将其标记为最佳答案。
  • 我还注意到,根据我的解决方案,您的数据在键和属性方面不一致。 where-object 是当条件键不存在作为成为属性的哈希键时引发错误的那个。我并不期待非常高的性能数字,尤其是与蛮力相比时。即使您不计算将哈希转换为对象的初始时间,管道也会减慢速度。一个小问题,您的数据来自哪里并生成如此多的哈希值以至于性能很重要?
  • 我实际上已经尝试过条件键不存在的场景,并且它没有抛出错误,这也是我最初的想法之一。这些数据来自数百万个文本文件的集合。这些文件的大小范围从几 kb 到略低于 1 gb,并且以不一致的方式分隔。我希望通过这个实现的是,替换为这些文件的每个查询编写一个新的正则表达式。由于体积,性能成为一个问题,但也允许尽可能多的线程/作业同时运行。
  • @DavidWall,不想多管闲事,您是否尝试过将文本文件直接加载到对象而不是哈希表中。我想你已经知道了来自 json 等的Convert-From* cmdlet。例如,yaml 库中甚至还有一个。我问的原因是优化从哈希到pscustomobject 的转换时间。我很好奇,期待您的发现。
  • 我实际上是在使用 Import-CSV 来获取这些文件,我相当肯定它确实创建了一个 PSCustomObject(如果我在这里错了,请纠正我),但在我有限的经验中,处理这个数据,因为我会为我的意图使用哈希。我解决了我遇到的错误,经过一些测试,您的示例快了约 7%,这正是我所追求的。
【解决方案2】:

设计一个基本函数来针对一个哈希表测试您的条件,然后使用Where-Object 过滤哈希表数组。

对于第一部分,我们可以这样做

function Compare-HashtableSubset
{
  param(
    [Parameter(Mandatory,Position=0)]
    [hashtable]$HashTable,

    [Parameter(Mandatory,Position=1)]
    [hashtable]$SubTable
  )

  foreach($entry in $SubTable.GetEnumerator()) {
    if((-not $HashTable.ContainsKey($entry.Key)) -or $HashTable[$entry.Key] -ne $entry.Value){
      # missing key or value mismatch, we're done here
      return $false
    }
  }
  # made it to the end, must be good
  return $true
}

现在我们可以比较两个哈希表,让我们使用它!

PS C:\> $filteredArray = $myarr |Where-Object { Compare-HashtableSubset $_ $crit }
PS C:\> $filteredArray.Count
2

【讨论】:

    【解决方案3】:

    不知道这是否有帮助,但您可以针对测试集运行它,看看它是否比暴力迭代更好:

    $myarr = @(
        @{"first" = "A";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "E"},
        @{"first" = "M";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "E";"sixth"="F"},
        @{"first" = "A";"second" = "B";"third" = "C";"fourth" = "D";"fifth" = "Z"})
    
    $crit = @{"first"="A";"third"="C"}
    
    $match1 = '*"first":  "A"*'
    $match2 = '*"third":  "C"*'
    
     ($myarr |% {$_ | convertto-json}) -like $match1 -like $match2 | convertfrom-json
    

    您可能需要也可能不需要最后一个 convertfrom-json。如果结果为 JSON 是可以接受的,它应该在没有它的情况下运行得更快。它将比蛮力迭代使用更多的内存,但应该一次执行整个数组,而不是一次处理一个哈希表。

    【讨论】:

    • 我非常喜欢这个想法,但是对于我的特定目的,它不能很好地扩展。由于我需要在现实世界场景中进行匹配的数量,like 语句对我来说太麻烦了。
    猜你喜欢
    • 2019-02-03
    • 2015-05-13
    • 2021-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-24
    • 2019-03-03
    • 1970-01-01
    相关资源
    最近更新 更多