Public/Merge-PSCustomObject.ps1
function Merge-PSCustomObject { <# .SYNOPSIS This function merges two hashtables into one. It uses the "Left" one as the kind of master and extends it with "Right" .DESCRIPTION Apteco PS Modules - PowerShell merge PSCustomObject The function uses the "Left" one as the kind of master and extends it with "Right" This runs recursively through the PSCustomObject and merges all values 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 .PARAMETER Left Master PSCustomObject that is the base and where values are getting replaced from right .PARAMETER Right Those values will be added into the left one .PARAMETER AddKeysFromRight Add properties from right to left, otherwise not matching keys from right will be ignored .PARAMETER MergePSCustomObjects PSCustomObjects with the same name would be overwritten by default. Use this flag to merge them .PARAMETER MergeArrays Array with the same name would be overwritten by default. Use this flag to merge them .PARAMETER MergeHashtables Hashtables with the same name would be overwritten by default. Use this flag to merge them .EXAMPLE $left = [PSCustomObject]@{ "firstname" = "Florian" "lastname" = "Friedrichs" } $right = [PSCustomObject]@{ "lastname" = "von Bracht" "Street" = "Schaumainkai 87" } Merge-PSCustomObject -Left $left -right $right results to firstname lastname --------- -------- Florian von Bracht So it replaces all values on left with the ones from right .EXAMPLE $left = [PSCustomObject]@{ "firstname" = "Florian" "lastname" = "Friedrichs" } $right = [PSCustomObject]@{ "lastname" = "von Bracht" "Street" = "Schaumainkai 87" } Merge-PSCustomObject -Left $left -right $right -AddPropertiesFromRight results to firstname Street lastname --------- ------ -------- Florian Schaumainkai 87 von Bracht So it adds properties from right to left that are not existing in left .EXAMPLE $left = [PSCustomObject]@{ "firstname" = "Florian" "lastname" = "Friedrichs" "address" = [PSCustomObject]@{ "Street" = "Schaumainkai 87" } } $right = [PSCustomObject]@{ "lastname" = "von Bracht" "Street" = "Schaumainkai 87" "address" = [PSCustomObject]@{ "Postcode" = 60596 } } Merge-PSCustomObject -Left $left -right $right results to firstname lastname address --------- -------- ------- Florian von Bracht @{Postcode=60596} So it replaces the PSCustomObject from left with the one from right. Using the `-MergeHashtables` flag will merge the child PSCustomObject as well .EXAMPLE $left = [PSCustomObject]@{ "firstname" = "Florian" "lastname" = "Friedrichs" "address" = [PSCustomObject]@{ "Street" = "Schaumainkai 87" } } $right = [PSCustomObject]@{ "lastname" = "von Bracht" "Street" = "Schaumainkai 87" "address" = [PSCustomObject]@{ "Street" = "Kaiserstraße 35" "Postcode" = 60596 } } Merge-PSCustomObject -Left $left -right $right -MergePSCustomObjects So it replaces the also nested PSCustomObjects from left with the one from right. Using the `-AddPropertiesFromRight` flag will add properties from right to left, also in nested PSCustomObjects It results to firstname lastname address --------- -------- ------- Florian von Bracht @{Street=Kaiserstraße 35} .EXAMPLE $left = [PSCustomObject]@{ "firstname" = "Florian" "lastname" = "Friedrichs" "address" = [PSCustomObject]@{ "Street" = "Schaumainkai 87" } } $right = [PSCustomObject]@{ "lastname" = "von Bracht" "Street" = "Schaumainkai 87" "address" = [PSCustomObject]@{ "Street" = "Kaiserstraße 35" "Postcode" = 60596 } } Merge-PSCustomObject -Left $left -right $right -MergePSCustomObjects -AddPropertiesFromRight will result to firstname Street lastname address --------- ------ -------- ------- Florian Schaumainkai 87 von Bracht @{Postcode=60596; Street=Kaiserstraße 35} .INPUTS PSCustomObject .OUTPUTS PSCustomObject .NOTES Author: florian.von.bracht@apteco.de #> [CmdletBinding()] [OutputType([PSCustomObject])] 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-Object { $_.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-Object { $_.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-Object { $_.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 = Merge-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) { Write-Verbose "Merging hashtables from '$( $propEqual )'" # 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 = Merge-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 { } } |