【问题标题】:How can I enumerate a hashtable as key-value pairs / filter a hashtable by a collection of key values如何将哈希表枚举为键值对/通过键值集合过滤哈希表
【发布时间】:2016-10-04 18:51:30
【问题描述】:

编者注: 这个问题有一个复杂的历史,但归结为:
* 要了解如何通过 键值对枚举哈希表的条目,请参阅the accepted answer
* 要了解如何通过键值集合
过滤哈希表,请参阅the other answer


我想我又陷入了 X Y 问题,我最初的问题是关于过滤哈希表。我发现在创建哈希表之前过滤更容易。问题回答了,对吧?

不,Y 问题是循环每个键并使用 @briantist 帮助我解决的值。

我的目标是遍历键名,即时间戳,并使用键名作为任务名称和触发器来安排任务。

我正在使用Group-Object -AsHashTable -AsString-edit 从 CSV 文件创建哈希表,这里值得一提的是,在创建 HashTable 之前过滤 CSV 只会让Pipeline 或脚本变得更容易

举个例子:

Import-CSV (ls -path D:\ -Filter source*.csv | sort LastWriteTime | Select -Last 1).FullName |
 where {$_.TimeCorrected -ne 'ManualRebootServer'} |
 group TimeCorrected -AsHashTable -AsString

我正在尝试遍历键名并能够使用以下方式显示键名:

$var = Import-Csv csv123.csv | Group-Object Value1 -AsHashTable -AsString

foreach ($key in $var.Keys){"The key name is $key"}

#Create a scheduled task named and triggered based on the HashTable keyname
#test test test
foreach ($key in $var.keys){IF($key -ne 'ManualRebootServer'){"Register-ScheduledJob"}}

我只是不确定如何从我感兴趣的键中获取值。

我发现以下作品,但仅当我手动输入密钥名称时。我只是不确定如何组合这两个循环。

($val.GetEnumerator() | Where {$_.key -eq '06-11-16 18:00'} | ForEach-Object { $_.value }).Server

【问题讨论】:

  • 只是为了解释一下,过滤(至少在 PowerShell 中)总是最好在管道运算符 | 之前或左侧完成,以使脚本更高效。
  • 确实;换一种说法:使用带有-Filter 参数(如果可用)的提供程序原生过滤通常比通过管道传递未过滤的输出要快得多,以便让Where-Object cmdlet 稍后执行过滤。

标签: powershell hashtable


【解决方案1】:

通过专注于通过键值数组过滤哈希表(PSv3+ 语法)来补充briantist's helpful answer

# Sample hashtable.
$ht = @{ one = 1; two = 2; three = 3 }

# Filter it by an array of key values; applying .GetEnumerator() yields an array
# of [System.Collections.DictionaryEntry] instances, which have
# a .Key property and a .Value property.
$ht.GetEnumerator()  | ? Key -in 'one', 'two'

# Similarly, the *output* - even though it *looks* like a hashtable - 
# is a regular PS *array* ([Object[]]) containing [System.Collections.DictionaryEntry]
# entries (2 in this case).
$arrFilteredEntries = $ht.GetEnumerator()  | ? Key -in 'one', 'two'
$arrFilteredEntries.GetType().Name # -> Object[]

要进一步处理匹配的键值对,只需管道到% (ForEach-Object) 并访问$_.Key$_.Value(值):

$ht.GetEnumerator()  | ? Key -in 'one', 'two' | 
  % { "Value for key '$($_.Key)': $($_.Value)" }

等效命令使用更高效的foreach循环代替管道:

foreach ($key in $ht.Keys) { 
  if ($key -in 'one', 'two') { "Value for key '$($key)': $($ht.$key)" }
}

注意:在 PSv2 中:
* 不支持运算符-in,但您可以使用-contains 来代替操作数交换
'one', 'two' -contains $key
* 在管道中,使用Where-Object { 'one', 'two' -contains $_.Key }

使用示例哈希表,这会产生:

Value for key 'two': 2
Value for key 'one': 1

注意输出中的键顺序与定义顺序有何不同;在 PSv3+ 中,您可以创建 ordered 哈希表 ([ordered] @{ ... }) 以保留定义顺序。

上面使用的键过滤技术仅限于通过文字键数组进行过滤;任何(字符串)集合都将作为 -in 操作数的 RHS,例如 不同 哈希表的 .Keys 集合:

# Sample input hashtable.
$htInput = @{ one = 1; two = 2; three = 3 }

# Hashtable by whose keys the input hashtable should be filtered.
# Note that the entries' *values* are irrelevant here.
$htFilterKeys = @{ one = $null; two = $null }

# Perform filtering.
$htInput.GetEnumerator()  | ? Key -in $htFilterKeys.Keys | 
  % { "Value for key '$($_.Key)': $($_.Value)" }

# `foreach` loop equivalent:
foreach ($key in $htInput.Keys) {
  if ($key -in $htFilterKeys.Keys) { "Value for key '$($key)': $($htInput.$key)" }
}

结果与静态过滤键数组的示例相同。

最后,如果您想过滤一个哈希表就地创建一个只包含过滤条目的哈希表强>:

# *In-place* Updating of the hashtable.
# Remove entries other than the ones matching the specified keys.
# Note: The @(...) around $ht.Keys is needed to clone the keys collection before
# enumeration, so that you don't get an error about modifying a collection
# while it is being enumerated.
foreach ($key in @($ht.Keys)) { 
  if ($key -notin 'one', 'two') { $ht.Remove($key) } 
} 

# Create a *new* hashtable with only the filtered entries.
# By accessing the original's .Keys collection, the need for @(...) is obviated.
$htNew = $ht.Clone()
foreach ($key in $ht.Keys) { 
  if ($key -notin 'one', 'two') { $htNew.Remove($key) }
} 

顺便说一句:

[System.Collections.DictionaryEntry] 的默认输出格式(因此哈希表 ([System.Collections.Hashtable]) 使用列名 Name 而不是 KeyName 被定义为 @ 的 别名属性 987654346@ 由 PowerShell 添加(它不是 [System.Collections.DictionaryEntry].NET type definition 的一部分;请使用
@{ one = 1 }.GetEnumerator() | Get-Member 验证)。

【讨论】:

    【解决方案2】:

    这里有一些选择。

    通过键枚举:

    foreach ($key in $var.Keys) {
        $value = $var[$key]
        # or
        $value = $var.$key 
    }
    

    枚举键值对(您已经发现,但可能没有有效使用):

    foreach ($kvp in $var.GetEnumerator()) {
        $key = $kvp.Key
        $val = $kvp.Value
    }
    

    【讨论】:

    • 我已经尝试了第一个块中的代码,使用 IF 语句,因为我想跳过一个键。这工作$value = $val[$key].server 但我有进一步的过滤工作需要在此之后完成。是否可以像将$value 放入数组并在事后过滤一样简单?
    • @user4317867 如果您能更明确地说明您最终要完成的工作,这将有所帮助,因为我只能根据您目前所说的进行推测。 “进一步的过滤工作”可能意味着很多事情。
    • 抱歉,这个问题我花了半天时间!值得指出的是,在创建哈希表之前过滤 CSV,例如 Import-CSV file.csv | where {$_.Column -ne 'String'} 以使事情变得更容易!
    • 巧妙地总结了要掌握的2个成语。可惜Powershell没有那么麻烦的.GetEnumerator()。
    猜你喜欢
    • 2014-11-22
    • 2015-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-19
    • 2021-12-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多