Reputation: 584
I'm creating an array of PSObjects with calculated properties. I need one property that is calculated based on another property of the same object. How do I do that?
Example - let's say I have array of strings like "a_1", "b_2", "c_3" etc. and I have a lookup function that returns something based on the first part of those strings, i.e. someLookUpFunction('a')
would return "AA" with input of "a".
Now I need a property in my object that has this calculated 'AA' based on the my 'name' property
$stringArray = @('a_1', 'b_2', 'c_3')
$objectArray = $stringArray | ForEach-Object{
New-Object PSObject -Property @{
'name' = ($_ -split "_")[0]
'extendedName' = {$name = ($_ -split "_")[0]; someLookUpFunction($name) }
}
}
The code above doesn't work in part that the output for 'extendedName' property is just this script block. How do I make it to take the value?
Upvotes: 1
Views: 2222
Reputation: 25001
If you need to capture the output of an expression within an expression, you can use the sub-expression operator $()
.
$stringArray = @('a_1', 'b_2', 'c_3')
$objectArray = $stringArray | ForEach-Object {
[pscustomobject]@{
'name' = ($_ -split "_")[0]
# You can't reference the name property above in this property because it has not been created yet.
'extendedName' = $($name = ($_ -split "_")[0]; someLookUpFunction $name)
}
}
However, that should not be necessary in your example. You can define a variable before the custom object creation and then reference it within the object creation code:
$stringArray = @('a_1', 'b_2', 'c_3')
$objectArray = $stringArray | ForEach-Object {
$name = ($_ -split '_')[0]
[pscustomobject]@{
'name' = $name
'extendedName' = someLookUpFunction $name
}
}
You could also pass expressions to parameters directly provided it can be tokenized correctly:
$stringArray = @('a_1', 'b_2', 'c_3')
$objectArray = $stringArray | ForEach-Object {
[pscustomobject]@{
'name' = ($_ -split '_')[0]
'extendedName' = someLookUpFunction ($_ -split '_')[0]
}
}
Note: The proper way to call a function without using the pipeline is functionName -parametername parametervalue
or functionName parametervalue
if positional parameters are enabled. The syntax functionName(parametervalue)
could have unintended consequences. See this answer for a deeper dive into function/method calling syntax.
You cannot access the name
property of an object before that object has been created.
Upvotes: 3
Reputation: 7057
In addition to AdminOfThings Good Answer you can bypass the loop altogether using a select statement with the calculated property hash syntax:
$stringArray = @('a_1', 'b_2', 'c_3')
$objectArray = $stringArray |
Select-Object @{Name = 'Name'; Expression = { ($_ -Split '_')[0] } },
@{Name = 'ExtendedName'; Expression = { SomeLookupFunction ($_ -Split '_')[0] } }
For the efficiency of not executing -Split '_'
2x, if you do go with a loop just use a variable to and reference twice.
Altered version of AdminOfThings Example:
$stringArray = @('a_1', 'b_2', 'c_3')
$objectArray = $stringArray | ForEach-Object {
$TmpName = ($_ -split '_')[0]
[pscustomobject]@{
'name' = $TmpName
'extendedName' = someLookUpFunction $TmpName
}
}
It's also correct that you can't reference a property before it's been added to an object. One way around this is to just use 2 select statements:
$stringArray = @('a_1', 'b_2', 'c_3')
$objectArray = $stringArray |
Select-Object @{Name = 'Name'; Expression = { ($_ -Split '_')[0] } } |
Select-Object *, @{Name = 'ExtendedName'; Expression = { SomeLookupFunction ($_ -Split '_')[0] } }
This may have some readability advantage, but, I try to avoid it in favor of invoking as few commands as possible.
Upvotes: 4