powershell 哈希表键与对象属性

Dwe*_*rly 2 powershell

我对 PowerShell 如何处理 Hashtable 键与对象属性感到困惑。

考虑:

$list = @{
    'one' = @{
        name  = 'one'
        order = 80
        };

    'two' = @{
        name  = 'two'
        order = 40
        };

    'twotwo' = @{
        name  = 'twotwo'
        order = 40
        };

    'three' = @{
        name  = 'three'
        order = 20
        }
    }

$list.Values|group-object { $_.order }
$list.Values|group-object -property order
Run Code Online (Sandbox Code Playgroud)

第一个组对象给了我我期望的(三个组),第二个没有(一个大组)。显然 Hashtable 键不是对象属性,但在语法上它们以相同的方式(var.name)被引用。

第二个组对象实际上在做什么?
它认为“订单”属性是什么?

bri*_*ist 5

这是可以理解的混淆,但正如您所说,哈希表键不是对象属性。

对它们一视同仁是很诱人的(有时这很有效),但在这种情况下绝对不会。

部分原因是您使用的是不同的 cmdlet,而不是直接的语言语义。

哈希表可以对它们的键使用点表示法,但键不是属性,当您使用 的-Property参数时Group-Object,您正在寻找专门的属性,而不仅仅是“您可以用点访问的任何东西”。

正如您所见,采用脚本块的该参数的另一种形式是将要执行的代码,因此块返回的任何值都将被分组。

要更直接地回答您的问题:

第二个组对象实际上在做什么?

它在当前对象(它是一个哈希表)上寻找一个属性(特别是)。

如果您想查看其中一个对象的属性,请尝试以下操作:

$list.Values[0].PSObject.Properties | ft
Run Code Online (Sandbox Code Playgroud)

它认为“订单”属性是什么?

它不认为这是什么;它查找具有该名称的属性,如果找到,则使用该值;否则它使用$null.

你会得到相同的结果:

$list.Values | group -Property FakeProp
Run Code Online (Sandbox Code Playgroud)

或者

$list.Values | group -Property { $null }
Run Code Online (Sandbox Code Playgroud)

在评论中解决您的问题:

有什么方法可以知道“什么时候应该”和“什么时候不应该”,我是否应该推迟使用脚本块,何时使用 -Property 优于 { ... }?

-Property每当您要分组的值可用作被检查对象的直接属性时,(没有脚本块)是首选。如果它是某个属性的一个属性,或者某个计算值,或者一个哈希表键/值,或者其他任何东西,请使用脚本块。我将这些称为“复杂值”。

如果复数值有用,你可能想把它添加为对象本身的实际属性来封装它;那么你可以直接引用它。这个例子并不适合你的哈希表情况,但考虑代表人的对象。它们有 2 个属性:NameDateOfBirth

$people 是这些对象的数组,您想按年龄对人进行分组(请忽略我不准确的年龄确定代码)。

$people | Group-Object -Property { ([DateTime]::Now - $_.DateOfBirth).Days / 365 -as [int] }
Run Code Online (Sandbox Code Playgroud)

如果您再也不需要知道年龄,那也没关系;当然这不太可能,而且这看起来有点乱。应该更清楚地表明您想要“按年龄分组”。

相反,您可以使用以下命令将您自己的(计算的)Age属性添加到现有对象中Add-Member

$people |
    Add-Member -MemberType ScriptProperty -Name Age -Value {
        ([DateTime]::Now - $this.DateOfBirth).Days / 365 -as [int]
    } -Force
Run Code Online (Sandbox Code Playgroud)

从现在开始,$people数组中的每个对象都有一个Age基于DateOfBirth属性值计算的属性。

现在你可以让你的代码更清晰:

$people | Group-Object -Property Age
Run Code Online (Sandbox Code Playgroud)

同样,这并没有真正解决您的哈希表问题;事实是,它们不适用于分组。如果您要对它们进行大量分组,并且您真的不需要哈希表,请将它们变成对象:

$objs = $list.Values |
    ForEach-Object -Process {
        New-Object -TypeName PSObject -Property $_ # takes a hashtable
    }
Run Code Online (Sandbox Code Playgroud)

或者

$objs = $list.Values |
    ForEach-Object -Process {
        [PSCustomObject]$_ # converts a hashtable to PSObject
    }
Run Code Online (Sandbox Code Playgroud)

然后:

$objs | Group-Object -Property Order
Run Code Online (Sandbox Code Playgroud)