简体   繁体   English

Powershell 排序哈希表

[英]Powershell sorting hash table

I am seeing some seemingly very weird behavior with a hash table I am sorting and then trying to review the results.我在排序的哈希表中看到一些看似非常奇怪的行为,然后尝试查看结果。 I build the hash table, then I need to sort that table based on values, and I see two bits of weirdness.我构建了哈希表,然后我需要根据值对该表进行排序,我看到了两个奇怪的地方。

This works fine outside of a class这在课堂外工作正常

$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

I see the contents of the hash twice, unsorted and then sorted.我看到散列的内容两次,未排序然后排序。 However, when using a class it does nothing.然而,当使用一个类时它什么也不做。

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()

This just echos Test to the console, with nothing related to the hash table.这只是将 Test 回显到控制台,与哈希表无关。 My assumption here is that it relates somehow to how the pipeline is interrupted, which lets be honest, is a great reason to move to classes, given how common polluted pipeline errors are.我的假设是,它与管道如何中断有关,老实说,考虑到管道错误的常见污染程度,这是转移到类的一个很好的理由。 So, moving to a loop based approach, this fails to show the second, sorted, sorted hash table in a class or not.因此,转向基于循环的方法,这无法显示类中的第二个已排序的已排序哈希表。

$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)!!"
}

But, very weirdly, this shows only the first loop based output, but BOTH of the direct dumps.但是,非常奇怪的是,这仅显示了第一个基于循环的输出,但同时显示了直接转储。

$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

The output now is现在的输出是

conformset 3!
applyset 1!

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

applyset                       1                                                                                                                                                                              
conformset                     3  

So obviously $hash is being sorted.所以显然 $hash 正在被排序。 But the loop won't show it?但是循环不会显示它? Huh?嗯? Is this buggy behavior, or intended behavior I just don't understand the reason for, and therefor the way around?这是错误的行为,还是我不明白其原因的预期行为,以及解决方法?

  • Vasil Svilenov Nikolov's helpful answer explains the fundamental problem with your approach: Vasil Svilenov Nikolov 的有用回答解释了您的方法的基本问题:

    • You fundamentally cannot sort a hash table ( [hashtable] instance) by its keys: the ordering of keys in a hash table is not guaranteed and cannot be changed.从根本上讲,您无法按键对哈希表[hashtable]实例)进行排序:哈希表中键的顺序无法保证且无法更改。

    • What $hash = $hash.GetEnumerator() | Sort-Object -property:Value什么$hash = $hash.GetEnumerator() | Sort-Object -property:Value $hash = $hash.GetEnumerator() | Sort-Object -property:Value does is to instead create an array of [System.Collections.DictionaryEntry] instances ; $hash = $hash.GetEnumerator() | Sort-Object -property:Value作用是创建一个[System.Collections.DictionaryEntry]实例数组 the resulting array has no .Keys property, so your second foreach ($key in $hash.Keys) loop is never entered.结果数组没有.Keys属性,因此永远不会输入您的第二个foreach ($key in $hash.Keys)循环。

  • An unrelated problem is that you generally cannot implicitly write to the output stream from PowerShell classes :一个不相关的问题是您通常无法从 PowerShell隐式写入输出流:

    • Writing to the output stream from a class method requires explicit use of return ;从类方法写入输出流需要显式使用return similarly, errors must be reported via Throw statements.同样,错误必须通过Throw语句报告。
    • In your case, the code is in a constructor for class Test , and constructors implicitly return the newly constructed instance - you're not allowed to return anything from them.在您的情况下,代码位于类Test构造函数中,并且构造函数隐式返回新构造的实例 - 您不允许从中return任何内容。

To solve your problem, you need a specialized data type that combines the features of a hash table with maintaining the entry keys in sort order .为了解决您的问题,您需要一种专门的数据类型,它将哈希表的功能与按排序顺序维护条目键相结合 [1] [1]

.NET type System.Collections.SortedList provides this functionality (there's also a generic version , as Lee Dailey notes): .NET 类型System.Collections.SortedList提供了此功能(还有一个通用版本,正如Lee Dailey指出的那样):

You can use that type to begin with:您可以使用该类型开始:

# 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++

Or even convert from (and to) an existing hash table:或者甚至从(和到)现有的哈希表转换:

# Construct the hash table as before...
$hash = [hashtable]::new() # Or: $hash = @{}
$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] Note that this is different from an ordered dictionary, which PowerShell offers with literal syntax [ordered] @{ ... } : an ordered dictionary maintains the keys in the order in which they are inserted , not based on sorting . [1]请注意,这是从有序字典,它提供的PowerShell用文字语法不同[ordered] @{ ... }一个有序字典保持在它们所插入的顺序按键,不是基于排序 Ordered dictionaries are of type System.Collections.Specialized.OrderedDictionary有序字典的类型为System.Collections.Specialized.OrderedDictionary

When you do $hash = $hash.GetEnumerator() | Sort-Object -property:Value当你做$hash = $hash.GetEnumerator() | Sort-Object -property:Value $hash = $hash.GetEnumerator() | Sort-Object -property:Value , you are re-assigning the hashtable in to an array, try $hash.GetType() , and that will of course behave differently than a hashtable, you can check out the methods etc. Get-Member -InputObject $hash $hash = $hash.GetEnumerator() | Sort-Object -property:Value ,您正在将哈希表重新分配到数组中,尝试$hash.GetType() ,这当然与哈希表的行为不同,您可以查看方法等。 Get-Member -InputObject $hash

I dont think you can sort a hashtable, and you do not need to.我不认为您可以对哈希表进行排序,并且您不需要这样做。 You might instead try Ordered Dictionary $hash = [Ordered]@{}您可以改为尝试有序字典$hash = [Ordered]@{}

Ordered dictionaries differ from hash tables in that the keys always appear in the order in which you list them.有序字典与哈希表的不同之处在于,键总是按照您列出它们的顺序出现。 The order of keys in a hash table is not determined.哈希表中键的顺序是不确定的。

One of the best use of a hashtable that I like is the speed of search.我喜欢的哈希表的最佳用途之一是搜索速度。 For example, you can instantly get the value of a name in the hashtable the following way $hash['applyset']例如,您可以通过以下方式立即获取哈希表中名称的值$hash['applyset']

If you want to know more about how hashtable work, and how/when to use it, I think this article is a good start : https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_hash_tables?view=powershell-6如果您想了解有关哈希表如何工作以及如何/何时使用它的更多信息,我认为这篇文章是一个好的开始: https : //docs.microsoft.com/en-us/powershell/module/microsoft.powershell。 core/about/about_hash_tables?view=powershell-6

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM