Powershell数组到哈希表无法使用括号表示法获取键值,但点号可以吗?

问题描述 投票:0回答:2

我正在尝试将数组转变为键值对作为哈希表,此过程似乎创建了一个哈希表列表,但它们的行为几乎按预期进行。我怎样才能按照预期创建一个哈希表?来自Python,我期待类似字典理解的东西..

PS > ('1','2','3') | foreach {@{($_+'A') = $_}}

Name                           Value
----                           -----
1A                             1
2A                             2
3A                             3

PS > (('1','2','3') | foreach {@{($_+'A') = $_}}).'1A'
1
PS > (('1','2','3') | foreach {@{($_+'A') = $_}})['1A']
  • 最后一行空是 $null 而不是 1
arrays powershell dictionary hashtable
2个回答
2
投票
  • 您的管道发出[1]

    [hashtable]
    实例数组,在每次迭代中构造一个哈希表文字

  • 成员访问枚举 - 即能够访问 collection 级别的属性并返回 elements' 属性值 - 仅适用于

    .
    成员访问运算符) 不适用于
    [...]
    索引运算符

    • 请参阅 GitHub 问题 #17514,了解有关这种不对称性的讨论,该不对称性被声明为 设计所致。

    • 换句话说:您的管道相当于以下内容,它在每个哈希表上查找“1A”属性(键),并仅返回具有此类键的属性的值:

      [2]

      # Works, but only due to use of . rather than [] @( @{ '1A' = 1 } @{ '2A' = 2 } @{ '3A' = 3 } ).'1A' # -> 1
      
      

相比之下,如果您的意图是

发出一个单个哈希表实例,请使用以下内容,这样您就可以互换使用点和索引表示法:

( ('1','2','3') | foreach { $ht = @{} } { $ht[$_+'A'] = $_ } { $ht } )['1A'] # -> 1
请注意使用带有 

foreach (

ForEach-Object
) 的
 Three
脚本块,它们分别绑定到
-Begin
-Process
-End
 参数。 
-Begin
 块初始化哈希表,
-Process
 块向其中添加条目,
-End
 块发出现已填充的哈希表。
请注意,
$ht
 变量将保留在调用者的作用域中,这可能是这里所需要的;但是,您可以使用 
{ $ht; Remove-Variable ht }
 作为 
-End
 块来防止这种情况。


[1] 严格来说,它streams多个实例是成功的输出流,当通过(...)

捕获时,它变成一个
[object[]]
数组。

[2] 在成员访问枚举期间,不具有所请求属性的元素将被简单地跳过,但有一个例外 bug[pscustomobject]

实例意外地向输出贡献了
$null
值 - 请参阅
GitHub问题#13752


0
投票
我编写了一个函数,我认为它满足了我对类似 Python 的字典理解的大部分期望。 请参阅

https://gist.github.com/YoraiLevi/292bb8d0e2ce0f87d37e5d5d735fff16

