使用数组将Hashtable排序为值

时间:2017-07-09 20:43:49

标签: arrays powershell sorting hashtable powershell-v5.0

说明:我正在构建一个搜索文件的PowerShell脚本,然后为它们提供唯一的名称,复制它们然后通过哈希计算验证它们 - 我选择将脚本拆分为函数对于每一步,所以更容易维护整个事情。 要将所有值从一个函数传递到另一个函数,我选择使用[hashtable]$FooBar - 在$FooBar内,有多个数组,例如FullNameOutputPath(可能会更改)每个文件,因为它们将被复制到名为yyyy-mm-dd的子文件夹中。所有数组都相互关联(意味着索引1包含第一个文件的所有值,索引2包含第二个文件的值,...),这在目前的情况下工作正常。

简短的简化可视化:

$FooBar = @{}
$FooBar.FullName = @()
$FooBar.Size = @()
$FooBar.Ext = @()
Get-ChildItem | ForEach-Object {
    $FooBar.FullName += $_.FullName
    $FooBar.Size += $_.Length
    $FooBar.Ext += $_.Extension
}

但是,我现在需要通过其中一个数组的一个值集对它们进行排序,例如:尺寸。或者,再次可视化:

# From:
$FooBar
Name                           Value
----                           -----
fullname                       {D:\AAA.XYZ, D:\BBB.ZYX, D:\CCC.YZX}
size                           {222, 111, 555}
extension                      {.XYZ, .ZYX, .YZX}

# To:
$FooBar = $FooBar | Sort-Object -Property Size -Descending
$FooBar
Name                           Value
----                           -----
fullname                       {D:\CCC.YZX, D:\AAA.XYZ, D:\BBB.ZYX}
size                           {555, 222, 111}
extension                      {.YZX, .XYZ, .ZYX}

我尝试了$FooBar.GetEnumerator() | Sort-Object -Property Size,但这并没有改变任何事情。谷歌提出了如何对一组哈希表进行排序的建议,但在我的情况下,反之亦然,我无法理解这一点,因为我甚至不明白为什么这是一个问题第一个地方

所以我的问题是:有没有办法通过其中一个数组的值集对哈希表中的所有数组进行排序?我无法理解这一点。

免责声明:我是一个PowerShell-autodidact,在脚本/编程方面没有合理的背景,所以很可能我的“将所有内容都包含在一个哈希表中”-solution不会起作用完全或可能效率极低 - 如果是这样,请告诉我。

3 个答案:

答案 0 :(得分:5)

我认为你想要做的最简单的方法是Select-Object

$fooBar = Get-ChildItem | Select-Object FullName, Size, Extension

这将创建一个只有所需属性的新对象数组。这种方法起作用的原因并不是因为Sort-Object适用于属性,而您指定的属性位于几层之后。

如果您需要比完全属性更多的灵活性,您可以像这样创建自己的

$fooBar = Get-ChildItem | Select-Object @{Name = 'SizeMB'; Expression = {$_.Size / 1MB}}

或使用[PSCustomObject]类型加速器手动创建新属性:

$fooBar = Get-ChildItem | ForEach-Object {
    [PSCustomObject]@{
        FullName = $_.FullName
        Extension = $_.Extension
        Size = $_.Size
    }
}

<强>更新

如果您需要在对象最初创建后添加其他属性,则可以选择几个。

添加会员

到目前为止,最常用的方法是使用Add-Member cmdlet。

$object | Add-Member -MemberType NoteProperty -Name NewProperty -Value 'MyValue'

$object

要记住的重要一点是,默认情况下,此cmdlet不会返回任何内容。因此,如果将上述语句放在函数的末尾并且不单独返回对象,则函数不会返回任何内容。确保使用-PassThru参数(这对于链接Add-Member命令也很有用)或之后调用变量(如上例所示)

选择-对象

使用计算属性添加成员时,可以选择所有以前的属性。请记住,由于Select-Object的工作原理,源对象中的所有方法都不会被转移。

$fooBar | Select-Object *, @{Name = 'NewProperty'; Expression = {'MyValue'}}

psobject.Properties

这个是我个人最喜欢的,但它仅限于PowerShell的更高版本,我还没有真正看到它被其他人使用过。

$fooBar.psobject.Properties.Add([psnoteproperty]::new('NewProperty', 'MyValue'))
$fooBar

每个成员类型都有自己的构造函数。您还可以将方法添加到$fooBar.psobject.Methods或输入$fooBar.psobject.Members。我喜欢这种方法,因为它感觉更明确,而且添加成员与成员的感觉是正确的。

摘要

您选择的方法主要是偏好。我会建议Add-Member,如果可能的话,因为它是最常用的,因此具有更好的可读性,更多的人可以回答有关它的问题。

我还想提一下,如果可能的话,通常最好避免添加其他成员。理想情况下,函数的返回值应具有可靠的形式。如果某人正在使用您的函数并且他们必须猜测对象上何时存在属性或方法,则调试变得非常困难。显然这不是一个硬性规则,但如果你需要添加一个成员,你至少应该考虑改造是否更好。

答案 1 :(得分:4)

出于所有实际目的,我强烈建议您只需将所需对象存储在一个数组中,对其进行排序一次,然后在需要时引用每个对象的各个属性:

$FooBar = Get-ChildItem |Sort-Object -Property Length

# Need the Extension property of the object at index 4?
$FooBar[4].Extension

回答你的实际问题:

Array.Sort() has an overload分别获取键和值数组。您可以为要排序的每个其他属性制作要排序的数组的副本:

# Create hashtable of correlated arrays 
$FooBar = @{}
$FooBar.FullName = @()
$FooBar.Size = @()
$FooBar.Ext = @()
# Types cast explicitly to avoid Array.Sort() calling .CompareTo() on the boxing object
Get-ChildItem | ForEach-Object {
    $FooBar.FullName += [string]$_.FullName
    $FooBar.Size     += [int]$_.Length
    $FooBar.Ext      += [string]$_.Extension
}

# Define name of reference array property
$SortKey = 'Size'

# Sort all arrays except for the reference array
$FooBar.Keys |Where-Object {$_ -ne $SortKey} |ForEach-Object {
    # Copy reference values to new array
    $Keys = $FooBar[$SortKey].Clone()

    # Sort values in target array based on reference values
    [array]::Sort($Keys,$FooBar[$_])
}

# Finally sort the reference array
[array]::Sort($FooBar[$SortOn])

以上只有参考数组由值类型组成才有效

答案 2 :(得分:2)

PowerShell使对象变得非常简单。

尝试:

$FooBar = Get-Childitem
$FooBar | Get-Member

这会告诉您$Foobar实际上包含FileInfoDirectoryInfo类型的对象,并显示Properties可用的对象。

$FooBarSortedBySizeDesc  = $FooBar | Sort-Object Length -Descending
$FooBarFullNamesOnly = $FooBar.FullName