functions/Merge-Hashtables.ps1
<#
.NOTES This function provides two merge modes: - normal (no data loss, arrays merge, but removes duplicates) - shallow (only array from primary hashtable is taken) .DESCRIPTION This function merges deeply two hashtables without data loss. In case of object mismatch, first hashtable will have priority. Value-objects like strings or integers will be taken from primary hashtable. Arrays will be merged without duplicates in normal mode. In shallow mode array from primary hashtable will be taken. .PARAMETER primary First hashtable to merge. This will have priority. It can be null or empty. .PARAMETER secondary Second hashtable to merge. It can be null or empty. .PARAMETER shallow When switched ON only shallow merge is performed. Arrays will not be merged, but array from primary hashtable will be taken. .EXAMPLE Merge-Hashtables -primary $primary -secondary $secondary Merge-Hashtables -primary $primary -secondary $secondary -shallow #> function Merge-Hashtables { [CmdletBinding()] [OutputType([hashtable])] Param ( [Parameter(Mandatory=$false)] [hashtable] $primary, [Parameter(Mandatory=$false)] [hashtable] $secondary, [Parameter(Mandatory=$false)] [switch] $shallow ) if($primary.Count -eq 0) { return $secondary } if($secondary.Count -eq 0) { return $primary } # hshtables and dictionaries can be merged $mergableTypes = @( "Hashtable" "Dictionary``2" ) # variable needs to exist to apply [ref] $primaryCopy, $secondaryCopy = $null Copy-Object -Original $primary -DeepClone ([ref]$primaryCopy) Copy-Object -Original $secondary -DeepClone ([ref]$secondaryCopy) $duplicateKeys = $primaryCopy.keys | Where-Object {$secondaryCopy.ContainsKey($_)} foreach ($key in $duplicateKeys) { if($null -ne $primaryCopy.$key -and $null -ne $secondaryCopy.$key) { # mergable types merge recursively if ($mergableTypes -contains $primaryCopy.$key.GetType().Name -and $mergableTypes -contains $secondaryCopy.$key.GetType().Name) { $primaryCopy.$key = Merge-Hashtables -primary $primaryCopy.$key -secondary $secondaryCopy.$key } # merge arrays (unless it is in shallow mode) if (-not $shallow -and $primaryCopy.$key.GetType().Name -eq "Object[]" -and $secondaryCopy.$key.GetType().Name -eq "Object[]") { $result = @() # because Object[] can contain many different types, Unique of Select may not work properly # hence iterate over each of the two arrays foreach ($item in ($primaryCopy.$key + $secondaryCopy.$key)) { # Switch on the type of the item to determine how to add the information switch ($item.GetType().Name) { # unique strings and integers {$_ -in "String","Int32"} { if ($result -notcontains $item) { $result += $item } } default { $result += $item } } } # assign the result back to the primary array $primaryCopy.$key = $result } } # force primary key, so remove secondary conflict $secondaryCopy.Remove($key) } # join the two hash tables and return to the calling function $primaryCopy + $secondaryCopy } |