function ConvertTo-Hashtable { <# .LINK https://gist.github.com/YoraiLevi/292bb8d0e2ce0f87d37e5d5d735fff16 .LINK https://stackoverflow.com/questions/77265408/powershell-array-to-hashtable-cannot-get-keys-value-with-bracket-notation-but-d .LINK https://peps.python.org/pep-0274/ .SYNOPSIS Converts input objects into a hashtable. .DESCRIPTION The ConvertTo-Hashtable function converts input objects into a hashtable. It can be used to create a hashtable from an array of values or from pipeline input. The function takes a scriptblock that is used to generate the hashtable values. The scriptblock is executed for each input object and must output a single hashtable. The function can handle duplicate keys in the input objects and provides options for how to handle them. .PARAMETER ScriptBlock The scriptblock that is used to generate the hashtable values. The scriptblock is executed for each input object and must output a single hashtable. .PARAMETER InputArray An array of input objects to convert to a hashtable. .PARAMETER InputObject An input object to convert to a hashtable. .PARAMETER DuplicateKeyAction Specifies how to handle duplicate keys in the input objects. Valid values are 'Stop', 'Ignore', 'Overwrite', 'Append', and 'Prepend'. The default value is 'Stop'. .EXAMPLE ConvertTo-Hashtable 1, 2, 3 Name Value ---- ----- 3 3 2 2 1 1 .EXAMPLE 1, 2, 3 | ConvertTo-Hashtable Name Value ---- ----- 3 3 2 2 1 1 .EXAMPLE 1, 2, 3 | ConvertTo-Hashtable { @{($_ + 1) = $_ } } Name Value ---- ----- 4 3 3 2 2 1 .EXAMPLE 1, 2, 3 | ConvertTo-Hashtable { @{($PSItem + 1) = $PSItem } } Name Value ---- ----- 4 3 3 2 2 1 .EXAMPLE 1, 2, 3 | ConvertTo-Hashtable { @{($args[0] + 1) = $args[0] } } Name Value ---- ----- 4 3 3 2 2 1 .EXAMPLE 1, 2, 3 | ConvertTo-Hashtable { @{($input[0] + 1) = $input[0] } } Name Value ---- ----- 4 3 3 2 2 1 .EXAMPLE 1, '2', 3 | ConvertTo-Hashtable { if ($_ -is [int]) { @{$_ = $_ } } else { [pscustomobject]@{} } } Scriptblock execution resulted in an error/exception: 'Provided scriptblock must only output a single hashtable, but output type was System.Management.Automation.PSCustomObject' .EXAMPLE 1, '2', 3 | ConvertTo-Hashtable { if ($_ -is [int]) { @{$_ = $_ } } else { Write-Error 'Write-Error example' } } Scriptblock execution resulted in an error/exception: 'Write-Error example' .EXAMPLE 1, '2', '3' | ConvertTo-Hashtable { if ($_ -is [int]) { @{$_ = $_ } }else { throw 'throw example' } } Scriptblock execution resulted in an error/exception: 'throw example' .EXAMPLE 1, $null, 3 | ConvertTo-Hashtable { @{$_ = $_ } } Scriptblock execution resulted in an error/exception: 'A null key is not allowed in a hash literal.' .EXAMPLE $i = 0 ConvertTo-Hashtable 1, 1, 1, 2 { @{$_ = ($_ + $i) } ; $i++; } Scriptblock execution resulted in an error/exception: Key '1' was found multiple times .EXAMPLE $i = 0 ConvertTo-Hashtable 1, 1, 1, 2 { @{$_ = $_ + $i } ; $i++; } -DuplicateKeyAction Stop Scriptblock execution resulted in an error/exception: Key '1' was found multiple times .EXAMPLE $i = 0 ConvertTo-Hashtable 1, 1, 1, 2 { @{$_ = $_ + $i } ; $i++; } -DuplicateKeyAction Ignore Name Value ---- ----- 2 5 1 1 .EXAMPLE $i = 0 ConvertTo-Hashtable 1, 1, 1, 2 { @{$_ = $_ + $i } ; $i++; } -DuplicateKeyAction Overwrite Name Value ---- ----- 2 5 1 3 .EXAMPLE $i = 0 ConvertTo-Hashtable 1, 1, 1, 2 { @{$_ = $_ + $i } ; $i++; } -DuplicateKeyAction Append Name Value ---- ----- 2 {5} 1 {1, 2, 3} .EXAMPLE $i = 0 ConvertTo-Hashtable 1, 1, 1, 2 { @{$_ = $_ + $i } ; $i++; } -DuplicateKeyAction Prepend Name Value ---- ----- 2 {5} 1 {3, 2, 1} #> [CmdletBinding( DefaultParameterSetName = 'Pipeline')] param( [ValidateNotNull()] [Parameter(ParameterSetName = 'Call', Position = 1)] [Parameter(ParameterSetName = 'Pipeline', Position = 0)] [scriptblock]$ScriptBlock = { @{$_ = $_ } }, [Parameter(Mandatory = $true, Position = 0, ParameterSetName = 'Call')] [array]$InputArray, [Parameter(ValueFromPipeline = $true, ParameterSetName = 'Pipeline')] [psobject]$InputObject, [ValidateSet('Stop', 'Ignore', 'Overwrite', 'Append', 'Prepend')] [string]$DuplicateKeyAction = 'Stop' ) begin { $ht = @{} } process { try { $InputObject = switch ($PsCmdlet.ParameterSetName) { 'Call' { $InputArray } 'Pipeline' { @($InputObject) } } $tables = $InputObject | ForEach-Object { try { $PreviousErrorActionPreference = $ErrorActionPreference $ErrorActionPreference = 'Stop' $table = Invoke-Command -NoNewScope -ScriptBlock $ScriptBlock -InputObject $_ -ArgumentList $_ $ErrorActionPreference = $PreviousErrorActionPreference } catch { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( ([System.ArgumentException]::New("Scriptblock execution resulted in an error/exception: '$($PSItem.Exception.Message)'", $_)), 'ScriptBlockException', [System.Management.Automation.ErrorCategory]::InvalidResult, $ScriptBlock ) ) } if ($table -isnot [hashtable]) { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( ([System.ArgumentException]::New("Provided scriptblock must only output a single hashtable, but output type was $($table.GetType())")), 'ArgumentException', [System.Management.Automation.ErrorCategory]::InvalidResult, $ScriptBlock ) ) } $table } $tables | ForEach-Object { $table = $_ $table.GetEnumerator() | ForEach-Object { $Key = $_.Key $Value = switch ($DuplicateKeyAction) { 'Stop' { if (!$ht.ContainsKey($Key)) { Write-Output -NoEnumerate $table.$key } else { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( ([System.ArgumentException]::New("Key '$($Key)' was found multiple times")), 'ArgumentException', [System.Management.Automation.ErrorCategory]::InvalidData, $Key ) ) } } 'Ignore' { if (!$ht.ContainsKey($Key)) { Write-Output -NoEnumerate $table.$key } else { Write-Output -NoEnumerate $ht.$key } } 'Overwrite' { Write-Output -NoEnumerate $table.$key } 'Append' { if (!$ht.ContainsKey($Key)) { Write-Output -NoEnumerate @($table.$key) } else { Write-Output -NoEnumerate ($ht.$key + @($table.$key)) } } 'Prepend' { if (!$ht.ContainsKey($Key)) { Write-Output -NoEnumerate @(, $table.$key) } else { Write-Output -NoEnumerate (@(, $table.$key) + $ht.$key) } } } $ht.$Key = $Value } } } catch { $PSCmdlet.ThrowTerminatingError($PSItem) } } end { return $ht } }
    
© www.soinside.com 2019 - 2024. All rights reserved.