【问题标题】:Converting hashtable to array of strings将哈希表转换为字符串数组
【发布时间】:2014-01-28 18:11:37
【问题描述】:

如何将哈希表转换为字符串数组? 假设 $l_table 是一个哈希表。如果我尝试

$l_array = $l_table | format-table

那么 $l_array 是一个数组,而是一个“FormatEntryData”对象的数组。 如果我这样做了

[string[]]$l_array = $l_table | format-table

那么 $l_array 是一个字符串数组,但是字符串都是“Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData”。如果我尝试

$l_array = $l_table | out-string

那么 $l_array 是一个字符串。我已经尝试了很多其他的东西,但没有任何效果,除了手动循环,我真的不想这样做。

【问题讨论】:

    标签: powershell


    【解决方案1】:

    虽然原始发帖人可能想要一个与 Format-Table 的输出相对应的字符串数组,但正如所提供的尝试和发帖人自己的答案/cmets 所暗示的那样,问题标题的措辞是更典型的是希望将哈希表本身转换为每个项目值的数组(如果还不是String,则转换为String)。对于那些寻求解决这个问题的人,我提出以下内容(因为似乎还没有类似的问题):-

    简单的答案是使用

    [string[]]$l_table.values
    

    这确实会根据需要提供一个字符串数组。

    注意:$l_table.values[System.Collections.Hashtable+ValueCollection],这就是为什么即使值已经是字符串也需要转换为 [string[]] 的原因。例如,

    'Found {0} with submatch {1}' -f [string[]]$matches.values
    

    没有它就行不通。 (接下来会提到另一个可能的问题。)

    $l_table.values 的主要缺陷是,除非哈希表被定义为 [ordered] (PSv3+),否则项目的顺序是未定义的(并且可以随着哈希表的修改而改变)。通常,在将哈希表转换为数组时,需要对该数组中的元素进行某种排序。实际上,有时键是(正)整数,其目的是生成的数组使用与数组索引相同的值(参见上面的$matches 示例)。要创建这种类型的数组,请使用

    [string[]]$l_table[($l_table.keys | sort)]
    

    或者,如果值已经是字符串,则只是

    $l_table[($l_table.keys | sort)]
    

    调用 Powershell 的集合切片功能(通过使用数组表达式作为索引,单个表达式可以生成一组独立选择的集合项。例如,$array[1,3,5,9]$array[1,2,3,4,5]$array[1..5])。请注意,如果键形成从 0 开始的连续范围,这只会生成一个以键作为索引的数组。但是,由于索引表达式是一个管道,因此可以获得几乎任何东西作为所需的键数组。要从“稀疏”哈希表(非字符串值)中获取结果数组,请使用

    [string[]]$l_table[0..($l_table.keys | sort -descending)[0]]
    #
    # ($l_table.keys | sort -descending) produces a sorted array of key values
    # with the largest value in element 0
    #
    

    现在,通过将任何中间(未使用)数组项设置为“”(即[string]$null),结果数组将具有正确对应于适当索引值的整数键。如果这是一个问题,那么可以使用两步过程来留下“缺失”的条目 作为$null。首先,通过一次一个字典对构建一个新的哈希表(使用GetEnumerator()),将非字符串哈希表转换为字符串而不添加条目。其次,在将(现在的字符串)哈希表转换为数组时,不要使用[string[]],以便将$null留在“未使用”项中,如下所示

    ($l_table.getenumerator() | foreach -begin {$str_table = @{}} -process {$str_table.add($_.key,[string]$_.value)} -end {$str_table[0..($str_table.keys | sort -descending)[0]]})
    

    或者可能更有效(通过删除可能成本高昂的排序)

    ($l_table.getenumerator() | foreach -begin {$maxkey = 0; $str_table = @{}} -process {$str_table.add($_.key,[string]$_.value); if ($maxkey -lt $_.key){$maxkey = $_.key}} -end {$str_table[0..$maxkey]})
    

    注意:对于字符串值的稀疏散列表,初始形式无需转换

    $l_table[0..($l_table.keys | sort -descending)[0]]
    

    可以,但如果需要,使用 [string[]] 会将任何缺失的条目从 $null 更改为 ""

    【讨论】:

    • 所有好的解决方案,尽管与我最初尝试解决的问题略有不同。
    • [string[]]$hash.values 是最简单优雅的解决方案。
    【解决方案2】:

    David I. McIntosh's own answer 效果很好,但需要注意的是,结果数组的元素对应于默认输出的所有行,其中包括:

    • 空的前导行和尾随行
    • 两个标题行(带有列名的行和分隔线)

    Out-String 只是将您通常在控制台(终端)中看到的内容发送到一个字符串,默认为 single 字符串,-Stream 作为一个 array 个字符串。

    这是 David 命令的一个变体,它同时删除了标题和空行:

    [string[]] $l_array = ($l_table | Out-String -Stream) -ne '' | select -Skip 2
    

    这个答案的其余部分展示了如何控制获得的字符串表示的细节;为简洁起见,它使用 PS v3+ 语法和内置别名 %ForEach-Object

    注意:为清楚起见,示例输入哈希表在以下示例中称为$ht(而不是$l_table); PSv4+ .ForEach() 变种表现更好。

    • 获取所有作为字符串数组:

        $ht.Keys | % ToString # or (PSv4+): $ht.Keys.ForEach('ToString')
      
    • 获取所有作为字符串数组:

        $ht.Values | % ToString # or (PSv4+): $ht.Values.ForEach('ToString')
      
    • 获取键值对的自定义表示,格式为<key>=<value>;请注意,通过管道单独发送键值对需要.GetEnumerator();默认情况下,PowerShell 传递一个散列表作为一个整体

        $ht.GetEnumerator() | % { "$($_.Name)=$($_.Value)" }
      
        # or (PSv4+):
        $ht.GetEnumerator().ForEach({ "$($_.Name)=$($_.Value)" })
      

    请注意,虽然.ToString() 在字符串插值期间也隐式应用(在"..." 内,一个可扩展的字符串),但与primitive .NET 类型(以及其他数字类型,例如 [decimal][bigint]);通常,类型只会打印它们的完整类型名称,除非它们的.ToString() 方法被显式覆盖以返回更有意义的自定义表示(这是原始类型所做的,只有 一些 PowerShell cmdlet 返回的非原始类型)。
    另请注意,在可扩展字符串中使用数组(类似数据结构)会扩展为其元素与 $OFS 首选项变量的值的(字符串化)元素连接,默认为空格字符。 (例如,$a='one', 'two'; "$a" 扩展为 'one two') - 有关 PowerShell 中可扩展字符串(字符串插值)的更多信息,请参阅 this answer

    选择一个值的属性在字符串中表示它的简单示例:

    # Sample hashtable containing a value of a non-built-in type,
    # [System.Diagnostics.Process]    
    $ht = @{ one = 1; two = Get-Process -ID $PID }
    
    # Use the `.Path` property to represent the value.
    $ht.GetEnumerator()  | % { "$($_.Name)=$($_.Value.Path)" }
    

    【讨论】:

    • 实际上,我的问题是将类型分为 primitive (又名 built-in)而不是 primitive似乎将某些特权状态归因于 primitive 类型(相对于ToString())。我的观点是 all 类型必须覆盖 ToString() 才能返回,而不是打印(稍后会出现注释),而不是类型名称的字符串(继承自 Object.ToString() )。尝试[int32].getmethods() | ft name,isstatic,declaringtype 看看即使是 very primitive 类型也为自己定义了 4 个 ToString() 重载(并且不允许任何继承的重载)。
    • @UberKluger 原始类型的“特权”状态是它们已知可以提供有意义的表示,但是,是的,它们也从显式覆盖的@987654350 中获得了这种行为@ 方法。这只是一种务实的说法:您可以依靠这些众所周知的类型(加上更多)来获得有意义的字符串表示 - 不能对任何其他类型做出一般性的陈述。跨度>
    • 最后评论:查询-他们如何知道提供有意义的表示?我只能想到两种方法,阅读文档(这包括在类型定义上使用反射)或直接测试(又名吸它看看)。但是,这两种方法同样适用于 所有 类型。我们知道intbool“看起来像”的事实只是因为我们已经“看到”了它(可能在另一个环境中,例如MS BASIC 或ECMA-262)。最终,知道转换为 String 的类型“看起来像”的唯一方法是“调用其 ToString() 方法(并查看)”。
    【解决方案3】:

    哈希只是一个哈希表,所以它有一个键和一个值属性。

    $hash = @{}
    
    $hash["foo"] = "bob"
    
    $hash.Values
    
    $hash.Values | Out-string
    

    如果你想获取枚举器,它将返回键值对

    $hash.GetEnumerator() |%{$_ | out-string}
    

    【讨论】:

    • 这只获取值。
    • 我更新了答案,你能多说一些细节吗?我不确定你要找的结果是什么。
    【解决方案4】:
    [string[]]$l_array = $l_table | out-string -stream
    

    【讨论】:

      【解决方案5】:

      这是一个常见的问题;哈希表必须通过键或值来检索,但您无法获得对的集合——毕竟,这就是原始哈希表。在我的示例中,哈希表由“字典”表示(您会从 VBS 中记住 :-)。但是,您需要确定一个分隔符来分隔键和值,在本例中为“KeyValue”

      $ARRAY = ( $DICT.Keys | foreach-object { "$_<->$($DICT[$_])"})
      

      如果需要,您甚至可以先对键进行排序。在这种情况下,我反转为 ValueKey 并按值排序。我还稍微扩展了字符串以使其更详细但更清晰:

      $ARRAY = ( $DICT.Keys | foreach-object { $DICT[$_] + "<--->" + $_} | sort-object)
      

      【讨论】:

      • Re "Hashtable 必须通过键或值来检索,但你不能得到对的集合":是的,你可以:$DICT.GetEnumerator()
      猜你喜欢
      • 1970-01-01
      • 2014-08-22
      • 1970-01-01
      • 2011-01-27
      • 2012-07-05
      • 2013-11-20
      • 2014-07-01
      • 2016-10-21
      • 1970-01-01
      相关资源
      最近更新 更多