Powershell 散列:是否有 adding/updating 值的简单 was?
Powershell hashes: Is there an easy was of adding/updating values?
在 Powershell 中,您可以使用 .Add 将新的 key/value 对插入到现有哈希中。
如果散列已经包含密钥,则会导致错误。
对于已知键,期望的(我 :) 行为只是将现有值更新为提供的值。
我可以用很多墨水来做这个。将 .Add 命令放在 try 短语中并捕捉值的变化 - 这工作正常,但墨水成本!
说真的,因为我在解析多个配置时到处都有这种逻辑(这是已经设置并需要更新还是新设置?),这会导致代码混乱:
# $msHashtable is potentially empty at this point or may not contain the key
try {
$myHashtable.Add($thisKey, $thisValue)
}
catch {
$myHashtable.$thisKey = $thisValue
}
我遇到的另一个哈希问题是:
- 假设您有一个哈希表 $motherOfAll,它最终将包含其他哈希表,而其他哈希表又将包含哈希表。
- 现在你想在哈希表的最底层插入一些东西。您首先需要检查沿途的所有哈希表是否存在并包含正确的键。
- 如果不是,则必须插入一堆空哈希表,然后再用另一个空哈希表填充……当然不是无限循环,但仍然很丑陋。
代码比较乱。有没有更好的方法?
如果需要我可以提供代码,但我希望问题足够清楚。由于在我的真实世界示例中除了相关部分之外还有很多其他代码,所以我现在不会发布它...
最佳,
YeOldHinnerk
使用 index operator 键引用特定条目,然后为该条目分配一个新值:
$hashtable = @{}
# this will add a new key/value entry
$hashtable['abc'] = 1
# this will overwrite the existing value associated with the key `abc`
$hashtable['abc'] = 2
如果您有一个大型代码库,其中包含许多对 .Add($key, $value)
的现有调用,并且希望避免重构每个调用,您可以修改哈希表本身的行为,以便 Add
像索引器:
function New-NonStrictHashTable {
return @{} |Add-Member -MemberType ScriptMethod -Name Add -Value {
param($key,$value)
$this[$key] = $value
} -Force -PassThru
}
现在你可以做:
# Create hashtable with Add() overriden
$hashtable = New-NonStrictHashTable
$key,$value = 'key','value'
# This works like before
$hashtable.Add($key, $value)
# This works too now, it simply updates the existing entry
$hashtable.Add($key, 'some other value')
这将适用于任何调用 $hashtable.Add()
的 PowerShell 脚本语句,因为 ETS 方法的解析(就像我们使用 Add-Member
附加到哈希表的方法) ) 优先于基础 .NET 方法。
Another issue with hashes that I have is this:
- Assume you have a hashtabel $motherOfAll which will eventually contain other hashtables, which in turn will also contain hashtables.
- Now you want to insert something into the bottommost layer of hashtables. You first need to check, that all the hashtables along the
way exist and contain the proper keys.
- If not, you have to insert a bunch of empty hashtables, which get filled with another empty one... not ad infinitum of course, but still
ugly. More messy code. Is there a better way?
您在此处描述的所需行为可在 Perl 中找到,称为 autovivification:
my %users;
# the nested hashes $users{YeOldHinnerk} and $users{YeOldHinnerk}{contact_details}
# will automatically come into existence when this assignment is evaluated
$users{YeOldHinnerk}{contact_details}{email_address} = "yoh@domain.tld"
上面链接的维基百科文章给出了如何在 C# 中实现类似行为的示例,可以按如下方式针对 PowerShell 进行调整:
Add-Type -TypeDefinition @'
using System.Collections.Generic;
public class AVD
{
private Dictionary<string, object> _data = new Dictionary<string, object>();
public object this[string key]
{
get {
if(!_data.ContainsKey(key)){
_data[key] = new AVD();
}
return _data[key];
}
set {
_data[key] = value;
}
}
}
'@
现在我们可以利用 PowerShell 的原生索引访问语法:
PS ~> $autovivifyingHashtable = [AVD]::new()
PS ~> $autovivifyingHashtable['a']['b']['c'] = 123
PS ~> $autovivifyingHashtable['a'] -is [AVD]
True
PS ~> $autovivifyingHashtable['a']['b'] -is [AVD]
True
PS ~> $autovivifyingHashtable['a']['b']['c']
123
您的问题的第一部分已经 。
这是我在第二部分的尝试——如何分配嵌套哈希表的成员。在创建任何尚未存在的父哈希表时,没有简单的内置语法来设置嵌套值,因此我为此创建了一个可重用函数 Set-TreeValue
。
function Set-TreeValue( $ht, [String] $path, $value ) {
# To detect errors like trying to set child of value-type leafs.
Set-StrictMode -Version 3.0
do {
# Split into root key and path remainder (", 2" -> split into max. 2 parts)
$key, $path = $path -split '\.', 2
if( $path ) {
# We have multiple path components, so we may have to create nested hash table.
if( -not $ht.Contains( $key ) ) {
$ht[ $key ] = [ordered] @{}
}
# Enter sub tree.
$ht = $ht[ $key ]
}
else {
# We have arrived at the leaf -> set its value
$ht[ $key ] = $value
}
}
while( $path )
}
演示:
$ht = [ordered] @{}
Set-TreeValue $ht foo.bar.baz 42 # Create new value and any non-existing parents
Set-TreeValue $ht foo.bar.baz 8 # Update existing value
Set-TreeValue $ht foo.bar.bam 23 # Add another leaf
Set-TreeValue $ht fop 4 # Set a leaf at root level
#Set-TreeValue $ht fop.zop 16 # Outputs an error, because .fop is a leaf
Set-TreeValue $ht 'foo bar' 15 # Use a path that contains spaces
$ht | ConvertTo-Json # Output the content of the hash table
输出:
{
"foo": {
"bar": {
"baz": 8,
"bam": 23
}
},
"fop": 4,
"foo bar": 15
}
注意: 我选择将嵌套哈希表创建为 OrderedDictionary
,因为它们比常规哈希表更有用(例如,确保 [= =33=]输出)。如果您想要无序哈希表(可能具有轻微的性能优势),请删除 [ordered]
。
在 Powershell 中,您可以使用 .Add 将新的 key/value 对插入到现有哈希中。 如果散列已经包含密钥,则会导致错误。 对于已知键,期望的(我 :) 行为只是将现有值更新为提供的值。 我可以用很多墨水来做这个。将 .Add 命令放在 try 短语中并捕捉值的变化 - 这工作正常,但墨水成本!
说真的,因为我在解析多个配置时到处都有这种逻辑(这是已经设置并需要更新还是新设置?),这会导致代码混乱:
# $msHashtable is potentially empty at this point or may not contain the key
try {
$myHashtable.Add($thisKey, $thisValue)
}
catch {
$myHashtable.$thisKey = $thisValue
}
我遇到的另一个哈希问题是:
- 假设您有一个哈希表 $motherOfAll,它最终将包含其他哈希表,而其他哈希表又将包含哈希表。
- 现在你想在哈希表的最底层插入一些东西。您首先需要检查沿途的所有哈希表是否存在并包含正确的键。
- 如果不是,则必须插入一堆空哈希表,然后再用另一个空哈希表填充……当然不是无限循环,但仍然很丑陋。 代码比较乱。有没有更好的方法?
如果需要我可以提供代码,但我希望问题足够清楚。由于在我的真实世界示例中除了相关部分之外还有很多其他代码,所以我现在不会发布它...
最佳,
YeOldHinnerk
使用 index operator 键引用特定条目,然后为该条目分配一个新值:
$hashtable = @{}
# this will add a new key/value entry
$hashtable['abc'] = 1
# this will overwrite the existing value associated with the key `abc`
$hashtable['abc'] = 2
如果您有一个大型代码库,其中包含许多对 .Add($key, $value)
的现有调用,并且希望避免重构每个调用,您可以修改哈希表本身的行为,以便 Add
像索引器:
function New-NonStrictHashTable {
return @{} |Add-Member -MemberType ScriptMethod -Name Add -Value {
param($key,$value)
$this[$key] = $value
} -Force -PassThru
}
现在你可以做:
# Create hashtable with Add() overriden
$hashtable = New-NonStrictHashTable
$key,$value = 'key','value'
# This works like before
$hashtable.Add($key, $value)
# This works too now, it simply updates the existing entry
$hashtable.Add($key, 'some other value')
这将适用于任何调用 $hashtable.Add()
的 PowerShell 脚本语句,因为 ETS 方法的解析(就像我们使用 Add-Member
附加到哈希表的方法) ) 优先于基础 .NET 方法。
Another issue with hashes that I have is this:
- Assume you have a hashtabel $motherOfAll which will eventually contain other hashtables, which in turn will also contain hashtables.
- Now you want to insert something into the bottommost layer of hashtables. You first need to check, that all the hashtables along the way exist and contain the proper keys.
- If not, you have to insert a bunch of empty hashtables, which get filled with another empty one... not ad infinitum of course, but still ugly. More messy code. Is there a better way?
您在此处描述的所需行为可在 Perl 中找到,称为 autovivification:
my %users;
# the nested hashes $users{YeOldHinnerk} and $users{YeOldHinnerk}{contact_details}
# will automatically come into existence when this assignment is evaluated
$users{YeOldHinnerk}{contact_details}{email_address} = "yoh@domain.tld"
上面链接的维基百科文章给出了如何在 C# 中实现类似行为的示例,可以按如下方式针对 PowerShell 进行调整:
Add-Type -TypeDefinition @'
using System.Collections.Generic;
public class AVD
{
private Dictionary<string, object> _data = new Dictionary<string, object>();
public object this[string key]
{
get {
if(!_data.ContainsKey(key)){
_data[key] = new AVD();
}
return _data[key];
}
set {
_data[key] = value;
}
}
}
'@
现在我们可以利用 PowerShell 的原生索引访问语法:
PS ~> $autovivifyingHashtable = [AVD]::new()
PS ~> $autovivifyingHashtable['a']['b']['c'] = 123
PS ~> $autovivifyingHashtable['a'] -is [AVD]
True
PS ~> $autovivifyingHashtable['a']['b'] -is [AVD]
True
PS ~> $autovivifyingHashtable['a']['b']['c']
123
您的问题的第一部分已经
这是我在第二部分的尝试——如何分配嵌套哈希表的成员。在创建任何尚未存在的父哈希表时,没有简单的内置语法来设置嵌套值,因此我为此创建了一个可重用函数 Set-TreeValue
。
function Set-TreeValue( $ht, [String] $path, $value ) {
# To detect errors like trying to set child of value-type leafs.
Set-StrictMode -Version 3.0
do {
# Split into root key and path remainder (", 2" -> split into max. 2 parts)
$key, $path = $path -split '\.', 2
if( $path ) {
# We have multiple path components, so we may have to create nested hash table.
if( -not $ht.Contains( $key ) ) {
$ht[ $key ] = [ordered] @{}
}
# Enter sub tree.
$ht = $ht[ $key ]
}
else {
# We have arrived at the leaf -> set its value
$ht[ $key ] = $value
}
}
while( $path )
}
演示:
$ht = [ordered] @{}
Set-TreeValue $ht foo.bar.baz 42 # Create new value and any non-existing parents
Set-TreeValue $ht foo.bar.baz 8 # Update existing value
Set-TreeValue $ht foo.bar.bam 23 # Add another leaf
Set-TreeValue $ht fop 4 # Set a leaf at root level
#Set-TreeValue $ht fop.zop 16 # Outputs an error, because .fop is a leaf
Set-TreeValue $ht 'foo bar' 15 # Use a path that contains spaces
$ht | ConvertTo-Json # Output the content of the hash table
输出:
{
"foo": {
"bar": {
"baz": 8,
"bam": 23
}
},
"fop": 4,
"foo bar": 15
}
注意: 我选择将嵌套哈希表创建为 OrderedDictionary
,因为它们比常规哈希表更有用(例如,确保 [= =33=]输出)。如果您想要无序哈希表(可能具有轻微的性能优势),请删除 [ordered]
。