Passing 2 PSCustomObjects to a function

I’m fairly new to Powershell scripting and am stuck with a strange problem. This is just an example, data would normally have been read from a .json file. ‘JoinInherit’ should mix default data and user data, but I’m not able to pass two objects into the function.:

function JoinInherit {
      param (
        [Parameter()]
        [PSCustomObject]
        $ObjDef),
        [Parameter()]
        [PSCustomObject]
        $ObjUs
      )
      Write-Host "Parameter 1: $($ObjDef.length)"
      $ObjDef | Format-List
      Write-Host 'Parameter 2'
      $ObjUs | Format-List
     
      $res = $ObjDef
      # do something to join/inherit both objects
     
      return $res
    }
    
    $myObjectDefaults = [PSCustomObject]@{
      FirstName = ""
      Language  = "Englisch"
      Country   = "USA"
    }    
    
    $myObjectUserA = [PSCustomObject]@{
      FirstName  = 'Michael'
      Language   = ''
      Country    = ''
    }    
    
    $myObjectUserB = [PSCustomObject]@{
      FirstName = "Thomas"
      Language  =  ""
      State     = "Great Britain"
    }    
   
    $myObjectDefaults | Format-List
    $myObjectUserA | Format-List
    $myObjectUserB | Format-List
    
    $data = JoinInherit($myObjectDefaults, $myObjectUserA)
    
    $data | Format-List`

When running the code the output looks like this:

FirstName : 
Language  : Englisch
Country   : USA

FirstName : Michael
Language  : 
Country   : 

FirstName : Thomas
Language  : 
State     : Great Britain

Parameter 1: 2
Parameter 2

FirstName : 
Language  : Englisch
Country   : USA

FirstName : Michael
Language  : 
Country   : 

FirstName : 
Language  : Englisch
Country   : USA

FirstName : Michael
Language  : 
Country   : 

I’m using Powershell 7.4.0 Core (fresh installation) without any other modules installed.
As one can see the 3 objects get defined and dumped as expected. Inside the JoinInherit() function they seem to be empty!?
Using a breakpoint and Watches inside the function reveals, that $ObjUs seems to be $null and $ObjDef has been changed to some sort of array(?) of type ???:

[0]: @{FirstName=; Language=Englisch; Country=USA}
[1]: @{FirstName=Michael; Language=; Country=}

So the originally passed in PSCustomObjects are converted into this strange sort of array!??
What is happening here? How can I pass the two objects unchanged?

Whookie1,
Servus & Welcome to the forum. :wave:t3:

Don’t you get error messages running your function definition?

Maybe it got messed up while copying and pasting here, but your param block is wrong. This should be enough:

    param (
      [PSCustomObject]
      $ObjDef,
      [PSCustomObject]
      $ObjUs
    )

That’s not how we call fuinctions in PowerShell. It should look like this:

 $data = JoinInherit -ObjDef $myObjectDefaults -ObjUs $myObjectUserA

Since you’re actually not doing anything in your function with the provided parameters - what output would you expect?

And BTW: It is best practice to name functions just like cmdlets … Verb-Noun.
You can read more about here:

Thanks for your answer!

No there is no syntax error, param() - syntax is correct (you could extend [Parameter(Mandatory=$frue)] and much more.

Oh my, that was really a stupid mistake, using parentheses when calling the function :see_no_evil:

Now I can see the two objects get passed to the function (at least in the Watch windows) but other than that the example does not work.

I can not output the object (using format-list), I can not access any of the properties of the objects in code nor can I change them.

At the moment I just want to return one of the two objects ($ObjDef in the example). The returned value is again joint data. The output:

FirstName : Michael
Language  : 
Country   : 

FirstName : 
Language  : Englisch
Country   : USA

Yes, there is. But I was not talking about the [Parameter()] attribute. I meant the closing parenthesis you have before the comma in your param block!! :point_up:t3:

It’s a common mistake for coders comming from another language. :man_shrugging:t3:

You can force PowerShell to output it to the console with Out-Host

You actually don’t have code to do something like this yet. At least you did not post those code. :man_shrugging:t3:

I don’t know what you mean. Since you actually just assign the input object from the parameter -ObjDef to the variable $res and output that the function does exactly that.

If I run your code the last output is …

PS C:\_Sample> $data | Format-List

FirstName : 
Language  : Englisch
Country   : USA

… and that’s exactly what I would expect from that code.

What would you expect actually? Maybe you should explain what you actually want to do. The bigger picture if you like. There might be a better way. :wink::love_you_gesture:t3:

1 Like

Sorry, that extra paranthesis was only in the formatted code here in the posting.

Okay the example seem to work now but the order of the output seems to be wrong somehow:

Parameter 1: 1
  : >Englisch<
Parameter 2
Show result:

FirstName : Michael
Language  : 
Country   : 

FirstName : 
Language  : Englisch
Country   : USA

The first block is print inside the function but between “Parameter” and “Show result:” I’m using $ObjsUs | Format-List and I expected that the data gets dumped there, but es it seems it is output belos the “Show result:” and than $data is shown. If I disable the $ObjsUs | Format-List inside the function the output is as expected now.

if input is $myObjectDefaults and $myObjectUserA output should be:

FirstName  = 'Michael'
Language  = "Englisch"
Country   = "USA"

for $myObjectDefaults and $myObjectUserB output should be:

FirstName = "Thomas"
Language  =  "Englisch"
State     = "Great Britain"

I suspected that in my first reply!! :smirk: :man_shrugging:t3:

PowerShell tries to optimize the output for implicitly by default. If you don’t want that you can force it to output whatever you need right away with Out-Host.
Like this:

function JoinInherit {
    param (
        [PSCustomObject]
        $ObjDef,
        [PSCustomObject]
        $ObjUs
    )
    Write-Host "Parameter 1: $($ObjDef.length)"
    $ObjDef | Format-List | Out-Host
    Write-Host 'Parameter 2'
    $ObjUs | Format-List | Out-Host
   
    $res = $ObjDef
    return $res
}

Why? What code line or code block should do this kind of merge operation? :thinking:

I think you’re looking for something like this:

function Join-Inherit {
    param (
        [PSCustomObject]
        $ObjDef,
        [PSCustomObject]
        $ObjUs
    )
    [PSCustomObject]@{
        FirstName = if ($ObjUs.FirstName) { $ObjUs.FirstName } else { $ObjDef.FirstName }
        Language  = if ($ObjUs.Language) { $ObjUs.Language } else { $ObjDef.Language }
        State     = if ($ObjUs.State) { $ObjUs.State } else { $ObjDef.State }
    }
}
  
$myObjectDefaults = 
[PSCustomObject]@{
    FirstName = ""
    Language  = "Englisch"
    State     = "USA"
}    
  
$myObjectUserA = 
[PSCustomObject]@{
    FirstName = 'Michael'
    Language  = ''
    State     = ''
}    
  
Join-Inherit -ObjDef $myObjectDefaults -ObjUs $myObjectUserA

And the output looks like this:

PS C:\_Sample> Join-Inherit -ObjDef $myObjectDefaults -ObjUs $myObjectUserA

FirstName Language State
--------- -------- -----
Michael   Englisch USA

EDIT:
A little more modern way would be to use the ternary operator instead of if-else and it looks like this:

function Join-Inherit {
    param (
        [PSCustomObject]
        $ObjDef,
        [PSCustomObject]
        $ObjUs
    )
    [PSCustomObject]@{
        FirstName = $ObjUs.FirstName ? $ObjUs.FirstName : $ObjDef.FirstName
        Language  = $ObjUs.Language ? $ObjUs.Language : $ObjDef.Language
        State     = $ObjUs.State ? $ObjUs.State : $ObjDef.State
    }
}

Many thanks for your help, this is the final version of the function. Now it’s also possible to ignore a default properties content (by assigning ‘ˣ’):

function Get-JoinedUser {
  param (
      [Parameter(Mandatory=$true)]
      [PSCustomObject]
      $ObjDef,

      [Parameter(Mandatory=$true)]
      [PSCustomObject]
      $ObjUs
  )
  $res = $ObjDef.psobject.copy()
  $resMember = $res.psobject.Members | where-object MemberType -EQ 'NoteProperty'
  foreach ($member in $resMember) {
    if (-not [string]::IsNullOrEmpty($ObjUs.($member.Name)) ) {
        $member.Value = $ObjUs.($member.Name) -ne 'ˣ' ? $ObjUs.($member.Name) : ''
    }
  }
  return $res
}