PowerShell排序哈希表。

5

我发现对于我正在排序并尝试查看结果的哈希表,出现了一些看起来非常奇怪的行为。我构建了哈希表,然后需要根据值对该表进行排序,但是我发现了两个奇怪的问题。

在类外部运行正常。

$hash = [hashtable]::New()
$type = 'conformset'
$hash.Add($type, 1)
$type = 'applyset'
$hash.Add($type , 1)
$type = 'conformset'
$hash.$type ++
$hash.$type ++
$hash
Write-Host
$hash = $hash.GetEnumerator() | Sort-Object -property:Value
$hash

我看到哈希表的内容两次,一次是未排序的,另一次是排序后的。然而,当使用类时,它什么也不做。

class Test {
    # Constructor (abstract class)
    Test () {
        $hash = [hashtable]::New()
        $type = 'conformset'
        $hash.Add($type, 1)
        $type = 'applyset'
        $hash.Add($type , 1)
        $type = 'conformset'
        $hash.$type ++
        $hash.$type ++
        $hash
        Write-Host
        $hash = $hash.GetEnumerator() | Sort-Object -property:Value
        $hash
    }
}

[Test]::New()

这只是将“Test”输出到控制台,与哈希表无关。我在这里的假设是它与管道如何中断有关,说实话,这是迁移到类的一个很好的理由,考虑到污染的管道错误是多么常见。因此,采用基于循环的方法,在类中或不在类中都无法显示第二个已排序的哈希表。

$hash = [hashtable]::New()
$type = 'conformset'
$hash.Add($type, 1)
$type = 'applyset'
$hash.Add($type , 1)
$type = 'conformset'
$hash.$type ++
$hash.$type ++

foreach ($key in $hash.Keys) {
    Write-Host "$key $($hash.$key)!"
}
Write-Host
$hash = ($hash.GetEnumerator() | Sort-Object -property:Value)
foreach ($key in $hash.Keys) {
    Write-Host "$key $($hash.$key)!!"
}

但非常奇怪的是,这只显示了基于循环的输出的第一个,而两个直接转储都显示出来。

$hash = [hashtable]::New()
$type = 'conformset'
$hash.Add($type, 1)
$type = 'applyset'
$hash.Add($type , 1)
$type = 'conformset'
$hash.$type ++
$hash.$type ++

foreach ($key in $hash.Keys) {
    Write-Host "$key $($hash.$key)!"
}
$hash
Write-Host
$hash = ($hash.GetEnumerator() | Sort-Object -property:Value)
foreach ($key in $hash.Keys) {
    Write-Host "$key $($hash.$key)!!"
}
$hash

现在的输出结果为:
conformset 3!
applyset 1!

Name                           Value                                                                                                                                                                          
----                           -----                                                                                                                                                                          
conformset                     3                                                                                                                                                                              
applyset                       1                                                                                                                                                                              

applyset                       1                                                                                                                                                                              
conformset                     3  

很明显,$hash被排序了。但是循环不会显示它?这是有缺陷的行为,还是我没有理解这种行为的原因,因此没有解决方案?


1
哈希表类型是不可排序的。[] 如果你想要一个有序的字典,请使用 >>> SortedList<TKey,TValue> Class (System.Collections.Generic) | Microsoft Docs — https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.sortedlist-2?view=netframework-4.8 <<< - Lee_Dailey
2个回答

11
  • Vasil Svilenov Nikolov的有用答案解释了你的方法存在的根本问题:

    • 你无法对哈希表([hashtable]实例)进行排序:哈希表中键的顺序不能保证且无法更改。

    • $hash = $hash.GetEnumerator() | Sort-Object -property:Value所做的是创建一个[System.Collections.DictionaryEntry]实例数组;结果数组没有.Keys属性,因此你的第二个foreach ($key in $hash.Keys)循环永远不会被执行。

  • 另一个问题是,你通常不能从PowerShell 隐式写入输出流:

    • 从类方法向输出流写入内容需要显式使用return;同样,必须通过Throw语句报告错误。
    • 在你的情况下,代码位于Test类的构造函数中,构造函数隐式返回新构造的实例——你不能从它们中return任何东西。
为了解决您的问题,您需要一种专门的数据类型,它将哈希表的特性与保持条目键的排序顺序相结合[1]
.NET类型System.Collections.SortedList提供了这个功能(还有一个泛型版本,正如Lee Dailey所指出的):
您可以首先使用该类型:
# Create a SortedList instance, which will maintain
# the keys in sorted order, as entries are being added.
$sortedHash = [System.Collections.SortedList]::new()

$type = 'conformset'
$sortedHash.Add($type, 1) # Or: $sortedHash[$type] = 1 or: $sortedHash.$type = 1
$type = 'applyset'
$sortedHash.Add($type , 1)
$type = 'conformset'
$sortedHash.$type++
$sortedHash.$type++

甚至可以从(和到)现有的哈希表进行转换:

# Construct the hash table as before...
$hash = [hashtable]::new() # Or: $hash = @{}, for a case-INsensitive hashtable
$type = 'conformset'
$hash.Add($type, 1)
$type = 'applyset'
$hash.Add($type , 1)
$type = 'conformset'
$hash.$type++
$hash.$type++

# ... and then convert it to a SortedList instance with sorted keys.
$hash = [System.Collections.SortedList] $hash

[1]请注意,这与PowerShell提供的有序字典不同,有序字典使用文字语法[ordered] @{ ... }:一个有序字典按照插入的顺序维护键,而不是基于排序。有序字典的类型为System.Collections.Specialized.OrderedDictionary


3
当您执行$hash = $hash.GetEnumerator() | Sort-Object -property:Value时,您正在将哈希表重新分配为数组,请尝试$hash.GetType(),这当然会与哈希表不同,您可以查看方法等Get-Member -InputObject $hash 我认为您无法对哈希表进行排序,也不需要这样做。您可以尝试有序字典$hash = [Ordered]@{}

有序字典与哈希表的区别在于键始终以您列出它们的顺序出现。哈希表中键的顺序未确定。

我最喜欢使用哈希表的最佳用途之一是搜索速度。 例如,您可以通过以下方式立即获取哈希表中名称的值$hash['applyset'] 如果您想了解更多关于哈希表如何工作以及何时使用它的信息,我认为这篇文章是一个很好的起点: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_hash_tables?view=powershell-6

2
好的诊断,但请注意,一个“有序”的哈希表 - 其中保持“插入顺序” - 不同于一个其“键已排序”的哈希表。楼主想要后者。 - mklement0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接