Search code examples
powershellpsobject

Powershell PSObject calculated property based on another property


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?


Solution

  • 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.