我对 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)被引用。
第二个组对象实际上在做什么?
它认为“订单”属性是什么?
这是可以理解的混淆,但正如您所说,哈希表键不是对象属性。
对它们一视同仁是很诱人的(有时这很有效),但在这种情况下绝对不会。
部分原因是您使用的是不同的 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 个属性:Name和DateOfBirth。
$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)
| 归档时间: |
|
| 查看次数: |
911 次 |
| 最近记录: |