private/PSCustomObject/Join-PSCustomObject.ps1
# TODO Put this into separate module and publish it # The function uses the "Left" one as the kind of master and extends it with "Right" # By default, properties in "Left" get overwritten, but with the flag "AddPropertiesFromRight" # it also adds properties from the right one # add the -verbose flag if you want to know more whats about to happen function Join-PSCustomObject { [CmdletBinding()] param ( [Parameter(Mandatory=$true,ValueFromPipeline)][PSCustomObject]$Left ,[Parameter(Mandatory=$true)][PSCustomObject]$Right ,[Parameter(Mandatory=$false)][Switch]$AddPropertiesFromRight = $false ,[Parameter(Mandatory=$false)][Switch]$MergePSCustomObjects = $false ,[Parameter(Mandatory=$false)][Switch]$MergeArrays = $false ,[Parameter(Mandatory=$false)][Switch]$MergeHashtables = $false ) begin { if ( $null -eq $Left ) { # return return $null } if ( $null -eq $Right ) { # return Write-Warning "-Right is null!" } } process { # Create an empty object $joined = [PSCustomObject]@{} # Go through the left object If ( $Left -is [PSCustomObject] ) { # Read all properties $leftProps = $Left.PsObject.Properties.name $rightProps = $Right.PsObject.Properties.name # Compare $compare = Compare-Object -ReferenceObject $leftProps -DifferenceObject $rightProps -IncludeEqual # Go through all properties $compare | where { $_.SideIndicator -eq "<=" } | ForEach-Object { $propLeft = $_.InputObject $joined | Add-Member -MemberType NoteProperty -Name $propLeft -Value $Left.($propLeft) Write-Verbose "Add '$( $propLeft )' from left side" } # Now check if we can add more properties If ( $AddPropertiesFromRight -eq $true ) { $compare | where { $_.SideIndicator -eq "=>" } | ForEach-Object { $propRight = $_.InputObject $joined | Add-Member -MemberType NoteProperty -Name $propRight -Value $Right.($propRight) Write-Verbose "Add '$( $propRight )' from right side" } } # Now overwrite existing values or check to go deeper if needed $compare | where { $_.SideIndicator -eq "==" } | ForEach-Object { $propEqual = $_.InputObject If ( $MergePSCustomObjects -eq $true -and $Left.($propEqual) -is [PSCustomObject] -and $Right.($propEqual) -is [PSCustomObject] -and @( $Right.($propEqual).psobject.properties ).Count -gt 0 ) { Write-Verbose "Going recursively into '$( $propEqual )'" # Recursively call this function, if it is nested ps custom $params = [Hashtable]@{ "Left" = $Left.($propEqual) "Right" = $Right.($propEqual) "AddPropertiesFromRight" = $AddPropertiesFromRight "MergePSCustomObjects" = $MergePSCustomObjects "MergeArrays" = $MergeArrays "MergeHashtables" = $MergeHashtables } $recursive = Join-PSCustomObject @params $joined | Add-Member -MemberType NoteProperty -Name $propEqual -Value $recursive } elseif ( $MergeArrays -eq $true -and $Left.($propEqual) -is [Array] -and $Right.($propEqual) -is [Array] ) { Write-Verbose "Merging arrays from '$( $propEqual )'" # Merge array $newArr = [Array]@( $Left.($propEqual) + $Right.($propEqual) ) | Sort-Object -unique $joined | Add-Member -MemberType NoteProperty -Name $propEqual -Value $newArr } elseif ( $MergeArrays -eq $true -and $Left.($propEqual) -is [System.Collections.ArrayList] -and $Right.($propEqual) -is [System.Collections.ArrayList] ) { Write-Verbose "Merging arraylists from '$( $propEqual )'" # Merge arraylist $newArr = [System.Collections.ArrayList]@() $newArr.AddRange($Left.($propEqual)) $newArr.AddRange($Right.($propEqual)) $newArrSorted = [System.Collections.ArrayList]@( $newArr | Sort-Object -Unique ) $joined | Add-Member -MemberType NoteProperty -Name $propEqual -Value $newArrSorted } elseif ( $MergeHashtables -eq $true -and $Left.($propEqual) -is [hashtable] -and $Right.($propEqual) -is [hashtable] -and @( $Right.($propEqual).Keys ).Count -gt 0) { # Recursively call this function, if it is nested hashtable $params = [Hashtable]@{ "Left" = $Left.($propEqual) "Right" = $Right.($propEqual) "AddKeysFromRight" = $AddPropertiesFromRight "MergePSCustomObjects" = $MergePSCustomObjects "MergeArrays" = $MergeArrays "MergeHashtables" = $MergeHashtables } $recursive = Join-Hashtable @params $joined | Add-Member -MemberType NoteProperty -Name $propEqual -Value $recursive } else { # just overwrite existing values if datatypes of attribute are different or no merging is wished $joined | Add-Member -MemberType NoteProperty -Name $propEqual -Value $Right.($propEqual) Write-Verbose "Overwrite '$( $propEqual )' with value from right side" #Write-Verbose "Datatypes of '$( $propEqual )' are not the same on left and right" } } } # return $joined } end { } } #----------------------------------------------- # TESTING PSCUSTOMOBJECTS #----------------------------------------------- <# $left = [PSCustomObject]@{ "firstname" = "Florian" "lastname" = "Friedrichs" "testnull" = $null "emptyArr" = $null "arr" = [Array]@("a","b","c") "nested" = [PSCustomObject]@{ "firstname" = "Flo" } "ht" = [hashtable]@{ "firstname" = "Florian" "lastname" = "Friedrichs" } } $right = [PSCustomObject]@{ "lastname" = "von Bracht" "Street" = "Schaumainkai 87" "Postcode" = 52080 "emptyArr" = [Array]@() "nested" = [PSCustomObject]@{ "lastname" = "von Bracht" } "arr" = [Array]@("d","e","f") "ht" = [hashtable]@{ "lastname" = "von Bracht" "Street" = "Schaumainkai 87" } } $res = Join-PSCustomObject -Left $left -Right $right -AddPropertiesFromRight -MergePSCustomObject -MergeArrays -verbose -MergeHashtables ConvertTo-Json -InputObject $res #> |